feat: recreate AgentServiceMarketplace.sol based on docs

This commit is contained in:
oib
2026-02-27 22:56:13 +01:00
parent 501676ff93
commit 8ca8fa041d

View File

@@ -0,0 +1,231 @@
// 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 AgentServiceMarketplace
* @dev Advanced marketplace for AI agents to discover, offer, and monetize their capabilities and services.
*/
contract AgentServiceMarketplace is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
IERC20 public aitbcToken;
uint256 public serviceCounter;
uint256 public subscriptionCounter;
uint256 public platformFeePercentage = 250; // 2.5% in basis points (10000 = 100%)
struct ServiceOffering {
uint256 serviceId;
address providerAgent;
string capabilityURI; // IPFS hash containing service metadata (inputs, outputs, SLA)
uint256 pricePerUse;
uint256 subscriptionPricePerMonth;
bool isSubscriptionAvailable;
bool isActive;
uint256 totalUses;
uint256 totalRevenue;
uint256 reputationScore; // Updated via cross-chain reputation or oracle
}
struct Subscription {
uint256 subscriptionId;
uint256 serviceId;
address subscriberAgent;
uint256 expiryTimestamp;
bool isActive;
}
mapping(uint256 => ServiceOffering) public services;
mapping(uint256 => Subscription) public subscriptions;
mapping(address => uint256[]) public providerServices;
mapping(address => uint256[]) public subscriberSubscriptions;
// Events
event ServiceRegistered(uint256 indexed serviceId, address indexed provider, string capabilityURI, uint256 pricePerUse);
event ServiceUpdated(uint256 indexed serviceId, uint256 pricePerUse, bool isActive);
event ServicePurchased(uint256 indexed serviceId, address indexed buyer, uint256 pricePaid);
event SubscriptionCreated(uint256 indexed subscriptionId, uint256 indexed serviceId, address indexed subscriber, uint256 expiryTimestamp);
event SubscriptionRenewed(uint256 indexed subscriptionId, uint256 newExpiryTimestamp);
event PlatformFeeUpdated(uint256 newFeePercentage);
event ServiceReputationUpdated(uint256 indexed serviceId, uint256 newScore);
modifier serviceExists(uint256 _serviceId) {
require(_serviceId < serviceCounter, "Service does not exist");
_;
}
constructor(address _aitbcToken) {
require(_aitbcToken != address(0), "Invalid token address");
aitbcToken = IERC20(_aitbcToken);
}
/**
* @dev Register a new agent capability/service
* @param _capabilityURI IPFS hash for service metadata
* @param _pricePerUse Price to pay per individual API/capability call
* @param _subscriptionPricePerMonth Price for unlimited/tiered monthly access
* @param _isSubscriptionAvailable Boolean flag enabling subscriptions
*/
function registerService(
string calldata _capabilityURI,
uint256 _pricePerUse,
uint256 _subscriptionPricePerMonth,
bool _isSubscriptionAvailable
) external whenNotPaused returns (uint256) {
require(bytes(_capabilityURI).length > 0, "Invalid URI");
uint256 serviceId = serviceCounter++;
services[serviceId] = ServiceOffering({
serviceId: serviceId,
providerAgent: msg.sender,
capabilityURI: _capabilityURI,
pricePerUse: _pricePerUse,
subscriptionPricePerMonth: _subscriptionPricePerMonth,
isSubscriptionAvailable: _isSubscriptionAvailable,
isActive: true,
totalUses: 0,
totalRevenue: 0,
reputationScore: 0
});
providerServices[msg.sender].push(serviceId);
emit ServiceRegistered(serviceId, msg.sender, _capabilityURI, _pricePerUse);
return serviceId;
}
/**
* @dev Update an existing service offering
*/
function updateService(
uint256 _serviceId,
uint256 _pricePerUse,
uint256 _subscriptionPricePerMonth,
bool _isSubscriptionAvailable,
bool _isActive
) external serviceExists(_serviceId) {
ServiceOffering storage service = services[_serviceId];
require(service.providerAgent == msg.sender, "Not service provider");
service.pricePerUse = _pricePerUse;
service.subscriptionPricePerMonth = _subscriptionPricePerMonth;
service.isSubscriptionAvailable = _isSubscriptionAvailable;
service.isActive = _isActive;
emit ServiceUpdated(_serviceId, _pricePerUse, _isActive);
}
/**
* @dev Purchase a single use of an agent service
*/
function purchaseService(uint256 _serviceId) external nonReentrant whenNotPaused serviceExists(_serviceId) {
ServiceOffering storage service = services[_serviceId];
require(service.isActive, "Service inactive");
require(service.pricePerUse > 0, "Not available for single use");
uint256 platformFee = (service.pricePerUse * platformFeePercentage) / 10000;
uint256 providerAmount = service.pricePerUse - platformFee;
// Transfer funds
aitbcToken.safeTransferFrom(msg.sender, address(this), service.pricePerUse);
// Pay provider
if (providerAmount > 0) {
aitbcToken.safeTransfer(service.providerAgent, providerAmount);
}
// Retain platform fee in contract (owner can withdraw)
service.totalUses += 1;
service.totalRevenue += providerAmount;
emit ServicePurchased(_serviceId, msg.sender, service.pricePerUse);
}
/**
* @dev Subscribe to an agent service for 30 days
*/
function subscribeToService(uint256 _serviceId) external nonReentrant whenNotPaused serviceExists(_serviceId) returns (uint256) {
ServiceOffering storage service = services[_serviceId];
require(service.isActive, "Service inactive");
require(service.isSubscriptionAvailable, "Subscriptions not enabled");
uint256 platformFee = (service.subscriptionPricePerMonth * platformFeePercentage) / 10000;
uint256 providerAmount = service.subscriptionPricePerMonth - platformFee;
// Transfer funds
aitbcToken.safeTransferFrom(msg.sender, address(this), service.subscriptionPricePerMonth);
// Pay provider
if (providerAmount > 0) {
aitbcToken.safeTransfer(service.providerAgent, providerAmount);
}
service.totalRevenue += providerAmount;
uint256 subId = subscriptionCounter++;
uint256 expiry = block.timestamp + 30 days;
subscriptions[subId] = Subscription({
subscriptionId: subId,
serviceId: _serviceId,
subscriberAgent: msg.sender,
expiryTimestamp: expiry,
isActive: true
});
subscriberSubscriptions[msg.sender].push(subId);
emit SubscriptionCreated(subId, _serviceId, 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 service (Oracle/Owner only)
*/
function updateServiceReputation(uint256 _serviceId, uint256 _newScore) external onlyOwner serviceExists(_serviceId) {
services[_serviceId].reputationScore = _newScore;
emit ServiceReputationUpdated(_serviceId, _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();
}
}