Abstract

This ERC defines an onchain metadata record and an interface for expressing that an ERC-8004 agent identity is controlled by an external object, such as a token, an account, or a contract. The metadata record stores only the binding contract address (20 bytes) under a reserved metadata key. That contract is deployed as a canonical per-chain singleton. The standard, bound address, and token id are read from it with bindingOf(agentId), and bindingHashOf(agentId) returns a globally unique 32 byte identifier for the bound object.

This ERC introduces the nickname 8004A. An NFT registered as a master NFT under ERC-8217 can use the 8004A label, regardless of the registration method used.

Motivation

ERC-8004 registrations are themselves NFTs. For an existing NFT to own an ERC-8004 registration, the registration must be bound to that NFT. Without that binding, the ERC-8004 identity would not transfer with the master NFT when it changes owner. The owning NFT, called the master NFT, might be a project, character, or collection NFT. This ERC defines that binding. A canonical per-chain singleton binding contract records which master NFT each ERC-8004 agent id is bound to. The owner of the master NFT controls the bound ERC-8004 registration. When the master NFT is transferred, control follows automatically. The ERC-8004 ownership record itself never changes.

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.

Simplified Interface

Per-chain singleton binding contracts compliant with this ERC MUST expose a minimal interface that allows clients to:

  • retrieve the stored binding for an agent id
  • retrieve its globally unique bytes32 identifier with bindingHashOf(agentId)
  • index newly written bindings

The required interface is:

pragma solidity ^0.8.24;

interface IERC8217 {
    error UnknownAgent(uint256 agentId);

    enum Standard {
        ERC721,
        ERC1155,
        ERC6909,
        ERC1155F,
        ERC6909F,
        ACCOUNT,
        CONTRACT_OWNABLE,
        CONTRACT_ADMIN
    }

    struct Binding {
        Standard standard;
        address boundAddress;
        uint256 tokenId;
    }

    event AgentBound(
        uint256 indexed agentId,
        Standard indexed standard,
        address indexed boundAddress,
        uint256 tokenId,
        address registeredBy
    );

    function bindingOf(uint256 agentId) external view returns (Binding memory);

    function bindingHashOf(uint256 agentId) external view returns (bytes32);
}

The standard is part of the binding hash, so implementations and future updates to this standard MUST NOT renumber, reorder, or remove an existing Standard member.

ACCOUNT, CONTRACT_OWNABLE, and CONTRACT_ADMIN bind the registration to an address rather than a token, so tokenId is unused and MUST be zero. Because tokenId is part of the binding hash, a non-zero value would give the same bound address more than one identifier.

The binding contract MUST implement bindingHashOf(agentId) and MUST return the hash derived from the stored Binding together with this contract’s Interoperable Address; it MUST revert UnknownAgent(agentId) when no binding is registered for that id.

Deployment Model

A binding contract compliant with this ERC MUST be deployed as a single canonical singleton per chain.

Metadata Record

Implementations compliant with this ERC MUST store the binding as an ERC-8004 onchain metadata record. The key MUST be agent-binding, and the value MUST be exactly the 20-byte EVM address of that chain’s canonical binding contract:

key    agent-binding
value  0x9c4e8f2a1b7d6e3c0a5f8d2b9e1c4a7f3d6e8b0c

The address is stored unpadded, as abi.encodePacked rather than abi.encode, so the value is 20 bytes and not 32.

The binding contract is the per-chain singleton that implements IERC8217, returning the Binding for bindingOf(agentId) and the 32 byte identifier for bindingHashOf(agentId).

The standard, bound address, and token id MUST be obtained only from bindingOf on this contract (see IERC8217.Binding).

The AgentBound event records the immutable binding when it is first written. registeredBy is the address that caused the binding to be registered.

Required Behavior

An implementation that uses this ERC to represent a binding for an ERC-8004 agent MUST write the binding as an ERC-8004 onchain metadata record under the agent-binding key, as exactly 20 bytes holding the address of that chain’s canonical binding singleton. It MUST treat agent-binding as a reserved key and prevent untrusted callers from overwriting it. It MUST emit AgentBound(agentId, standard, boundAddress, tokenId, registeredBy) when the binding is first written, and it MAY define control semantics in the binding contract, including ERC-721 ownership or ERC-1155 / ERC-6909 balance-based control.

