Contract Reference

Every external function on the ERC-8004 Identity Adapter, organized by section.

All functions live on Adapter8004 (src/Adapter8004.sol). Exact parameter types track the contract source. Check the repo if a signature changes.

The adapter implements IERC8004IdentityRecord and IERC8004AdapterRegistration, so an ERC-8004 client can talk to the adapter using the same interfaces it uses to talk to a vanilla registry.

Initialization#

initialize#

function initialize(address identityRegistry_, address initialOwner) external

What it does

UUPS initializer. Wires the proxy to the ERC-8004 registry it forwards into, and assigns the admin role.

Parameters

  • identityRegistry_ — ERC-8004 IdentityRegistry the adapter forwards into.
  • initialOwner — admin address. Receives OwnableUpgradeable ownership.

Reverts

  • InvalidTokenContract() if identityRegistry_ is zero.
  • OwnableInvalidOwner(address) if initialOwner is zero.
  • InvalidInitialization() on a second call.

Caller

Whoever deploys the proxy. The implementation's constructor calls _disableInitializers(), so the bare implementation cannot be initialized directly.

Registration#

register (with metadata)#

function register(
    TokenStandard standard,
    address tokenContract,
    uint256 tokenId,
    string memory agentURI,
    MetadataEntry[] memory metadata
) public returns (uint256 agentId)

What it does

Mints a new ERC-8004 identity through the registry and binds it to (standard, tokenContract, tokenId). Writes the supplied metadata entries, the canonical 20-byte agent-binding row, and clears the default agentWallet slot the registry initialized to the adapter address.

Parameters

  • standardERC721, ERC1155, or ERC6909.
  • tokenContract — external token contract address.
  • tokenId — id within tokenContract.
  • agentURI — initial off-chain URI.
  • metadata — array of MetadataEntry { string metadataKey; bytes metadataValue; }.

Returns

  • agentId — the newly minted ERC-8004 id.

Reverts

  • InvalidTokenContract() if tokenContract is zero.
  • NotController(account, agentId) if the caller does not currently control the external token. (agentId == type(uint256).max since the agent does not exist yet.)
  • ReservedMetadataKey("agent-binding") if a caller tries to overwrite the canonical binding row through the metadata array.

Events

  • AgentBound(agentId, standard, tokenContract, tokenId, registeredBy).

Caller

Whoever currently controls the external token: literal ownerOf for ERC-721, or any holder with balanceOf > 0 for ERC-1155 / ERC-6909.

register (no metadata)#

function register(
    TokenStandard standard,
    address tokenContract,
    uint256 tokenId,
    string calldata agentURI
) external returns (uint256 agentId)

What it does

Convenience overload equivalent to the metadata-array form above with an empty MetadataEntry[]. Same revert behavior, same AgentBound event.

Returns

  • agentId — the newly minted ERC-8004 id.

Caller

Same as the metadata-array overload.

Controller-gated writes#

Every function in this section calls _requireController(agentId, msg.sender), which reverts with UnknownAgent(agentId) if the id was never bound and NotController(account, agentId) if the caller does not currently control the bound token. Each function below assumes those two reverts; only function-specific reverts and events are listed.

setAgentURI#

function setAgentURI(uint256 agentId, string calldata newURI) external

What it does

Updates the agent's off-chain URI in the registry.

Behavior

Forwards to identityRegistry.setAgentURI(agentId, newURI).

Events

  • AgentURISet(agentId, newURI, updatedBy) after the registry call succeeds.

Caller

Current controller of the bound token.

setMetadata#

function setMetadata(uint256 agentId, string memory metadataKey, bytes memory metadataValue) external

What it does

Writes one metadata key on the agent.

Reverts

  • ReservedMetadataKey("agent-binding") — the canonical binding row cannot be overwritten through this path.

Behavior

Forwards to identityRegistry.setMetadata(agentId, metadataKey, metadataValue).

Events

  • MetadataSet(agentId, metadataKey, metadataValue, updatedBy) after the registry call succeeds.

Caller

Current controller of the bound token.

setMetadataBatch#

function setMetadataBatch(uint256 agentId, MetadataEntry[] calldata metadata) external

What it does

Writes multiple metadata keys in one transaction.

Reverts

  • ReservedMetadataKey("agent-binding") if any entry tries to overwrite the canonical binding row.

Behavior

Loops over metadata and forwards each entry to identityRegistry.setMetadata. The count can be zero; the event still fires.

Events

  • MetadataBatchSet(agentId, count, updatedBy) after the loop.

Caller

Current controller of the bound token.

setAgentWallet#

function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes calldata signature) external

What it does

Binds an agent wallet (newWallet) to the ERC-8004 record. The registry verifies that newWallet itself signed the EIP-712 typed data.

Behavior

Forwards to identityRegistry.setAgentWallet(agentId, newWallet, deadline, signature). The registry enforces the signature rule. The signed payload uses owner = adapter address because the adapter is ownerOf(agentId) from the registry's view. See Concepts for the full caller-vs-signer breakdown.

Events

  • AgentWalletSet(agentId, newWallet, updatedBy) after the registry call succeeds.

Caller

Current controller of the bound token. Note: the signer of the signature is newWallet, not the caller.

unsetAgentWallet#

function unsetAgentWallet(uint256 agentId) external

What it does

Clears the agent's wallet binding.

