Weâve been running a production ERC-8004 implementation and wanted to
share how we handled binding, because the factory pattern we use may offer a simpler
path than a separate binding contract.
The structural binding approach
Our AgentIdentityRegistryFactory deploys one AgentIdentityRegistry per NFT collection.
The collection address is set at deploy time and never changes. This means binding isnât a
metadata field that can drift or be set incorrectly, itâs a structural invariant:
If an agent exists in registry X, it is bound to collection X. There is no separate
binding step and no possibility of an agent claiming a binding to a different collection.
The registerWithSource(sourceTokenId) call mints the agent NFT and records the
source token ID in one transaction. The binding is established atomically at registration
and is immutable by construction.
This directly addresses Codyâs third request â immutability is not a policy declaration
but an architectural consequence. There is no setBinding() function to protect against.
On the agent-binding metadata key
The refined design (storing only the 20-byte binding contract address) is cleaner than the
initial approach, but it adds an indirection layer that the factory pattern avoids entirely.
With a per-collection registry, the binding contract address IS the registry address â
clients already have it from the ERC-8004 factory lookup:
factory.lookup(collectionAddress) â registryAddress
registry.bindingOf(agentId) â (collectionAddress, tokenId)
No separate binding contract needed. The registry already holds this information.
Supporting all three indexer requests
Weâd advocate for adding these to the AgentIdentityRegistry interface directly rather
than to a separate binding contract:
1. AgentBound event, agree this is necessary for reactive indexing. In the factory
pattern it fires at registerWithSource() time:
event AgentBound(uint256 indexed agentId, address indexed tokenContract, uint256 tokenId);
2. tokenToAgentId(address tokenContract, uint256 tokenId) â this is a natural fit for
the per-collection registry since every agentâs source token is already stored. A reverse
lookup mapping would make marketplace integrations straightforward without hitting an
off-chain gateway.
3. Immutability â in the factory pattern this is already guaranteed. For implementations
using the agent-binding metadata key, an explicit AgentBound event with no corresponding
AgentUnbound event is probably sufficient to signal intent to indexers.
Proposal
Rather than a separate binding contract per agent, ERC-8217 could recognise the
factory-deployed per-collection registry as a compliant binding implementation, with
the minimal interface requirement being:
interface IAgentBindings {
event AgentBound(uint256 indexed agentId, address indexed tokenContract, uint256 indexed tokenId);
function bindingOf(uint256 agentId) external view returns (address tokenContract, uint256 tokenId);
function tokenToAgentId(address tokenContract, uint256 tokenId) external view returns (uint256 agentId);
}
Implementations that deploy per-collection registries satisfy these by construction.
Implementations that want per-agent flexibility can use the separate binding contract
approach. Both are valid, ERC-8217 defines the interface, not the architecture.
Live on mainnet: AgentIdentityRegistryFactory at 0xc2bb6502a7d8ee3cdb2f96508d6cdf426aa2858f