This ERC standardizes discovery and canonical binding verification only. It does not standardize authorization rules inside the binding contract.

Bindings are immutable: once agent-binding has been written for an agentId, both the stored binding contract address and the Binding returned by bindingOf(agentId) MUST remain unchanged. This allows clients and indexers to verify a binding once and rely on the result without cache invalidation.

Binding Hash

ERC-8004 agent ids from one chain overlap with ids from other chains, so they are not globally unique. The 32 byte hash returned by bindingHashOf(agentId) is globally unique.

For a binding contract implementing IERC8217, bindingHashOf(agentId) MUST return:

keccak256(ABI_ENCODE(
    bytes   bindingContractInteroperableAddress,
    uint8   standard,
    address boundAddress,
    uint256 tokenId
))

The four values are ABI-encoded as the tuple (bytes,uint8,address,uint256), and that byte string is hashed with keccak256 to produce the 32 byte result. The hash is a pure function of those four values, so implementations MAY compute it offchain.

bindingContractInteroperableAddress MUST be the full ERC-7930 Interoperable Address of the binding contract, on the chain hosting it, and MUST NOT be pre-hashed. For a binding contract at 0x1111111111111111111111111111111111111111 on Ethereum mainnet it is:

0x000100000101141111111111111111111111111111111111111111

ABI_ENCODE means the canonical ABI encoding, equivalent to Solidity abi.encode, and MUST NOT be packed encoding.

standard, boundAddress and tokenId MUST be the values returned by bindingOf(agentId).

For ease of reference, a binding hash may be called a Universal Binding Identifier, or UBID. A UBID is globally unique and always 32 bytes, which makes it usable as an identifier for a bound object by systems that reference one, such as reputation systems or other identity systems.

Rationale

Why multiple standards?

Control of an ERC-8004 agent identity takes several shapes, and each Standard value names a different authorization rule. The bound address on its own does not say which rule applies, because one address can satisfy more than one of them, so Standard records the choice explicitly.

Why a binding hash?

In many cases the bound object holds the agent’s primary identity, and the ERC-8004 registration is secondary to it. The common case is an NFT collection where each member has a registration bound to it. The NFT is the primary identity. It exists before the registration, it is what holders trade, and whoever holds it controls the registration.

The binding hash identifies the bound object, and its preimage contains no agent id and no registry address, so one bound object keeps one identifier however many registrations are bound to it.

While useful for discovery, to find a contract address and token id for example, the Binding is not as useful as an identifier. bindingHashOf reduces it to a single 32 byte value. A reputation system, or any system recording a claim about an agent, can key on that value rather than on one of the agent’s registrations.

The authorization rule is part of what is being identified, so standard is included in the hash. The same address bound under ACCOUNT and under CONTRACT_OWNABLE describes two different things and receives two different identifiers.

Backwards Compatibility

This ERC is backwards compatible with:

  • ERC-8004, because it only standardizes one metadata key and value format
  • ERC-721, ERC-1155, and ERC-6909 binding schemes, because it does not alter their token semantics

Security Considerations

This standard was recently updated. Four things changed in the interface:

  • tokenContract was renamed to boundAddress, in the Binding struct and in the AgentBound event
  • TokenStandard was renamed to Standard and grew from three values to eight
  • the bindingHashOf function was added
  • the UnknownAgent error was added

Renaming tokenContract to boundAddress leaves the encoding identical, though code referring to it by name needs updating. The new Standard values are appended, so values 0 through 2 keep their meaning. Neither addition changes an existing call. A client can tell an updated deployment from an older one by calling bindingHashOf, which an older deployment does not have. Indexers keyed on the un-updated standard should continue to function normally, because the rename leaves the AgentBound signature and its topics unchanged, though they will encounter Standard values they do not recognize.

ERC1155F and ERC6909F are ERC-1155 and ERC-6909 contracts exposing ownerOf for token ids with a single owner, specified by a draft ERC that will be referenced here once merged.

Trust in this system rests entirely on the binding contract’s code, whose authorization rules this ERC does not specify.

Copyright and related rights waived via CC0.