EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8323: Source-Token Agent Binding for ERC-8004

Derive an ERC-8004 agent identity from a pre-existing ERC-721, recording permanent provenance while exposing live ownership separately.

Authors
Created2026-06-12
Discussion Linkhttps://ethereum-magicians.org/t/erc-8323-source-token-agent-binding-for-erc-8004/28920
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#1851PR open

EIP-GPT summary

Contents
AbstractMotivationSpecificationQuery-only source bindingRegistrationProvenance vs. live ownershipConsumer requirementsBinding scopeMetadata inheritanceRationaleBackwards CompatibilityReference ImplementationSecurity ConsiderationsCopyright

Abstract

This standard defines a minimal interface for an ERC-8004 agent identity registry that is bound to a single source ERC-721 collection. Holders of the bound collection bridge a token they own into an agent identity. The source token is recorded at registration time as permanent provenance and is never locked or transferred; ownership of the source token is checked once at mint via ownerOf and thereafter exposed as a separate, live, re-checkable view. This separates two facts that existing standards conflate: which token an agent was derived from (immutable) and whether the current agent holder still owns that token (mutable).

Motivation

ERC-8004 establishes agent identities but assumes an agent is minted fresh, with no relationship to a pre-existing asset. In practice the most natural agents are derived from NFTs people already hold β€” a PFP, a membership token, a game character. There is no standard way to say, verifiably and on-chain, "this agent is that NFT," nor to express the relationship without custody.

ERC-6551 (token-bound accounts) gives a token an account, but inverts the direction this standard needs: TBA is "a wallet owned by a token," whereas an agent identity derived from a token is "an identity whose provenance is a token, owned by whoever the agent owner is." The two compose but solve different problems.

Concretely, this standard answers:

  • Provenance: Which source token (collection + tokenId) was an agent minted from? This MUST be immutable for the life of the agent.
  • Live ownership: Does the current owner of the agent still own the source token? This MUST be queryable at any time without trusting stored state.
  • Non-custody: Binding MUST NOT lock, escrow, or transfer the source token. Eligibility is checked at mint via ownerOf; nothing on the source collection changes.
  • Visual identity: An agent bridged from a token SHOULD inherit that token's metadata, so it presents in wallets and marketplaces as the NFT it was derived from β€” making the binding legible to humans, not only to contracts.
  • Discoverability: Given a source collection, which registry holds its agents? (See the companion discovery standard.)

Keeping provenance and live ownership distinct is the core contribution. A consumer evaluating an agent can ask "was this agent legitimately minted by a holder of collection X?" (permanent, answered by getSourceNFT + the SourceNFTLinked log) independently of "is the agent's source NFT still held by the agent owner today?" (live, answered by isSourceNFTOwnershipValid). Collapsing these β€” as a naΓ―ve "bound NFT" field would β€” loses provenance the moment the source token is sold, and loses freshness the moment it is cached.

Specification

The key words "MUST", "MUST NOT", "SHOULD", and "MAY" are to be interpreted as described in RFC 2119 and RFC 8174.

A compliant registry MUST implement ERC-721 (the agent identity is itself an NFT), ERC-8004, and the following interface. It MUST return true from ERC-165 supportsInterface(0x27eba962), the interface id of IAgentSourceBinding (the XOR of its five function selectors).

interface IAgentSourceBinding /* is IERC165 */ {
    /// @notice Emitted once, at registration, binding an agent to its source token.
    ///         MUST be emitted exactly once per agentId and MUST NOT be emitted again.
    event SourceNFTLinked(
        uint256 indexed agentId,
        address indexed sourceContract,
        uint256 sourceTokenId
    );

    /// @notice The source ERC-721 collection this registry is bound to.
    /// @dev MUST be immutable for the life of the registry.
    function boundCollection() external view returns (address);

    /// @notice Register an agent derived from `sourceTokenId` of `boundCollection`.
    /// @dev MUST revert unless `IERC721(boundCollection).ownerOf(sourceTokenId) == msg.sender`.
    ///      MUST mint a new agent identity (ERC-721) to `msg.sender`.
    ///      MUST record (boundCollection, sourceTokenId) as immutable provenance.
    ///      MUST emit `SourceNFTLinked`.
    ///      MUST NOT lock, escrow, or transfer the source token.
    /// @return agentId The newly minted agent identity.
    function registerWithSource(uint256 sourceTokenId)
        external
        payable
        returns (uint256 agentId);

