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:
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
Collection metadata: Associate agents with specific collections and track their provenance
Mint number tracking: Permanent record of each agent’s position in the collection (#1 of 1000)
Time-gated releases: Collections that activate at specific block numbers
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:
pragmasolidity^0.8.25;interfaceIERC8041Collection{/// @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
eventCollectionUpdated(uint256maxSupply,uint256startBlock,boolopen);/// @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
eventAgentMinted(uint256indexedagentId,uint256mintNumber,addressindexedowner);/// @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
functiongetAgentMintNumber(uint256agentId)externalviewreturns(uint256mintNumber);/// @notice Get the current supply of the collection
/// @return currentSupply Current number of agents minted
functiongetCollectionSupply()externalviewreturns(uint256currentSupply);}
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:
Write collection metadata to the agent’s Onchain Metadata using a key that identifies the collection
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(addresscollectionAddress,// 20 bytes: the collection contract address
uint8mintNumberLength,// 1 byte: length of mint number bytes
bytesmintNumberBytes// 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
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:
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.
A minimal reference implementation demonstrating the core requirements:
pragmasolidity^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.
*/contractERC8041MinimalCollectionisIERC8041Collection{IERC8004AgentRegistrypublicimmutableagentRegistry;uint256publicmaxSupply;uint256publiccurrentSupply;uint256publicstartBlock;boolpublicopen;/// @notice Metadata key: "agent-collection" or parameterized per ERC-8119 (e.g. agent-collection:label, agent-collection/label, agent-collection[label])
stringpublicimmutablecollectionKey;mapping(uint256agentId=>uint256mintNumber)private_agentMintNumber;constructor(IERC8004AgentRegistry_agentRegistry,uint256_maxSupply,stringmemory_collectionLabel){agentRegistry=_agentRegistry;maxSupply=_maxSupply;startBlock=block.number;open=true;collectionKey=bytes(_collectionLabel).length==0?"agent-collection":string(abi.encodePacked("agent-collection:",_collectionLabel));emitCollectionUpdated(_maxSupply,block.number,true);}/**
* @notice Add an existing agent to the collection
* @param agentId The ID of an agent already registered in ERC-8004
*/functionmint(uint256agentId)external{require(currentSupply<maxSupply,"Supply exceeded");require(agentRegistry.ownerOf(agentId)==msg.sender,"Not agent owner");require(_agentMintNumber[agentId]==0,"Already in collection");currentSupply++;uint256mintNumber=currentSupply;_agentMintNumber[agentId]=mintNumber;// Store collection metadata in the agent's onchain metadata
bytesmemorymintNumberBytes=abi.encode(mintNumber);uint8mintNumberLength=uint8(mintNumberBytes.length);bytesmemorycollectionMetadata=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);emitAgentMinted(agentId,mintNumber,msg.sender);}functiongetAgentMintNumber(uint256agentId)externalviewreturns(uint256){return_agentMintNumber[agentId];}functiongetCollectionSupply()externalviewreturns(uint256){returncurrentSupply;}}
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:
Query the collection contract directly using getAgentMintNumber(agentId) to verify membership
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
Treat a mint number of 0 from the collection contract as definitive proof that an agent is not part of that collection