ERC-8041 - Fixed-Supply Agent NFT Collections

Created 2025-10-11
Status Draft
Category ERC
Type Standards Track
Authors
Requires

Abstract

An interface for creating fixed-supply collections of Agent NFTs that are registered in an ERC-8004 Agent Registry. While ERC-8004 provides an unlimited mint registry for agent identities, many use cases require limited collections. Collections can be created with fixed supply limits while maintaining the association between agents and their collection through onchain metadata and mint number tracking.

Motivation

ERC-8004 establishes a singleton registry for AI Agent identity tokens with unlimited minting capability. This open registration model serves many use cases, but there is a clear need for:

  1. Fixed-supply collections: Ability to create limited editions (e.g., "Genesis 100", "Season 1")
  2. Collection metadata: Associate agents with specific collections and track their provenance
  3. Mint number tracking: Permanent record of each agent's position in the collection (#1 of 1000)
  4. Time-gated releases: Collections that activate at specific block numbers
  5. Curated drops: Controlled minting by collection creators rather than open registration

This standard enables these use cases while leveraging the existing ERC-8004 registry infrastructure.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Collection Structure

Every compliant collection contract MUST maintain the following collection properties:

Core Interface

Compliant collection contracts MUST implement the IERC8041Collection interface:

pragma solidity ^0.8.25;

interface IERC8041Collection {
    /// @notice Emitted when the collection is created or updated
    /// @param maxSupply Maximum number of agents in the collection
    /// @param startBlock Block number when minting becomes available
    /// @param open Whether the collection is open for minting
    event CollectionUpdated(uint256 maxSupply, uint256 startBlock, bool open);

    /// @notice Emitted when an agent is minted in the collection
    /// @param agentId The ERC-8004 token ID of the agent in the registry
    /// @param mintNumber The agent's position in the collection (1-indexed)
    /// @param owner The address that received the agent
    event AgentMinted(uint256 indexed agentId, uint256 mintNumber, address indexed owner);

    /// @notice Get the mint number for a specific agent
    /// @param agentId The ERC-8004 token ID of the agent
    /// @return mintNumber The agent's position in the collection (1-indexed, e.g., "5 of 1000")
    ///         Returns 0 if the agent was not minted through this collection
    function getAgentMintNumber(uint256 agentId) external view returns (uint256 mintNumber);

    /// @notice Get the current supply of the collection
    /// @return currentSupply Current number of agents minted
    function getCollectionSupply() external view returns (uint256 currentSupply);
}

Required Functions

Every compliant collection contract MUST implement the following functions:

Required Events

Every compliant collection contract MUST emit the following events:

Required Behavior

Collection Association

When an agent is minted through a collection contract, the contract MUST:

  1. Register the agent in the ERC-8004 registry
  2. Assign a sequential mint number starting from 1
  3. Store the agent's mint number internally
  4. Write collection metadata to the agent's Onchain Metadata using the key "agent-collection"
  5. Emit an AgentMinted event

The collection metadata format SHOULD be:

abi.encodePacked(
    address collectionAddress,  // 20 bytes: the collection contract address
    uint8 mintNumberLength,     // 1 byte: length of mint number bytes
    bytes mintNumberBytes       // variable: the mint number (compact encoding)
)

This enables any party to:

Mint Number Tracking

Mint numbers MUST:

Supply Management

Collection contracts MUST:

Optional Features

The following features are OPTIONAL and left to implementation choice:

Contract-level Metadata

Collections SHOULD implement ERC-7572 contract metadata to provide collection-level information such as name, description, image, and external links. This is done by implementing:

function contractURI() external view returns (string memory);

The URI should point to a JSON file following the ERC-7572 metadata schema.

Rationale

ERC-8004 agents are unlimited mint NFTs; this ERC was created to enable verifiable fixed-supply collections of agents. This standard attempts to make minting fixed-supply ERC-8004 agents as convenient as possible. The getCollectionSupply() function allows clients to query current supply without relying on an indexer. Collection configuration (maxSupply, startBlock, open) is communicated via the CollectionUpdated event, which is emitted at creation and whenever these values change.

Backwards Compatibility

This standard is fully backwards compatible with:

Reference Implementation

A minimal reference implementation demonstrating the core requirements:

pragma solidity ^0.8.25;

import {IERC8004AgentRegistry} from "./interfaces/IERC8004AgentRegistry.sol";
import {IERC8041Collection} from "./interfaces/IERC8041Collection.sol";

/**
 * @title ERC8041MinimalCollection
 * @notice Minimal reference implementation
 * @dev Users supply pre-registered agent IDs to add to collection
 */
contract ERC8041MinimalCollection is IERC8041Collection {
    IERC8004AgentRegistry public immutable agentRegistry;

    uint256 public maxSupply;
    uint256 public currentSupply;
    uint256 public startBlock;
    bool public open;

    mapping(uint256 agentId => uint256 mintNumber) private _agentMintNumber;

    constructor(
        IERC8004AgentRegistry _agentRegistry,
        uint256 _maxSupply
    ) {
        agentRegistry = _agentRegistry;
        maxSupply = _maxSupply;
        startBlock = block.number;
        open = true;

        emit CollectionUpdated(_maxSupply, block.number, true);
    }

    /**
     * @notice Add an existing agent to the collection
     * @param agentId The ID of an agent already registered in ERC-8004
     */
    function mint(uint256 agentId) external {
        require(currentSupply < maxSupply, "Supply exceeded");
        require(agentRegistry.ownerOf(agentId) == msg.sender, "Not agent owner");
        require(_agentMintNumber[agentId] == 0, "Already in collection");

        currentSupply++;
        uint256 mintNumber = currentSupply;
        _agentMintNumber[agentId] = mintNumber;

        // Store collection metadata in the agent's onchain metadata
        bytes memory mintNumberBytes = abi.encode(mintNumber);
        uint8 mintNumberLength = uint8(mintNumberBytes.length);
        bytes memory collectionMetadata = abi.encodePacked(
            address(this),      // 20 bytes: collection contract address
            mintNumberLength,   // 1 byte: length of mint number bytes
            mintNumberBytes     // variable: the mint number (compact encoding)
        );

        IERC8004AgentRegistry(address(agentRegistry)).setMetadata(
            agentId,
            "agent-collection",
            collectionMetadata
        );

        emit AgentMinted(agentId, mintNumber, msg.sender);
    }

    function getAgentMintNumber(uint256 agentId) external view returns (uint256) {
        return _agentMintNumber[agentId];
    }

    function getCollectionSupply() external view returns (uint256) {
        return currentSupply;
    }
}

This implementation demonstrates:

Security Considerations

The owner of an ERC-8004 agent can modify or remove the "agent-collection" metadata stored on their agent at any time. Client applications MUST NOT rely solely on this metadata to verify collection membership. Instead, clients SHOULD:

  1. Query the collection contract directly using getAgentMintNumber(agentId) to verify membership
  2. Use the metadata as a convenience lookup to discover which collection to query, but always verify with the collection contract itself
  3. Treat a mint number of 0 from the collection contract as definitive proof that an agent is not part of that collection

Copyright

Copyright and related rights waived via CC0.