    /// @notice The immutable source token an agent was derived from.
    /// @dev MUST revert if `agentId` does not exist or has no source binding.
    function getSourceNFT(uint256 agentId)
        external
        view
        returns (address sourceContract, uint256 sourceTokenId);

    /// @notice Whether `agentId` has a recorded source binding.
    function hasSourceNFT(uint256 agentId) external view returns (bool);

    /// @notice Whether the source token is still under the control of `agentId`.
    /// @dev Live check via `ownerOf`. Valid if `ownerOf(sourceToken)` is the agent
    ///      holder, the agent's CANONICAL ERC-6551 token-bound account, OR the binding
    ///      contract itself (sovereignty patterns). A bare `ownerOf == ownerOf(agentId)`
    ///      check MUST NOT be used β€” it force-fails TBA/binding-custody bindings.
    ///      The TBA in (b) is pinned: the account from the ERC-6551 registry
    ///      0x000000006551c19487814612e58FE06813775758 with the implementation and salt
    ///      the registry declares β€” not "any TBA of the agent" (a token has many).
    ///      MUST NOT rely on cached ownership. Reverts for a non-existent `agentId`
    ///      (per ERC-721 `ownerOf`); returns false (not revert) when the source token
    ///      no longer exists.
    function isSourceNFTOwnershipValid(uint256 agentId) external view returns (bool);
}

Query-only source binding

Not every agent that can prove source ownership implements source-binding registration. The canonical case is a self-sourced agent, whose source is the agent contract itself β€” getSourceNFT(agentId) returns (address(this), agentId) β€” so there is no external collection and boundCollection / registerWithSource have no meaning for it. Such an implementation MUST NOT advertise IAgentSourceBinding (0x27eba962): it does not implement all five functions, and advertising the full id is a false ERC-165 positive that a consumer would trust.

For these implementations this standard defines a query-only companion interface, IAgentSourceBindingView β€” the read side of source binding, with ERC-165 id 0x8b3597c9 (the XOR of the getSourceNFT, hasSourceNFT, and isSourceNFTOwnershipValid selectors):

interface IAgentSourceBindingView /* is IERC165 */ {
    /// @notice The immutable source token an agent was derived from.
    /// @dev MUST revert if `agentId` does not exist or has no source binding.
    function getSourceNFT(uint256 agentId)
        external
        view
        returns (address sourceContract, uint256 sourceTokenId);

    /// @notice Whether `agentId` has a recorded source binding.
    function hasSourceNFT(uint256 agentId) external view returns (bool);

    /// @notice Whether the source token is still under the control of `agentId`.
    ///         Same semantics as in `IAgentSourceBinding`.
    function isSourceNFTOwnershipValid(uint256 agentId) external view returns (bool);
}

The three functions MUST implement semantics identical to their same-named counterparts in IAgentSourceBinding β€” this is a subsetting of interface surface, not a relaxation of behavior. isSourceNFTOwnershipValid in particular MUST implement the full three-case live-ownership rule defined under Provenance vs. live ownership below (direct holder, canonical ERC-6551 TBA, or binding-contract escrow) β€” a literal ownerOf(sourceToken) == ownerOf(agentId) check is non-conformant here for the same reason it is non-conformant in the full interface, and a self-sourced implementation is not exempt from it: a self-sourced agent can equally hold its source via a TBA or an escrowing binding contract rather than direct ownership, so a naΓ―ve equality check silently force-fails the sovereignty pattern this companion interface exists to serve. IAgentSourceBindingView is an independent interface, not a base of IAgentSourceBinding: the two ids stay fixed to the functions each declares.

  • An implementation that returns true from supportsInterface(0x8b3597c9) MUST implement all three functions with the semantics defined in this standard.
  • Such an implementation MUST return false from supportsInterface(0x27eba962) unless it also implements boundCollection and registerWithSource.
  • A full IAgentSourceBinding implementation MAY, and SHOULD, also return true from supportsInterface(0x8b3597c9) β€” the three functions are a subset it already implements β€” so a view-only consumer can detect it without probing the full interface.
  • A consumer that only needs to read and re-verify source ownership SHOULD accept either 0x8b3597c9 or 0x27eba962. A consumer that requires source-binding registration MUST check 0x27eba962.

The boundary is drawn here deliberately: IAgentSourceBindingView is not "a smaller interface for lesser contracts" but the honest limit of what a self-sourced agent can attest. Whether a source exists and is still owned is universal; registration presumes an external collection. An implementation MUST advertise only the interface it implements.

