Overview
The Primary Agent Registry is a singleton on-chain registry that links each Ethereum address to a single agent identity, enabling any address to be resolved to the agent it represents. It implements the ERC-8160 standard and works with ERC-8004 agent registries and ERC-8122 for cross-chain agent account discovery. It is deployed once per chain as a single authoritative source for agent resolution.
Agent IDs are packed into a compact byte sequence for efficient storage and transport:
20 bytes: registry address | 1 byte: ID length | N bytes: token IDGiven any Ethereum address, you can resolve the agent it represents with a single call:
(address registry, uint256 tokenId) = primaryAgentRegistry.resolveAgentId(someAddress);Standards
The Primary Agent Registry is built on three complementary ERC standards that together provide a complete agent identity layer for Ethereum.
ERC-8004: Agent Registry
Defines the NFT-based agent identity registry interface. Each agent is represented as an NFT in an ERC-8004 compliant registry, providing a standard way to create, manage, and query agent identities on-chain.
ERC-8122: Destination-Chain Agent-Owned Account Discovery
Enables cross-chain agent account discovery. Given an agent identity on one chain, ERC-8122 provides a standard mechanism to discover the accounts that agent controls on other destination chains.
ERC-8160: Primary Agent Registry (Draft)
Defines the singleton registry standard that maps Ethereum addresses to their agent identities. This is the standard that the Primary Agent Registry implements. ERC-8160 provides a single authoritative source per chain for resolving any address to its agent, using pluggable registrars for verification.
Architecture
The registry uses a hub-and-spoke architecture with a central PrimaryAgentRegistry and pluggable registrar contracts. The registry itself stores the mappings, while registrars handle verification logic for different account types.
PrimaryAgentRegistry
Central hub — stores address-to-agent mappings and delegates verification to registrars
SelfRegistrar
EOAs register themselves directly
ERC1271Registrar
Smart-contract wallets via ERC-1271 signature validation
OwnableRegistrar
Ownable contracts registered by their owner
AccessControlRegistrar
AccessControl contracts registered by an admin role
SignedRegistrar
Off-chain signature-based registration with replay protection
Registrars
Each registrar implements a different verification strategy, allowing the registry to support a wide range of account types.
| Registrar | Use Case | Verification |
|---|---|---|
| SelfRegistrar | EOA self-registration | msg.sender == target address |
| ERC1271Registrar | Smart-contract wallets (e.g. Safe, ERC-4337) | ERC-1271 isValidSignature check |
| OwnableRegistrar | Ownable contracts | Caller is owner() of target contract |
| AccessControlRegistrar | AccessControl contracts | Caller holds DEFAULT_ADMIN_ROLE on target |
| SignedRegistrar | Off-chain / gasless registration | EIP-712 typed signature with nonce replay protection |
Usage Examples
Register an EOA
An externally-owned account can register itself via the SelfRegistrar:
// The caller registers their own address
selfRegistrar.register(
agentRegistry, // address of the agent NFT registry
tokenId // the token ID representing the agent
);Register via Signature
For gasless or delegated registration, use the SignedRegistrar with an EIP-712 typed signature:
// A relayer submits the registration on behalf of the signer
signedRegistrar.register(
account, // address to register
agentRegistry, // address of the agent NFT registry
tokenId, // the token ID representing the agent
deadline, // signature expiration timestamp
signature // EIP-712 signature from the account holder
);Resolve an Address
Given any Ethereum address, resolve the agent identity it is linked to:
(address registry, uint256 tokenId) = primaryAgentRegistry.resolveAgentId(
someAddress
);
// registry == address(0) means no agent is registered
require(registry != address(0), "No agent registered");Contract Addresses
Sepolia
| Contract | Address |
|---|---|
| PrimaryAgentRegistry (Proxy) | 0xFb684dB5A38454Fe39cB314E495C1f5e3a3620c1↗ |
| PrimaryAgentRegistry (Implementation) | 0xe924d03daaf1AEcc39fDE1902551ce713812823b↗ |
| SelfRegistrar | 0xEbB9561Caa68009faf3D912334F7a3525a28F3B0↗ |
| ERC1271Registrar | 0xE831BA71aF7440a628f0b54476ad914d51731d8f↗ |
| OwnableRegistrar | 0xd180541E7aa7A0DD1ffF84C9Bd86Df7CEa8b7B4F↗ |
| AccessControlRegistrar | 0x39cFac3b757134247018DF61F6aA52d11764CC5C↗ |
| SignedRegistrar | 0x8C6E4dDeCc8ec13E6cbE34634f61656f7CB91999↗ |
Base
| Contract | Address |
|---|---|
| PrimaryAgentRegistry (Proxy) | 0xeC8554c3Ff5B986a6F631c402d281E27a7a42b5C↗ |
| PrimaryAgentRegistry (Implementation) | 0x090323AE9BD72E85BFbEA346bD9c9f24BbEE9AF8↗ |
| SelfRegistrar | 0x6F48C14C0C8426560B2b64240D17dB5f4F96FB27↗ |
| ERC1271Registrar | 0xABB1993b9eA0E15C10cC112C334CA21F4643dc55↗ |
| OwnableRegistrar | 0x96Def5706e2Cd957e347945C680d1d0f7bCc9184↗ |
| AccessControlRegistrar | 0x6F0f16F965A8885eA8E128A96b7d977375aa4468↗ |
| SignedRegistrar | 0x474E2193c25F74C824819ffCb60572fC0Ea00358↗ |
Storage Encoding
The registry packs each registration into a single storage slot for gas efficiency. The agent ID is encoded as a byte sequence containing the registry address, an ID-length byte, and the token ID.
| Field | Bytes | Description |
|---|---|---|
| Registry Address | 20 | The address of the agent NFT registry contract |
| ID Length | 1 | Length of the token ID in bytes (1–12) |
| Token ID | 1–12 | Big-endian encoded token ID, trimmed to minimal length |
This encoding fits within a single 32-byte storage slot for token IDs up to 12 bytes (covering IDs up to ~79 billion billion), keeping registration and resolution to a single SLOAD/SSTORE.
// Token ID 42 with registry at 0xABCD...1234
// Encoded: 0xABCD...1234 | 01 | 2A
// (20 bytes) (1) (1 byte for value 42)
// Token ID 100000 (0x0186A0) with same registry
// Encoded: 0xABCD...1234 | 03 | 01 86 A0
// (20 bytes) (1) (3 bytes)