ERC-8004: Agent Trust Protocol
ERC-8004 establishes trust infrastructure for autonomous AI agents, enabling them to discover, identify, and evaluate other agents across organizational boundaries.
When to Use
- Registering an AI agent's identity on-chain
- Building reputation systems for AI agents
- Verifying agent identity before interactions
- Querying agent reputation and feedback
- Implementing trust-based agent interactions
Core Concepts
The Three Registries
| Registry | Purpose | Key Functions |
|----------|---------|---------------|
| Identity Registry | Agent discovery via ERC-721 NFTs | register(), tokenURI(), getAgentWallet(), setMetadata(), getMetadata() |
| Reputation Registry | Feedback and attestations | giveFeedback(), revokeFeedback(), readFeedback(), readAllFeedback(), getSummary(), appendResponse(), getClients() |
| Validation Registry | Verification hooks | Custom validators |
Protocol Stack Position
Application Layer (Agent Apps, Marketplaces)
↓
Trust Layer (ERC-8004) ← This skill
↓
Payment Layer (x402)
↓
Communication Layer (A2A, MCP)
Installation
npm install viem
ABIs are available at
skills/8004/references/identity-registry-abi.jsonandskills/8004/references/reputation-registry-abi.json.
Contract Addresses
Celo Mainnet (Chain ID: 42220)
| Contract | Address |
|----------|---------|
| Identity Registry | 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 |
| Reputation Registry | 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 |
Celo Sepolia Testnet (Chain ID: 11142220)
| Contract | Address |
|----------|---------|
| Identity Registry | 0x8004A818BFB912233c491871b3d84c89A494BD9e |
| Reputation Registry | 0x8004B663056A597Dffe9eCcC1965A193B7388713 |
Setup (viem)
import { createPublicClient, createWalletClient, http, getContract } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { celo, celoAlfajores } from 'viem/chains';
import identityRegistryAbi from './references/identity-registry-abi.json';
import reputationRegistryAbi from './references/reputation-registry-abi.json';
// Celo Mainnet addresses
const IDENTITY_REGISTRY = '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432';
const REPUTATION_REGISTRY = '0x8004BAa17C55a88189AE136b182e5fdA19dE9b63';
// Celo Sepolia Testnet addresses
// const IDENTITY_REGISTRY = '0x8004A818BFB912233c491871b3d84c89A494BD9e';
// const REPUTATION_REGISTRY = '0x8004B663056A597Dffe9eCcC1965A193B7388713';
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const publicClient = createPublicClient({
chain: celo,
transport: http('https://forno.celo.org'),
});
const walletClient = createWalletClient({
account,
chain: celo,
transport: http('https://forno.celo.org'),
});
Agent Registration
1. Agent Metadata Format
Create an agent metadata JSON and host it (IPFS, HTTPS, etc.):
{
"type": "Agent",
"name": "My AI Agent",
"description": "Description of capabilities",
"image": "ipfs://Qm...",
"endpoints": [
{ "type": "a2a", "url": "https://example.com/.well-known/agent.json" },
{ "type": "mcp", "url": "https://example.com/mcp" },
{ "type": "wallet", "address": "0x...", "chainId": 42220 }
],
"supportedTrust": ["reputation", "validation", "tee"]
}
2. Register Agent On-Chain
// Register without URI (minimal)
const hash = await walletClient.writeContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'register',
args: [],
});
// Register with metadata URI
const hash = await walletClient.writeContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'register',
args: ['ipfs://QmYourAgentMetadata'],
});
// Register with URI + custom metadata key-value pairs
const hash = await walletClient.writeContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'register',
args: [
'ipfs://QmYourAgentMetadata',
[
{ metadataKey: 'category', metadataValue: '0x' + Buffer.from('defi-trading').toString('hex') },
{ metadataKey: 'version', metadataValue: '0x' + Buffer.from('1.0.0').toString('hex') },
],
],
});
// Get agent ID from transaction receipt
const receipt = await publicClient.waitForTransactionReceipt({ hash });
const transferLog = receipt.logs.find(log => log.topics.length === 4); // Transfer event
const agentId = BigInt(transferLog.topics[3]); // tokenId is the 3rd indexed param
console.log('Agent registered with ID:', agentId);
3. Read Agent Data
// Get agent metadata URI
const agentURI = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'tokenURI',
args: [agentId],
});
// Get agent wallet address
const agentWallet = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'getAgentWallet',
args: [agentId],
});
// Get agent owner (NFT holder)
const owner = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'ownerOf',
args: [agentId],
});
// Get custom metadata
const categoryBytes = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'getMetadata',
args: [agentId, 'category'],
});
4. Update Agent
// Update agent URI
await walletClient.writeContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'setAgentURI',
args: [agentId, 'ipfs://QmUpdatedMetadata'],
});
// Set custom metadata
await walletClient.writeContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'setMetadata',
args: [agentId, 'status', '0x' + Buffer.from('active').toString('hex')],
});
Reputation System
Give Feedback
Note: Self-feedback is blocked — the agent owner/operators cannot give feedback to their own agent. The
valuefield isint128(supports negative scores).valueDecimalsmax is 18.
import { keccak256, toBytes } from 'viem';
const feedbackContent = JSON.stringify({ quality: 'excellent', notes: 'Fast response' });
const feedbackHash = keccak256(toBytes(feedbackContent));
await walletClient.writeContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'giveFeedback',
args: [
agentId, // agentId (uint256)
85n, // value (int128) — can be negative
0, // valueDecimals (uint8)
'starred', // tag1: category
'', // tag2: optional sub-category
'https://agent.example.com', // endpoint used
'ipfs://QmDetailedFeedback', // feedbackURI for details
feedbackHash, // keccak256 of feedback content
],
});
Common Feedback Tags
| Tag | Measures | Example |
|-----|----------|---------|
| starred | Quality rating (0-100) | 87/100 |
| uptime | Endpoint uptime % | 99.77% |
| successRate | Task success rate % | 89% |
| responseTime | Response time (ms) | 560ms |
| reachable | Endpoint reachable | 1/0 |
Revoke Feedback
// Revoke feedback at a specific index (1-indexed)
await walletClient.writeContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'revokeFeedback',
args: [agentId, 1n], // feedbackIndex (uint64, 1-indexed)
});
Respond to Feedback
Agents (or anyone) can append responses to feedback entries:
await walletClient.writeContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'appendResponse',
args: [
agentId,
clientAddress, // address of feedback author
1n, // feedbackIndex
'ipfs://QmResponse', // responseURI
responseHash, // keccak256 of response content
],
});
Query Reputation
// Get all clients who gave feedback
const clients = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getClients',
args: [agentId],
});
// Read a single feedback entry
const [value, valueDecimals, tag1, tag2, isRevoked] = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'readFeedback',
args: [agentId, clientAddress, 1n], // feedbackIndex (1-indexed)
});
// Read all feedback (pass clients array, filter by tags)
const allFeedback = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'readAllFeedback',
args: [
agentId,
clients, // clientAddresses — pass [] to auto-use all known clients
'', // tag1 filter ('' = all)
'', // tag2 filter ('' = all)
false, // includeRevoked
],
});
// Returns: { clients, feedbackIndexes, values, valueDecimals, tag1s, tag2s, revokedStatuses }
// Get aggregated summary (requires clientAddresses)
const [count, summaryValue, summaryValueDecimals] = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getSummary',
args: [
agentId,
clients, // clientAddresses (required, cannot be empty)
'', // tag1 filter
'', // tag2 filter
],
});
console.log(`Average: ${summaryValue} (${count} reviews, ${summaryValueDecimals} decimals)`);
Trust Verification Workflow
async function verifyAndInteract(targetAgentId, minScore = 70n) {
// 1. Verify identity exists (reverts if not)
const owner = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'ownerOf',
args: [targetAgentId],
});
// 2. Get clients and check reputation
const clients = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getClients',
args: [targetAgentId],
});
if (clients.length > 0) {
const [count, summaryValue] = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getSummary',
args: [targetAgentId, clients, '', ''],
});
if (summaryValue < minScore) {
throw new Error(`Agent reputation ${summaryValue} below threshold ${minScore}`);
}
}
// 3. Fetch agent metadata and find endpoint
const agentURI = await publicClient.readContract({
address: IDENTITY_REGISTRY,
abi: identityRegistryAbi,
functionName: 'tokenURI',
args: [targetAgentId],
});
const agentData = await fetch(agentURI).then(r => r.json());
const endpoint = agentData.endpoints.find(e => e.type === 'a2a');
// 4. Interact with verified agent
const result = await interactWithAgent(endpoint.url);
// 5. Submit feedback
await walletClient.writeContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'giveFeedback',
args: [
targetAgentId,
result.success ? 90n : 30n,
0,
result.success ? 'starred' : 'failed',
'',
endpoint.url,
'',
'0x0000000000000000000000000000000000000000000000000000000000000000',
],
});
return result;
}
Integration with x402 Payments
ERC-8004 and x402 work together for trustworthy paid agent interactions:
import { wrapFetchWithPayment } from 'thirdweb/x402';
async function payTrustedAgent(agentId, serviceUrl) {
// 1. Verify trust via reputation
const clients = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getClients',
args: [agentId],
});
if (clients.length > 0) {
const [count, summaryValue] = await publicClient.readContract({
address: REPUTATION_REGISTRY,
abi: reputationRegistryAbi,
functionName: 'getSummary',
args: [agentId, clients, '', ''],
});
if (summaryValue < 80n) {
throw new Error('Agent not trusted enough for payment');
}
}
// 2. Make paid request via x402
const fetchWithPayment = wrapFetchWithPayment({
client,
account,
paymentOptions: { maxValue: "1000000" },
});
const response = await fetchWithPayment(serviceUrl);
return response.json();
}
Validation Registry
For high-stakes operations, use Validation Registry for additional verification:
| Model | Mechanism | Best For | |-------|-----------|----------| | Reputation-based | Client feedback | Low-stake, frequent | | Crypto-economic | Stake + slashing | Medium-stake financial | | zkML | Zero-knowledge proofs | Privacy-preserving | | TEE Attestation | Hardware isolation | High-assurance |
Celo Network Reference
| Network | Chain ID | RPC Endpoint | |---------|----------|--------------| | Celo Mainnet | 42220 | https://forno.celo.org | | Celo Sepolia | 11142220 | https://forno.celo-sepolia.celo-testnet.org |
微信扫一扫