// 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(); } }