Registration

  • registerWithSource MUST verify live ownership of the source token by the caller and MUST revert otherwise.
  • Implementations MAY offer overloads accepting a custom agent URI and/or initial metadata, provided the source-ownership check and the SourceNFTLinked emission are preserved.
  • The source binding, once recorded, MUST be immutable. There MUST be no function that rebinds, clears, or overwrites the (sourceContract, sourceTokenId) of an existing agent.

Provenance vs. live ownership

  • getSourceNFT and the SourceNFTLinked event express permanent provenance and MUST remain valid even after the source token is transferred away.
  • isSourceNFTOwnershipValid expresses live ownership and MUST be computed from a current ownerOf call at query time.
  • Consumers MUST NOT infer live ownership from getSourceNFT alone.
  • Valid ownership is not bare owner-equality. isSourceNFTOwnershipValid MUST return true when ownerOf(sourceToken) is any of: (a) the current owner of agentId β€” direct, non-custodial holding; (b) the agent's canonical ERC-6551 token-bound account β€” the agent holds its own source (sovereignty); or (c) the binding contract itself β€” source escrowed under the binding (sovereignty). A literal ownerOf(sourceToken) == ownerOf(agentId) check is non-conformant: it reports every TBA- or binding-custody binding as permanently lapsed, force-failing the sovereignty pattern.
  • Case (b) MUST pin a single account. A token-bound account is CREATE2-derived from (registry, implementation, chainId, tokenContract, tokenId, salt), so one agent token is the base of unboundedly many candidate TBAs; "any TBA of the agent" is not checkable, and two implementations that pick differently disagree on identical chain state. The canonical account is the one computed via the ERC-6551 registry at 0x000000006551c19487814612e58FE06813775758 using the implementation and salt the binding registry declares. A conformant registry MUST make that implementation and salt determinable (e.g. a view returning the account, or values fixed in the implementation and documented).
  • Nonexistent agent vs. nonexistent source. isSourceNFTOwnershipValid MUST revert for a non-existent agentId (it inherits ERC-721 ownerOf semantics, consistent with getSourceNFT). The return-false-not-revert rule applies only to the source token: a burned or non-existent source token yields false, not a revert.

Consumer requirements

The provenance / live-ownership split exists so a consumer can ask "is this binding still true?" This subsection states when a consumer is obligated to ask.

  • Re-check at action time, not approval time. Any consumer gating an irreversible action on a source-bound identity β€” a settlement contract, an executor acting under a pre-action verdict, an escrow releasing on agent identity β€” MUST call isSourceNFTOwnershipValid at action time. An attestation, verdict, or approval references chain state as of its issuance timestamp; source-token ownership is mutable and can lapse between judgment and execution.
  • Atomicity. Where the consumer is a contract, the re-check MUST be atomic with the gated action β€” performed in the same transaction. A separate re-check transaction ahead of the action reintroduces the lapse window at smaller scale (a TOCTOU gap). An off-chain consumer MUST re-check as close to execution as possible and record the check timestamp alongside the action.
  • Verdict semantics under a lapsed binding. A pre-action verdict over a source-bound agent attests the identity premise as of the verdict's timestamp β€” the premise does not carry forward. If isSourceNFTOwnershipValid returns false at execution time, the executor MUST treat the failed re-check as a blocking condition: the verdict does not authorize execution under a broken binding. The verdict itself remains authentic and historically valid β€” what fails is the premise, not the signature. Consumers MUST distinguish the two when recording outcomes.
  • No canonical agent per source token. Because re-mint is permitted, no agentId is the agent for a source token; multiple simultaneously-valid bindings to one source token are possible (same holder, multiple mints). A consumer MUST treat the agentId as the identity and MUST NOT resolve "the agent for token X" by source token. The source token was never the identity β€” neither the accountability record nor any notion of canonicalness travels with it.
  • Caching. getSourceNFT is immutable and MAY be cached indefinitely. isSourceNFTOwnershipValid MUST NOT be cached across actions; each gated action performs its own check. Collapsing the two cache policies recreates exactly the conflation this standard exists to remove.

Note (non-normative): this registry never signals validity changes β€” they occur as Transfer events on the source collection. A consumer indexing binding validity MUST watch both contracts.

Reference data: the first governed action by a source-bound agent β€” tokenId 14, the "PGA #14" / Pixel Goblins #577 identity whose end-to-end provenance walk is given under Reference Implementation β€” was pre-action reviewed approve_with_concerns, top concern "identity-binding correctness": a validator asking, before this standard existed, precisely the question isSourceNFTOwnershipValid answers. Record: api.babyblueviper.com/ledger/23 (the WYRIWE input-provenance proposal's Β§L4 triple-hash, ethereum/ERCs#1810; verdict timestamp strictly before execution timestamp).

