# ERC-8004 Registration ## What is ERC-8004? An Ethereum standard for trustless agent identity. Each agent gets an ERC-721 NFT as a portable, verifiable on-chain identity. - **Spec**: - **Website**: - **Contracts**: ## Contract Addresses Same address on all EVM chains (CREATE2 deployment): | Chain | Chain ID | Identity Registry | Reputation Registry | Public RPC | |-------|----------|-------------------|---------------------|------------| | Base | 8453 | `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` | `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63` | `https://mainnet.base.org` | | Ethereum | 1 | `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` | `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63` | `https://eth.llamarpc.com` | | BNB Chain | 56 | `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` | `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63` | `https://bsc-dataseed.binance.org` | ## Mandatory Registration Gate ### `ENDPOINT_DECLARATION_GATE` (required before `register()`) Before building the registration JSON and calling `register(agentURI)`, enforce a hard yes/no endpoint declaration: Must ask owner: ``` Before ERC-8004 registration, confirm endpoint status: 1) Does this agent have an independent public endpoint to declare? (yes/no) 2) If yes, provide endpoint URL(s) for services (web / A2A / API / MCP) so I can verify reachability. 3) If no, confirm I should register with no services field. ``` Must record: - `endpointDeclaration.hasIndependentEndpoint` (`true` / `false`) - `endpointDeclaration.services` (if any) - `endpointDeclaration.note` (if no endpoint, record "no independent endpoint") Hard fail: - If endpoint status is not explicitly declared, stop and ask owner. - If endpoints are declared but cannot be verified reachable, stop before `register()`. ## Agent Registration File Format Host this JSON at a URL (HTTPS or IPFS): ```json { "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1", "name": "YourAgentName", "description": "What your agent does", "image": "https://example.com/avatar.png", "services": [ { "name": "web", "endpoint": "https://youragent.example.com/" }, { "name": "A2A", "endpoint": "https://youragent.example.com/.well-known/agent-card.json", "version": "0.3.0" } ], "x402Support": true, "active": true, "supportedTrust": ["reputation"] } ``` **Required fields**: `type`, `name`. All others are optional but recommended. ### Services Field The `services` array declares your agent's externally reachable endpoints. If your agent has any public-facing service, you should list it here — this makes your agent discoverable and interoperable with other agents. Before including any `services` entry, verify endpoint reachability: ```bash curl -s -o /dev/null -w "%{http_code}" https://youragent.example.com/ curl -s https://youragent.example.com/.well-known/agent-card.json | jq . ``` If verification fails, do not include that endpoint in registration until fixed. **Common service types:** | Service name | Description | Example endpoint | |-------------|-------------|------------------| | `web` | Public website or dashboard | `https://youragent.example.com/` | | `A2A` | Google A2A agent-to-agent protocol | `https://youragent.example.com/.well-known/agent-card.json` | | `API` | REST or GraphQL API | `https://api.youragent.example.com/v1` | | `MCP` | Model Context Protocol server | `https://youragent.example.com/mcp` | **If your agent has no independent endpoint** (e.g. it runs inside an IDE, as a CLI tool, or within another platform), omit the `services` field entirely. Use this minimal registration file instead: ```json { "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1", "name": "YourAgentName", "description": "What your agent does — be specific and honest", "x402Support": true, "active": true } ``` This is perfectly valid. An honest registration without fake endpoints is better than listing services that do not actually exist. Required record for this branch: ```json { "endpointDeclaration": { "hasIndependentEndpoint": false, "note": "no independent endpoint" } } ``` > **You can update later.** If your agent gains a public endpoint in the future, call `setAgentURI()` to update the registration file with the new `services` entries. See the "Updating Agent URI" section below. ## Hosting Options ### Option 1: HTTP URL (simplest) Host the JSON at any public URL. No IPFS needed. ```bash AGENT_URI="https://youragent.example.com/agent.json" ``` ### Option 2: IPFS You may upload the registration JSON to IPFS; this document does not provide specific tools or execution advice for IPFS uploads. Once you have an IPFS hash, your URI is `ipfs://{hash}`. ### Option 3: Data URI (fully on-chain, no hosting) ```bash # Base64-encode the registration JSON ENCODED=$(echo -n 'YOUR_JSON' | base64) AGENT_URI="data:application/json;base64,${ENCODED}" ``` ### Option 4: 8004.org frontend Visit and register through the UI. Handles IPFS automatically. ## On-Chain Registration ### Using viem (recommended) ```javascript import { createWalletClient, http, parseAbi } from "viem"; import { base } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount(PRIVATE_KEY); const client = createWalletClient({ account, chain: base, transport: http("https://mainnet.base.org"), }); const IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"; const hash = await client.writeContract({ address: IDENTITY_REGISTRY, abi: parseAbi(["function register(string agentURI) returns (uint256)"]), functionName: "register", args: [AGENT_URI], }); // Wait for receipt to get agentId from logs ``` ### Using cast (Foundry CLI) ```bash cast send 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 \ "register(string)" "$AGENT_URI" \ --private-key $PRIVATE_KEY \ --rpc-url https://mainnet.base.org ``` ### Using ethers.js ```javascript const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("https://mainnet.base.org"); const wallet = new ethers.Wallet(PRIVATE_KEY, provider); const registry = new ethers.Contract( "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432", ["function register(string agentURI) returns (uint256)"], wallet ); const tx = await registry.register(AGENT_URI); const receipt = await tx.wait(); // Parse agentId from receipt logs ``` ## After Registration Save to credentials: ```json { "agentId": 123, "agentURI": "https://youragent.example.com/agent.json", "nftId": "8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432:123" } ``` The `nftId` format is `{chainId}:{registryAddress}:{agentId}`. ## Setting Agent Wallet After registration, set a verified receiving wallet for payments: ```javascript // Uses EIP-712 signature to prove wallet ownership await client.writeContract({ address: IDENTITY_REGISTRY, abi: parseAbi([ "function setAgentWallet(uint256 agentId, address wallet, bytes signature)" ]), functionName: "setAgentWallet", args: [agentId, walletAddress, signature], }); ``` ## Updating Agent URI ```javascript await client.writeContract({ address: IDENTITY_REGISTRY, abi: parseAbi([ "function setAgentURI(uint256 agentId, string agentURI)" ]), functionName: "setAgentURI", args: [agentId, newURI], }); ``` ## Gas Costs | Operation | Estimated Gas | Cost on Base | |-----------|---------------|--------------| | `register()` | ~150-200k | ~$0.01-0.05 | | `setAgentURI()` | ~50-80k | ~$0.005-0.02 | | `setAgentWallet()` | ~60-100k | ~$0.005-0.02 | Base is significantly cheaper than Ethereum mainnet for registration.