LicenseNFTV2¶
The LicenseNFTV2 contract is an ERC-721 token that represents access rights to AI models on WasiAI. Each license grants specific capabilities (API access, download) for a defined duration (perpetual or subscription).
Contract Information¶
| Property | Value |
|---|---|
| Address (Fuji) | 0xC657F1B26fc56A0AA1481F502BCC6532B93d7426 |
| Token Name | WasiAI License V2 |
| Token Symbol | WASI-LIC2 |
| Solidity Version | ^0.8.24 |
| Inherits | ERC721, ERC721URIStorage, Ownable2Step |
Overview¶
License NFTs provide an alternative to pay-per-inference. Instead of paying for each API call, users can purchase a license that grants unlimited access for a period (or forever).
License Types: - Perpetual: One-time payment, lifetime access - Subscription: Monthly payment, renewable
License Rights: - API Access: Call the inference endpoint - Download Access: Download model artifacts
Constants¶
uint8 public constant KIND_PERPETUAL = 0; // One-time purchase
uint8 public constant KIND_SUBSCRIPTION = 1; // Monthly subscription
uint8 public constant RIGHTS_API = 1; // API access (bit 0)
uint8 public constant RIGHTS_DOWNLOAD = 2; // Download access (bit 1)
Data Structures¶
License¶
struct License {
uint256 modelId; // Model this license is for
address originalBuyer; // First purchaser (for analytics)
uint8 kind; // 0 = perpetual, 1 = subscription
uint8 rights; // Bitmask of granted rights
uint256 expiresAt; // Expiration timestamp (0 = never)
bool revoked; // Whether license has been revoked
}
State Variables¶
uint256 public nextTokenId = 1; // Next license ID to mint
address public marketplace; // Marketplace contract (minter)
mapping(uint256 => License) public licenses; // License data by token ID
Functions¶
Minting¶
mint¶
Mints a new license NFT. Only callable by the Marketplace contract.
function mint(
address to,
uint256 modelId,
uint8 kind,
uint8 rights,
uint256 duration
) external onlyMarketplace returns (uint256 tokenId)
Parameters: | Name | Type | Description | |------|------|-------------| | to | address | Recipient of the license | | modelId | uint256 | Model this license is for | | kind | uint8 | 0 = perpetual, 1 = subscription | | rights | uint8 | Rights bitmask | | duration | uint256 | Duration in seconds (0 for perpetual) |
Returns: tokenId (The minted license token ID)
Example (called by Marketplace):
uint256 licenseId = licenseNFT.mint(
buyer,
modelId,
KIND_PERPETUAL,
RIGHTS_API | RIGHTS_DOWNLOAD,
0 // No expiration
);
Validation¶
isValid¶
Checks if a license is currently valid (not expired, not revoked).
Returns: true if license is valid, false otherwise
Example:
const valid = await licenseNFT.isValid(licenseId)
if (!valid) {
console.log('License expired or revoked')
}
hasRights¶
Checks if a license grants specific rights.
Example:
// Check if license has API access
const hasApi = await licenseNFT.hasRights(licenseId, 1)
// Check if license has download access
const hasDownload = await licenseNFT.hasRights(licenseId, 2)
// Check if license has both
const hasBoth = await licenseNFT.hasRights(licenseId, 3)
getLicensesByOwner¶
Returns all license IDs owned by an address.
getLicensesForModel¶
Returns all license IDs for a specific model.
Renewal¶
renew¶
Extends a subscription license. Only callable by Marketplace.
Requirements: - License must be subscription type - License must not be revoked
Revocation¶
revoke¶
Revokes a license (e.g., for terms violation). Only callable by Marketplace.
Effects: - Sets revoked = true - License becomes invalid immediately - Token can still be transferred but grants no access
Metadata¶
tokenURI¶
Returns the metadata URI for a license.
Metadata Schema:
{
"name": "WasiAI License #123",
"description": "License for Crypto Sentiment Analyzer",
"image": "ipfs://...",
"attributes": [
{ "trait_type": "Model ID", "value": "1" },
{ "trait_type": "Type", "value": "Perpetual" },
{ "trait_type": "Rights", "value": "API + Download" },
{ "trait_type": "Expires", "value": "Never" }
]
}
Events¶
event LicenseMinted(
uint256 indexed tokenId,
uint256 indexed modelId,
address indexed buyer,
uint8 kind,
uint8 rights,
uint256 expiresAt
);
event LicenseRenewed(
uint256 indexed tokenId,
uint256 newExpiresAt
);
event LicenseRevoked(
uint256 indexed tokenId
);
Errors¶
error OnlyMarketplace();
error LicenseNotFound();
error LicenseExpired();
error LicenseRevoked();
error InvalidKind();
error InvalidRights();
Rights Bitmask¶
Rights are stored as a bitmask, allowing multiple rights per license:
| Value | Binary | Rights |
|---|---|---|
| 0 | 00 | None |
| 1 | 01 | API only |
| 2 | 10 | Download only |
| 3 | 11 | API + Download |
Checking Rights:
// Check if license has API access
bool hasApi = (license.rights & RIGHTS_API) != 0;
// Check if license has download access
bool hasDownload = (license.rights & RIGHTS_DOWNLOAD) != 0;
Integration Examples¶
Check User's License for Model¶
// Get all licenses owned by user
const licenses = await licenseNFT.getLicensesByOwner(userAddress)
// Find valid license for specific model
for (const licenseId of licenses) {
const license = await licenseNFT.licenses(licenseId)
if (license.modelId === targetModelId) {
const valid = await licenseNFT.isValid(licenseId)
if (valid) {
console.log('User has valid license:', licenseId)
break
}
}
}
Verify License Before API Call¶
async function verifyLicense(userAddress: string, modelId: number): Promise<boolean> {
const licenses = await licenseNFT.getLicensesByOwner(userAddress)
for (const licenseId of licenses) {
const license = await licenseNFT.licenses(licenseId)
if (
license.modelId === modelId &&
await licenseNFT.isValid(licenseId) &&
await licenseNFT.hasRights(licenseId, RIGHTS_API)
) {
return true
}
}
return false
}
Get License Details¶
const license = await publicClient.readContract({
address: LICENSE_NFT_ADDRESS,
abi: LicenseNFTV2ABI,
functionName: 'licenses',
args: [licenseId]
})
console.log({
modelId: license.modelId,
kind: license.kind === 0 ? 'Perpetual' : 'Subscription',
rights: license.rights,
expiresAt: license.expiresAt === 0n ? 'Never' : new Date(Number(license.expiresAt) * 1000),
revoked: license.revoked
})
Security Considerations¶
- Minting Restricted: Only Marketplace can mint licenses
- Revocation: Licenses can be revoked for terms violations
- Expiration Check: Always verify
isValid()before granting access - Transfer Safe: Licenses can be transferred, but access follows the token
- No Burn by User: Users cannot burn their licenses (prevents abuse)