Binding scope

  • A registry MUST be bound to exactly one source collection (boundCollection), fixed at deployment and immutable thereafter.
  • A single deployment serves a single source collection. Multi-collection lookup is out of scope and is the responsibility of a separate registry-discovery standard (a factory mapping sourceCollection => registry). The reference implementation deploys one registry per collection via such a factory.

Metadata inheritance

  • On registration, an implementation SHOULD set the agent's ERC-721 tokenURI so that it resolves to metadata mirroring the source token's metadata β€” for example by reusing the source token's IPFS content β€” so that the bridged agent inherits the visual identity (image, name, attributes) of its source token.
  • The transport mechanism is implementation-defined: an implementation MAY reference the source token's content directly (same CID), or serve a derived metadata document that embeds the source media. The standard does not prescribe the document shape.
  • Metadata inheritance is RECOMMENDED rather than REQUIRED: an agent MAY carry distinct metadata where a separate visual identity is intended. Where an agent is meant to be its source NFT, mirroring the source metadata is the expected behavior and reinforces the provenance asserted by SourceNFTLinked.

Rationale

  • Live ownerOf over locking: non-custodial binding keeps the source NFT liquid and usable elsewhere; the standard never competes with the source collection for custody.
  • Immutable provenance: provenance that disappears on transfer is not provenance. Recording it once and never mutating it makes "minted by a holder of X" a permanent, log-indexable fact.
  • Separate live view: consumers that do care about current ownership (e.g. gating an action on continued holding) get a trustless live check, decoupled from the historical fact.
  • Agent identity as ERC-721: makes agents themselves transferable, marketplace-compatible, and royalty-bearing (ERC-2981), reusing existing infrastructure rather than inventing custody.
  • Metadata inheritance preserves identity: mirroring the source token's metadata means the agent presents as its source NFT, making the "this agent is that token" claim legible to wallets and marketplaces, not just to contracts reading getSourceNFT.
  • Fees are non-normative. registerWithSource is payable to accommodate implementations that charge a mint fee, but the standard does not define a fee, treasury, or pricing mechanism β€” these are implementation concerns. The reference implementation's per-registry mintPrice β†’ treasury forwarding is illustrative only. A conformant registry MAY mint for free.
  • Honest ERC-165 over convenient ERC-165. A self-sourced or read-only-source agent implements the read side of binding but not registration. Rather than let it advertise the full IAgentSourceBinding and misreport supportsInterface, the standard gives that profile its own id (0x8b3597c9). Advertising exactly what is implemented is the purpose of ERC-165; the companion view interface makes the honest choice expressible instead of forcing a false positive. Deriving the id via type(IAgentSourceBindingView).interfaceId (rather than a hardcoded constant) keeps it from drifting from the functions the interface declares.

Backwards Compatibility

This standard is strictly additive to ERC-8004 and changes no existing interface. A compliant registry is a valid ERC-721 and ERC-8004 identity registry; consumers unaware of IAgentSourceBinding treat agents as ordinary 8004 identities.

Reference Implementation

Deployed and live on Ethereum mainnet (chain id 1); dinamic.eth resolves through this stack. Every address below is verifiable with a single eth_call.

Deployment infrastructure

ContractAddressRole
AgentIdentityRegistryFactory0xc2bb6502a7d8ee3cdb2f96508d6cdf426aa2858fDeploys one AgentIdentityRegistry per source collection; lookup(collection), allCollections()
FactoryFacade0xC939b58F3B36293D3D55993ADD6EE0C79e7AAD2fPermissionless, fee-gated registry deployment

Live registries β€” each bound to a source ERC-721 collection, holding bridged agents:

Source collectionboundCollectionAgent registry (IAgentSourceBinding)Agents minted
Pixel Goblins0x6559807FfD23965d3aF54ee454bC69F113Ed06Ef0xe0454dFA17a57a84c3E0E2DBFdA5318CBBE91e2c14
Goblinarinos0x616F2ac5dd4F760dB693c21E9Ca7a8Aa962CF93b0xe61F5a6783AE09949B9a1b6821b68f89C0d7bB2D5
Sproto Gremlins0xEeca64ea9fCf99A22806Cd99b3d29cf6e8D549250x608520f2725C312092cC837a05a5A1c504AB26f40
Radbro Webring V20xABCDB5710B88f456fED1e99025379e2969F296100x860FAb14deDCFBf5C928E4Af0A88c6Ce0Ba879CF1

