MarketplaceV3¶
The MarketplaceV3 contract is the central hub of the WasiAI platform. It manages model listings, license sales, pricing, and coordinates with other contracts for agent registration and revenue splitting.
Contract Information¶
| Property | Value |
|---|---|
| Address (Fuji) | 0xf1eA59d71C67e9E6Ea481Aa26911641a6c97370C |
| Solidity Version | ^0.8.24 |
| License | MIT |
| Inherits | Ownable2Step, ReentrancyGuard, Pausable |
Constants¶
uint256 public constant MAX_BPS = 10_000; // 100% in basis points
uint256 public constant MAX_FEE_BPS_CAP = 2_000; // 20% max marketplace fee
uint256 public constant MAX_PRICE_CAP = 1_000_000 * 1e6; // $1M max price
uint256 public constant MAX_INFERENCE_PRICE = 1000 * 1e6; // $1000 max inference
uint256 public constant MIN_INFERENCE_PRICE = 100; // $0.0001 min inference
uint256 public constant TIMELOCK_DELAY = 24 hours; // Critical change delay
// License rights
uint8 public constant RIGHTS_API = 1; // API access
uint8 public constant RIGHTS_DOWNLOAD = 2; // Download access
// License types
uint8 public constant KIND_PERPETUAL = 0; // One-time purchase
uint8 public constant KIND_SUBSCRIPTION = 1; // Monthly subscription
Data Structures¶
Model¶
struct Model {
address owner; // Current owner
address creator; // Original creator (for royalties)
string name; // Display name
string uri; // Metadata URI (IPFS)
uint256 royaltyBps; // Creator royalty (0-10000 bps)
bool listed; // Active listing flag
uint256 pricePerpetual; // Perpetual license price (USDC)
uint256 priceSubscription; // Monthly subscription price (USDC)
uint256 defaultDurationDays;// Days per subscription period
uint8 deliveryRightsDefault;// Default rights bitmask
uint8 deliveryModeHint; // UX hint for delivery
uint16 version; // Model version
bytes32 termsHash; // Hash of legal terms
uint256 priceInference; // Price per inference (USDC)
address inferenceWallet; // Wallet for x402 payments
}
FamilyMeta¶
struct FamilyMeta {
uint256 latestId; // Latest model ID in family
uint16 latestVersion; // Latest version number
uint256 agentId; // Shared agent ID (ERC-8004)
uint256 firstModelId; // First model ID (for splitter)
}
State Variables¶
uint256 public nextId = 1; // Next model ID
uint256 public lastLicenseId; // Last minted license
uint256 public feeBps; // Marketplace fee (bps)
address public feeRecipient; // Fee recipient address
uint256 public activeModels; // Count of active models
uint256 public modelsLimit; // Max models (0 = unlimited)
mapping(uint256 => Model) public models;
mapping(address => mapping(bytes32 => FamilyMeta)) public families;
mapping(uint256 => bool) public revoked;
mapping(uint256 => PendingWalletChange) public pendingInferenceWalletChanges;
LicenseNFT public immutable licenseNFT;
ISplitterFactory public splitterFactory;
IAgentRegistry public agentRegistry;
IERC20 public paymentToken; // USDC
Functions¶
Publishing Models¶
publishModel¶
Registers a new model on the marketplace.
function publishModel(
string calldata name,
string calldata uri,
uint256 royaltyBps,
uint256 pricePerpetual,
uint256 priceSubscription,
uint256 defaultDurationDays,
uint8 deliveryRightsDefault,
uint8 deliveryModeHint,
bytes32 termsHash,
bytes32 slugHash,
uint256 priceInference,
address inferenceWallet,
string calldata agentEndpoint,
string calldata agentMetadataUri
) external whenNotPaused returns (uint256 modelId)
Parameters: | Name | Type | Description | |------|------|-------------| | name | string | Model display name | | uri | string | IPFS metadata URI | | royaltyBps | uint256 | Creator royalty (0-2000 bps) | | pricePerpetual | uint256 | Perpetual license price | | priceSubscription | uint256 | Monthly subscription price | | defaultDurationDays | uint256 | Subscription period length | | deliveryRightsDefault | uint8 | Default rights bitmask | | deliveryModeHint | uint8 | UX delivery hint | | termsHash | bytes32 | Hash of legal terms | | slugHash | bytes32 | Hash of URL slug (for versioning) | | priceInference | uint256 | Pay-per-inference price | | inferenceWallet | address | x402 payment recipient | | agentEndpoint | string | x402 inference endpoint | | agentMetadataUri | string | ERC-8004 metadata URI |
Returns: modelId (The newly assigned model ID)
Events Emitted: - ModelPublished(modelId, owner, name) - AgentRegistered(agentId, owner, modelId) (via AgentRegistry) - SplitterCreated(modelId, splitter, seller) (via SplitterFactory)
Example:
const tx = await marketplace.publishModel(
"Crypto Sentiment Analyzer",
"ipfs://QmXxx...",
1000, // 10% royalty
50_000_000, // $50 perpetual
10_000_000, // $10/month subscription
30, // 30 days
1, // API access
0, // delivery hint
termsHash,
slugHash,
10_000, // $0.01 per inference
walletAddress,
"https://wasiai.io/api/inference/1",
"ipfs://QmAgent..."
)
Buying Licenses¶
buyLicense¶
Purchases a license NFT for a model.
function buyLicense(
uint256 modelId,
uint8 kind,
uint8 rights
) external nonReentrant whenNotPaused returns (uint256 licenseId)
Parameters: | Name | Type | Description | |------|------|-------------| | modelId | uint256 | Model to license | | kind | uint8 | 0 = perpetual, 1 = subscription | | rights | uint8 | Rights bitmask (1 = API, 2 = download) |
Returns: licenseId (The minted license token ID)
Requirements: - Model must be listed - Caller must have approved USDC spend - Price must be configured for the license type
Payment Flow: 1. USDC transferred from buyer to model's splitter 2. Splitter distributes to seller, creator, marketplace 3. License NFT minted to buyer
Example:
// Approve USDC first
await usdc.approve(marketplaceAddress, price)
// Buy perpetual license with API access
const licenseId = await marketplace.buyLicense(
modelId,
0, // KIND_PERPETUAL
1 // RIGHTS_API
)
Pricing Configuration¶
setInferencePrice¶
Sets the pay-per-inference price for a model.
Parameters: | Name | Type | Description | |------|------|-------------| | modelId | uint256 | Model ID | | price | uint256 | Price in USDC base units (6 decimals) |
Requirements: - Caller must be model owner - Price must be within MIN/MAX bounds
Example:
setLicensePrices¶
Updates license prices for a model.
function setLicensePrices(
uint256 modelId,
uint256 pricePerpetual,
uint256 priceSubscription
) external
Wallet Management¶
requestInferenceWalletChange¶
Initiates a wallet change with 24-hour timelock.
executeInferenceWalletChange¶
Completes a wallet change after timelock expires.
Model Management¶
updateModel¶
Updates model metadata.
function updateModel(
uint256 modelId,
string calldata name,
string calldata uri,
uint256 royaltyBps,
bytes32 termsHash
) external
delistModel¶
Removes a model from active listings.
relistModel¶
Re-activates a delisted model.
Events¶
event ModelPublished(
uint256 indexed modelId,
address indexed owner,
string name
);
event ModelUpdated(uint256 indexed modelId);
event ModelDelisted(uint256 indexed modelId);
event ModelRelisted(uint256 indexed modelId);
event LicensePurchased(
uint256 indexed modelId,
uint256 indexed licenseId,
address indexed buyer,
uint8 kind,
uint256 price
);
event InferencePriceSet(
uint256 indexed modelId,
uint256 price
);
event InferenceWalletChangeRequested(
uint256 indexed modelId,
address oldWallet,
address newWallet,
uint256 effectiveAt
);
event InferenceWalletChanged(
uint256 indexed modelId,
address oldWallet,
address newWallet
);
Errors¶
error NotOwner();
error NotListed();
error InvalidBps();
error FeeOverCap();
error FeePlusRoyaltyOver100();
error InvalidRights();
error InvalidKind();
error InvalidDuration();
error InsufficientFunds();
error ModelsLimitReached();
error PriceNotConfigured();
error PriceTooHigh();
error TransferFailed();
error ZeroAddress();
error InvalidInferencePrice();
error TimelockNotExpired();
error NoPendingChange();
Integration Examples¶
Get Model Information¶
const model = await publicClient.readContract({
address: MARKETPLACE_ADDRESS,
abi: MarketplaceV3ABI,
functionName: 'models',
args: [modelId]
})
console.log({
owner: model.owner,
name: model.name,
priceInference: model.priceInference,
inferenceWallet: model.inferenceWallet
})
Check License Ownership¶
const hasLicense = await publicClient.readContract({
address: LICENSE_NFT_ADDRESS,
abi: LicenseNFTV2ABI,
functionName: 'balanceOf',
args: [userAddress]
})
Get Splitter for Model¶
const splitter = await publicClient.readContract({
address: SPLITTER_FACTORY_ADDRESS,
abi: SplitterFactoryABI,
functionName: 'getSplitter',
args: [modelId]
})
Security Considerations¶
- Reentrancy: All payment functions use
nonReentrant - Access Control: Owner checks on all model modifications
- Timelocks: 24-hour delay for wallet changes
- Pausability: Contract can be paused in emergencies
- Price Bounds: Min/max limits prevent extreme pricing