Behavior

Forwards to identityRegistry.unsetAgentWallet(agentId). Idempotent.

Events

  • AgentWalletUnset(agentId, updatedBy) after the registry call succeeds.

Caller

Current controller of the bound token.

Counterfactual#

Counterfactual functions and events have a dedicated reference page. See Counterfactual for the emit-only registration surface, registrationHash, event ABI, and indexer guidance.

Views#

getMetadata#

function getMetadata(uint256 agentId, string memory metadataKey) external view returns (bytes memory)

What it does

Reads a metadata value from the registry. Lets ERC-8004 clients read directly from the adapter address without bouncing to the underlying registry.

Behavior

Forwards to identityRegistry.getMetadata(agentId, metadataKey).

getAgentWallet#

function getAgentWallet(uint256 agentId) external view returns (address)

What it does

Returns the bound agent wallet, or the zero address if none is set.

Behavior

Forwards to identityRegistry.getAgentWallet(agentId).

ownerOf#

function ownerOf(uint256 agentId) external view returns (address)

What it does

Returns the registry's owner of the ERC-8004 token. For any agent registered through this adapter, the owner is the adapter contract itself.

Behavior

Forwards to identityRegistry.ownerOf(agentId).

tokenURI#

function tokenURI(uint256 agentId) external view returns (string memory)

What it does

Returns the agent's URI as known to the registry.

Behavior

Forwards to identityRegistry.tokenURI(agentId).

bindingOf#

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

What it does

Returns the Binding { TokenStandard standard; address tokenContract; uint256 tokenId; } stored at registration time.

Reverts

  • UnknownAgent(agentId) if no binding exists.

agentIdForBinding#

function agentIdForBinding(TokenStandard standard, address tokenContract, uint256 tokenId)
    external view returns (uint256 agentId, bool exists)

What it does

Reverse lookup. Resolves an external (standard, tokenContract, tokenId) back to the bound agent id.

Returns

  • agentId — bound id, or 0 if no binding exists.
  • exists — source of truth. The adapter stores bindings as agentId + 1 internally, so agentId == 0 is correctly distinguished from "unset" via this boolean.

isController#

function isController(uint256 agentId, address account) external view returns (bool)

What it does

Returns true iff account currently controls the bound external token under that token's standard. Returns false (no revert) if agentId is unknown.

Behavior

Calls into the bound token contract:

  • ERC-721 — ownerOf(tokenId) == account.
  • ERC-1155 / ERC-6909 — balanceOf(account, tokenId) > 0.

Use this as a safe pre-flight before attempting a controller-gated write.

Admin#

setIdentityRegistry#

function setIdentityRegistry(address newRegistry) external

What it does

Swaps the registry the adapter forwards into.

Reverts

  • InvalidTokenContract() if newRegistry is zero.
  • OwnableUnauthorizedAccount(address) if the caller is not the owner.

Events

  • IdentityRegistryUpdated(previousRegistry, newRegistry, updatedBy).

Behavior

Previously-registered bindings remain in the adapter's storage, but writes targeted at their agentIds will now route into the new registry, which has no record of those ids. The new registry will revert controller-gated writes against those agentIds because they are unknown to it.

Caller

Owner only.

upgradeToAndCall#

function upgradeToAndCall(address newImplementation, bytes memory data) external payable

What it does

UUPS upgrade. Replaces the implementation behind the proxy.

Behavior

Gated by _authorizeUpgrade(onlyOwner). Renouncing ownership via renounceOwnership() permanently disables upgrades and registry swaps.

Caller

Owner only.

ERC-721 receiver#

onERC721Received#

function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4)

What it does

Returns IERC721Receiver.onERC721Received.selector. The adapter never calls safeTransferFrom against the external bound token, but it implements the interface so it can receive the ERC-8004 token from the registry on register.

Errors#

ErrorWhen
InvalidTokenContract()Zero address passed as registry or tokenContract
NotController(address account, uint256 agentId)Caller isn't the current controller of the bound token
UnknownAgent(uint256 agentId)agentId was never bound
ReservedMetadataKey(string metadataKey)Caller tried to write the canonical agent-binding key
OwnableUnauthorizedAccount(address)Non-owner hitting an admin function
OwnableInvalidOwner(address)Zero address passed as initialOwner
InvalidInitialization()Re-initialization attempt

Events#

On-chain#

EventFired by
AgentBound(agentId, standard, tokenContract, tokenId, registeredBy)register
AgentURISet(agentId, newURI, updatedBy)setAgentURI
MetadataSet(agentId, metadataKey, metadataValue, updatedBy)setMetadata
MetadataBatchSet(agentId, count, updatedBy)setMetadataBatch
AgentWalletSet(agentId, newWallet, updatedBy)setAgentWallet
AgentWalletUnset(agentId, updatedBy)unsetAgentWallet
BindingMetadataRewritten(agentId, updatedBy)rewriteBindingMetadata (owner-only migration helper)
IdentityRegistryUpdated(previousRegistry, newRegistry, updatedBy)setIdentityRegistry
OwnershipTransferred(previousOwner, newOwner)Inherited from OwnableUpgradeable
Upgraded(newImplementation)Inherited from UUPS

Counterfactual#

Declared on IERC8004AdapterCounterfactual. See Counterfactual for full signatures, indexed topics, and payload semantics.