Verifiable cross-reference. The Pixel Goblins agent registry (0xe0454dFA…1e2c) reads totalSupply() == 14, with agent tokenId 14 derived from a Pixel Goblin held by its registrant β€” the same identity referenced as "PGA #14" in babyblueviper Ledger 23. Provenance is confirmable end to end: getSourceNFT(14) on the registry returns (0x6559807F…, <goblinTokenId>), and ownerOf(<goblinTokenId>) on the source collection can be compared against ownerOf(14) on the registry β€” exactly what isSourceNFTOwnershipValid(14) computes.

Source: AgentIdentityRegistry.sol in the ens-dynamic-kit stack. The reference implementation additionally provides EIP-712-verified agent wallets (setAgentWallet), ERC-2981 royalties, source-metadata inheritance, and an {agentId} URI template β€” only the IAgentSourceBinding surface is normative.

Self-sourced profile (IAgentSourceBindingView). The query-only companion is exercised by a self-sourced registry β€” an agent whose source is the registry itself. Reference: GenesisAgentRegistry at 0xe91934aB1f6A40cc1Bb4cD530FEFF56dFE524963 (mainnet), for which getSourceNFT(agentId) returns (0xe91934aB1f6A40cc1Bb4cD530FEFF56dFE524963, agentId), supportsInterface(0x8b3597c9) returns true, and supportsInterface(0x27eba962) returns false β€” advertising the read side it implements without the registration side it does not. Source: GenesisAgentRegistry.sol in the same stack.

Security Considerations

  • Provenance β‰  current ownership. Consumers MUST NOT treat getSourceNFT as proof the agent owner currently holds the source token; use isSourceNFTOwnershipValid for that. Conversely, a false from the live check does not retroactively invalidate provenance.
  • Source-token transfer after mint. Because binding is non-custodial, the source token can be sold while the agent persists. This is intended; provenance is historical. Applications gating on continued holding MUST re-check live.
  • Re-mint is permitted by design. A registry MUST NOT prevent the same source token from being bridged again. Eligibility is "the caller owns the source token now," so as a source token changes hands each successive holder MAY mint an agent from it. This is intentional and follows from the non-custodial model β€” forbidding it would require the standard to track historical mints and gate on them, recreating the custody it deliberately avoids. The resulting agents are distinct identities sharing a common provenance source.
  • Sybil surface. Because re-mint is allowed, the count of agents bound to a collection is not bounded by the collection's supply. Consumers MUST NOT treat "an agent exists for token X" as evidence of a unique or current relationship; combine getSourceNFT (provenance) with isSourceNFTOwnershipValid (live) and, where uniqueness matters, with application-level policy.
  • Track record does not travel with the source token. A re-minted agent inherits provenance, never track record. Accountability records (outcome-linked reputation: the WYRIWE proposal's Β§L4 recordPointer (ethereum/ERCs#1810), the AI-inference-proof-verification proposal's attestation/judgment history (ethereum/ERCs#1771)) bind to the agentId, not the source token. A consumer MUST NOT transfer trust across distinct agentIds sharing a source token: "this agent is Goblin #577" and "this agent has the predecessor's record" are different claims; only the first is made by this standard. A fresh agent re-earns its record from zero. This is a feature: it makes source-token resale Sybil-neutral at the trust layer (buying the token buys provenance and visual identity, not reputation) β€” the same boundary that proposal's aggregation guard draws: weight from settled outcomes and at-stake counterparties, never from inherited identity.
  • ownerOf reentrancy / hostile source collection. The source collection is arbitrary ERC-721 code; ownerOf is a view call but implementations MUST treat the bound collection as untrusted and avoid state assumptions across the call. The reference implementation reads ownerOf only.
  • Payable registration and value handling. registerWithSource is payable. An implementation that forwards or escrows value MUST revert the entire registration if that transfer fails, so no agent is minted without the associated value settling, and MUST NOT leave ETH stranded in the registry. The standard mandates no fee; a registry that mints for free SHOULD reject non-zero msg.value to avoid silent loss.
  • Smart-contract agent wallets (ERC-1271). Implementations that add an agent-wallet binding beyond the normative interface (the reference implementation's setAgentWallet) and accept a contract wallet MUST verify the authorization via ERC-1271 isValidSignature, treating any return other than the magic value as failure, and MUST bind the signed payload to both the agentId and an expiry to prevent cross-agent and replay reuse. This surface is outside IAgentSourceBinding but is called out because agent identities are commonly operated by smart-contract wallets.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth