Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-8041: Fixed-Supply Agent NFT Collections

Create fixed-supply collections of ERC-8004 Agent NFTs with mint number tracking

Authors Prem Makeig (@nxt3d)
Created 2025-10-11
Discussion Link https://ethereum-magicians.org/t/erc-8041-fixed-supply-agent-nft-collections/25656
Requires EIP-7572, EIP-8004, EIP-8119

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. Multiple collections per agent: Agents may belong to multiple named collections (e.g., agent-collection:dev-team, agent-collection/dev-team, or agent-collection[dev-team]) using ERC-8119 parameterized keys
  3. Collection metadata: Associate agents with specific collections and track their provenance
  4. Mint number tracking: Permanent record of each agent’s position in the collection (#1 of 1000)
  5. Time-gated releases: Collections that activate at specific block numbers
  6. 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:

  • maxSupply: Maximum number of agents that can exist in the collection
  • currentSupply: Current number of agents minted in the collection
  • startBlock: Block number when minting becomes available
  • open: Boolean indicating if the collection is currently accepting mints

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:

  • function getAgentMintNumber(uint256 agentId) external view returns (uint256 mintNumber) - Returns the mint number for a given agent ID (0 if not in collection)
  • function getCollectionSupply() external view returns (uint256 currentSupply) - Returns the current supply of the collection

Required Events

Every compliant collection contract MUST emit the following events:

  • event CollectionUpdated(uint256 maxSupply, uint256 startBlock, bool open) - MUST be emitted when the collection is created and whenever collection details are changed
  • event AgentMinted(uint256 indexed agentId, uint256 mintNumber, address indexed owner) - MUST be emitted when an agent is added to the collection

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 a key that identifies the collection
  5. Emit an AgentMinted event

The metadata key is either the default or a parameterized form:

  • agent-collection (no parameter, default): For the default or primary collection. This is the only case that does not use a parameterized key. Discoverable without indexing.
  • Parameterized form: For additional collections per agent, the key MUST follow ERC-8119 parameterized key format. The label identifies the collection. All ERC-8119 forms are acceptable: agent-collection:<label>, agent-collection/<label>, or agent-collection[<label>] (e.g., agent-collection:dev-team, agent-collection/dev-team, agent-collection[dev-team]). Because the label can be any value, indexing the agent’s metadata keys is necessary to discover collections beyond the default.

A collection contract MUST use a consistent key for all of its mints. Agents MAY belong to multiple collections; each collection writes to its own key.

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:

  • Look up which collection an agent belongs to
  • Verify the collection contract address
  • Decode the mint number from the metadata
  • Query additional information from the collection contract

Mint Number Tracking

Mint numbers MUST:

  • Start at 1 (not 0)
  • Increment sequentially as agents are minted
  • Be permanent and immutable once assigned
  • Be queryable via getAgentMintNumber()

Supply Management

Collection contracts MUST:

  • Enforce maxSupply limits (prevent minting beyond maximum)
  • Track currentSupply accurately
  • Emit AgentMinted events for each successful mint
  • Respect the startBlock timing constraint

Optional Features

The following features are OPTIONAL and left to implementation choice:

  • Access Control: Who can mint (owner, public, allowlist, etc.)
  • Payment Model: Free, paid (ETH/tokens), or other mechanisms
  • Collection State: Open/close mechanisms, locking, pausing
  • Batch Minting: Single or batch mint functions

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:

  • ERC-721: Agents remain standard ERC-721 tokens
  • ERC-8004: Leverages existing registry infrastructure
  • ERC-7572: Optionally supports contract-level metadata

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.
 *      Use empty label for default collection (agent-collection), or a label for
 *      named collections (e.g., agent-collection:dev-team) to support multiple
 *      collections per agent.
 */
contract ERC8041MinimalCollection is IERC8041Collection {
    IERC8004AgentRegistry public immutable agentRegistry;

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

    /// @notice Metadata key: "agent-collection" or parameterized per ERC-8119 (e.g. agent-collection:label, agent-collection/label, agent-collection[label])
    string public immutable collectionKey;

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

    constructor(
        IERC8004AgentRegistry _agentRegistry,
        uint256 _maxSupply,
        string memory _collectionLabel
    ) {
        agentRegistry = _agentRegistry;
        maxSupply = _maxSupply;
        startBlock = block.number;
        open = true;
        collectionKey = bytes(_collectionLabel).length == 0
            ? "agent-collection"
            : string(abi.encodePacked("agent-collection:", _collectionLabel));

        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,
            collectionKey,
            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:

  • Collection labels: Constructor accepts _collectionLabel; empty for default (agent-collection), or e.g. "dev-team" for agent-collection:dev-team (colon form; slash or bracket form also valid per ERC-8119) to enable multiple collections per agent
  • Single collection per contract: Simplest deployment model
  • No access control: Anyone can add their agents to the collection
  • Bring-your-own-agent model: Users register agents separately, then add them to the collection
  • Full mint number tracking: Each agent receives a sequential position number
  • Automatic start block: Collection starts at deployment block

Security Considerations

The owner of an ERC-8004 agent can modify or remove collection metadata (e.g., agent-collection or agent-collection:dev-team) 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. To discover labeled collections (e.g., agent-collection:dev-team, agent-collection/dev-team, agent-collection[dev-team]), index the agent’s metadata keys since labels are unbounded
  3. Treat a mint number of 0 from the collection contract as definitive proof that an agent is not part of that collection

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Prem Makeig (@nxt3d), "ERC-8041: Fixed-Supply Agent NFT Collections [DRAFT]," Ethereum Improvement Proposals, no. 8041, October 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-8041.