From 285c0dab1c4cae66ca4745ca985c49325449a92e Mon Sep 17 00:00:00 2001 From: oib Date: Fri, 27 Feb 2026 22:57:14 +0100 Subject: [PATCH] feat: recreate AgentMarketplaceV2.sol based on docs --- contracts/contracts/AgentMarketplaceV2.sol | 224 +++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 contracts/contracts/AgentMarketplaceV2.sol diff --git a/contracts/contracts/AgentMarketplaceV2.sol b/contracts/contracts/AgentMarketplaceV2.sol new file mode 100644 index 00000000..0ec2758c --- /dev/null +++ b/contracts/contracts/AgentMarketplaceV2.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/security/Pausable.sol"; + +/** + * @title AgentMarketplaceV2 + * @dev Advanced marketplace with capability trading and subscriptions for AI agents. + */ +contract AgentMarketplaceV2 is Ownable, ReentrancyGuard, Pausable { + using SafeERC20 for IERC20; + + IERC20 public aitbcToken; + + uint256 public capabilityCounter; + uint256 public subscriptionCounter; + uint256 public platformFeePercentage = 250; // 2.5% in basis points (10000 = 100%) + + struct Capability { + uint256 capabilityId; + address providerAgent; + string metadataURI; // IPFS hash containing capability description, API spec, and SLA + uint256 pricePerCall; // Price for a single capability use + uint256 subscriptionPrice; // Price for a 30-day subscription + bool isSubscriptionEnabled; + bool isActive; + uint256 totalCalls; + uint256 totalRevenue; + uint256 reputationScore; // Automatically updated score based on verifications + } + + struct Subscription { + uint256 subscriptionId; + uint256 capabilityId; + address subscriberAgent; + uint256 expiryTimestamp; + bool isActive; + } + + mapping(uint256 => Capability) public capabilities; + mapping(uint256 => Subscription) public subscriptions; + mapping(address => uint256[]) public providerCapabilities; + mapping(address => uint256[]) public subscriberSubscriptions; + + // Events + event CapabilityListed(uint256 indexed capabilityId, address indexed provider, string metadataURI, uint256 pricePerCall, uint256 subscriptionPrice); + event CapabilityUpdated(uint256 indexed capabilityId, uint256 pricePerCall, uint256 subscriptionPrice, bool isActive); + event CapabilityPurchased(uint256 indexed capabilityId, address indexed buyer, uint256 pricePaid); + event SubscriptionCreated(uint256 indexed subscriptionId, uint256 indexed capabilityId, address indexed subscriber, uint256 expiryTimestamp); + event PlatformFeeUpdated(uint256 newFeePercentage); + event CapabilityReputationUpdated(uint256 indexed capabilityId, uint256 newScore); + + modifier capabilityExists(uint256 _capabilityId) { + require(_capabilityId < capabilityCounter, "Capability does not exist"); + _; + } + + constructor(address _aitbcToken) { + require(_aitbcToken != address(0), "Invalid token address"); + aitbcToken = IERC20(_aitbcToken); + } + + /** + * @dev List a new agent capability on the marketplace + */ + function listCapability( + string calldata _metadataURI, + uint256 _pricePerCall, + uint256 _subscriptionPrice, + bool _isSubscriptionEnabled + ) external whenNotPaused returns (uint256) { + require(bytes(_metadataURI).length > 0, "Invalid URI"); + + uint256 capabilityId = capabilityCounter++; + + capabilities[capabilityId] = Capability({ + capabilityId: capabilityId, + providerAgent: msg.sender, + metadataURI: _metadataURI, + pricePerCall: _pricePerCall, + subscriptionPrice: _subscriptionPrice, + isSubscriptionEnabled: _isSubscriptionEnabled, + isActive: true, + totalCalls: 0, + totalRevenue: 0, + reputationScore: 0 + }); + + providerCapabilities[msg.sender].push(capabilityId); + + emit CapabilityListed(capabilityId, msg.sender, _metadataURI, _pricePerCall, _subscriptionPrice); + return capabilityId; + } + + /** + * @dev Update an existing capability + */ + function updateCapability( + uint256 _capabilityId, + uint256 _pricePerCall, + uint256 _subscriptionPrice, + bool _isSubscriptionEnabled, + bool _isActive + ) external capabilityExists(_capabilityId) { + Capability storage cap = capabilities[_capabilityId]; + require(cap.providerAgent == msg.sender, "Not the provider"); + + cap.pricePerCall = _pricePerCall; + cap.subscriptionPrice = _subscriptionPrice; + cap.isSubscriptionEnabled = _isSubscriptionEnabled; + cap.isActive = _isActive; + + emit CapabilityUpdated(_capabilityId, _pricePerCall, _subscriptionPrice, _isActive); + } + + /** + * @dev Purchase a single call of a capability + */ + function purchaseCall(uint256 _capabilityId) external nonReentrant whenNotPaused capabilityExists(_capabilityId) { + Capability storage cap = capabilities[_capabilityId]; + require(cap.isActive, "Capability inactive"); + require(cap.pricePerCall > 0, "Not available for single call"); + + uint256 platformFee = (cap.pricePerCall * platformFeePercentage) / 10000; + uint256 providerAmount = cap.pricePerCall - platformFee; + + // Transfer funds + aitbcToken.safeTransferFrom(msg.sender, address(this), cap.pricePerCall); + + // Pay provider + if (providerAmount > 0) { + aitbcToken.safeTransfer(cap.providerAgent, providerAmount); + } + + cap.totalCalls += 1; + cap.totalRevenue += providerAmount; + + emit CapabilityPurchased(_capabilityId, msg.sender, cap.pricePerCall); + } + + /** + * @dev Subscribe to an agent capability for 30 days + */ + function subscribeToCapability(uint256 _capabilityId) external nonReentrant whenNotPaused capabilityExists(_capabilityId) returns (uint256) { + Capability storage cap = capabilities[_capabilityId]; + require(cap.isActive, "Capability inactive"); + require(cap.isSubscriptionEnabled, "Subscriptions not enabled"); + + uint256 platformFee = (cap.subscriptionPrice * platformFeePercentage) / 10000; + uint256 providerAmount = cap.subscriptionPrice - platformFee; + + // Transfer funds + aitbcToken.safeTransferFrom(msg.sender, address(this), cap.subscriptionPrice); + + // Pay provider + if (providerAmount > 0) { + aitbcToken.safeTransfer(cap.providerAgent, providerAmount); + } + + cap.totalRevenue += providerAmount; + + uint256 subId = subscriptionCounter++; + uint256 expiry = block.timestamp + 30 days; + + subscriptions[subId] = Subscription({ + subscriptionId: subId, + capabilityId: _capabilityId, + subscriberAgent: msg.sender, + expiryTimestamp: expiry, + isActive: true + }); + + subscriberSubscriptions[msg.sender].push(subId); + + emit SubscriptionCreated(subId, _capabilityId, msg.sender, expiry); + return subId; + } + + /** + * @dev Check if a subscription is still active and valid + */ + function checkSubscription(uint256 _subscriptionId) external view returns (bool) { + Subscription memory sub = subscriptions[_subscriptionId]; + return sub.isActive && (block.timestamp < sub.expiryTimestamp); + } + + /** + * @dev Update the reputation score of a capability (Oracle/Owner only) + */ + function updateCapabilityReputation(uint256 _capabilityId, uint256 _newScore) external onlyOwner capabilityExists(_capabilityId) { + capabilities[_capabilityId].reputationScore = _newScore; + emit CapabilityReputationUpdated(_capabilityId, _newScore); + } + + /** + * @dev Update platform fee percentage + */ + function updatePlatformFee(uint256 _newFee) external onlyOwner { + require(_newFee <= 1000, "Fee too high"); // Max 10% + platformFeePercentage = _newFee; + emit PlatformFeeUpdated(_newFee); + } + + /** + * @dev Withdraw accumulated platform fees + */ + function withdrawPlatformFees() external onlyOwner { + uint256 balance = aitbcToken.balanceOf(address(this)); + require(balance > 0, "No fees to withdraw"); + aitbcToken.safeTransfer(owner(), balance); + } + + function pause() external onlyOwner { + _pause(); + } + + function unpause() external onlyOwner { + _unpause(); + } +}