Guides
Agent Identity

Agent Identity & Reputation

This guide walks through using the ERC-8004 on-chain identity and reputation system in the Veridex Agent SDK. By the end, your agent will have an on-chain identity (ERC-721 NFT), can accumulate reputation, discover other agents, and enforce trust gates before payments.

⚠️

Passkey Wallet Required: The Agent SDK (@veridex/agentic-payments) is built on @veridex/sdk, which requires a passkey wallet created via a browser frontend. Passkey operations use WebAuthn APIs and cannot run server-side. See the Agent Payments guide for the full setup flow.

Try It β€” Working Examples

Overview

The ERC-8004 (opens in a new tab) standard provides three singleton registries deployed on every EVM chain:

RegistryPurpose
IdentityAgent registration as ERC-721 NFT, metadata URI, wallet linkage
ReputationFeedback submission, revocation, scoring, summaries
ValidationThird-party attestations (spec in progress)

The SDK provides modular clients for each registry, plus higher-level abstractions for trust gating and agent discovery.

Prerequisites

bun add @veridex/agentic-payments

You'll need:

  • A funded wallet on an ERC-8004 supported chain (Base, Ethereum, Monad, etc.)
  • For IPFS publishing: a Pinata or web3.storage API key (optional β€” data URIs work without external deps)

Next.js Webpack Configuration

If you're using Next.js, you must configure next.config.mjs to handle transitive dependency resolution:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  transpilePackages: ['@veridex/sdk', '@veridex/agentic-payments'],
  webpack: (config, { isServer }) => {
    config.resolve.fallback = {
      ...config.resolve.fallback,
      fs: false,
      net: false,
      tls: false,
    };
 
    // Ignore problematic transitive deps from @aptos-labs/ts-sdk
    // that reference unexported paths in @noble/curves
    config.resolve.alias = {
      ...config.resolve.alias,
      '@aptos-labs/ts-sdk': false,
    };
 
    return config;
  },
};
 
export default nextConfig;
⚠️

Without this config, Next.js builds will fail with a Module not found: Package path ./nist.js is not exported from package @noble/curves error. See the Agent Payments guide for details.

1. Register an Agent Identity

Via AgentWallet (Recommended)

import { createAgentWallet } from '@veridex/agentic-payments';
 
const agent = await createAgentWallet({
  masterCredential: {
    credentialId: process.env.CREDENTIAL_ID!,
    publicKeyX: BigInt(process.env.PUBLIC_KEY_X!),
    publicKeyY: BigInt(process.env.PUBLIC_KEY_Y!),
    keyHash: process.env.KEY_HASH!,
  },
  session: {
    dailyLimitUSD: 50,
    perTransactionLimitUSD: 10,
    expiryHours: 24,
    allowedChains: [10004], // Base Sepolia
  },
  erc8004: {
    enabled: true,
    testnet: true,
  },
});
 
// Register on-chain β€” mints an ERC-721 NFT
const { agentId, agentURI } = await agent.register({
  name: 'Market Data Analyst',
  description: 'Autonomous agent that analyzes crypto market data',
  services: [
    { name: 'analyze', endpoint: 'https://my-agent.com/api/analyze' },
    { name: 'report', endpoint: 'https://my-agent.com/api/report' },
  ],
  x402Support: true,
});
 
console.log(`Registered as agent #${agentId}`);
console.log(`Agent URI: ${agentURI}`);

Via IdentityClient (Standalone)

import { IdentityClient } from '@veridex/agentic-payments';
import { ethers } from 'ethers';
 
const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const identity = new IdentityClient(provider, signer, { testnet: false });
 
const { agentId, agentURI } = await identity.registerWithFile({
  name: 'Price Oracle',
  description: 'Real-time crypto prices via Pyth + CoinGecko',
  services: [{ name: 'price', endpoint: 'https://oracle.example.com/price' }],
});

Registration mints an ERC-721 NFT on the Identity Registry. The NFT's tokenURI points to a registration file containing the agent's metadata and service endpoints. The same canonical registry address works on every EVM chain.

2. Build & Publish Registration Files

The RegistrationFileManager handles the creation, validation, and publishing of ERC-8004 registration files.

Build a Registration File

import { RegistrationFileManager } from '@veridex/agentic-payments';
 
const file = RegistrationFileManager.buildRegistrationFile({
  name: 'My Agent',
  description: 'Does cool things',
  services: [
    { name: 'api', endpoint: 'https://my-agent.com/api' },
    { name: 'webhook', endpoint: 'https://my-agent.com/webhook' },
  ],
});

Validate Against Schema

const { valid, errors } = RegistrationFileManager.validate(file);
 
if (!valid) {
  console.error('Validation errors:', errors);
} else {
  console.log('Registration file is valid');
}

Publish Options

Data URI (no external dependencies):

const dataURI = RegistrationFileManager.buildDataURI(file);
// data:application/json;base64,eyJuYW1lIjoiTXkgQWdlbnQiLC...

IPFS (requires Pinata or web3.storage):

const manager = new RegistrationFileManager({
  ipfs: {
    gateway: 'https://api.pinata.cloud',
    apiKey: process.env.PINATA_API_KEY!,
    provider: 'pinata',
  },
});
 
const ipfsURI = await manager.publishToIPFS(file);
// ipfs://QmXyz...

Manage Services

// Add a service
const updated = RegistrationFileManager.addService(file, {
  name: 'new-endpoint',
  endpoint: 'https://my-agent.com/new',
});
 
// Remove a service
const trimmed = RegistrationFileManager.removeService(updated, 'new-endpoint');

Well-Known File

For zero-lookup agent resolution, serve a well-known file:

const wellKnown = RegistrationFileManager.buildWellKnownFile(
  Number(agentId),
  `eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432:${agentId}`,
  ipfsURI,
);
 
// Serve at: https://my-agent.com/.well-known/agent-registration.json

Other agents can then resolve your agent just from your URL β€” no on-chain lookup needed.

3. Query & Submit Reputation

Get Reputation Score

// Via AgentWallet
const score = await agent.getReputationScore(targetAgentId);
console.log(`Agent reputation: ${score}/100`);
 
// Detailed summary
const summary = await agent.getReputation(targetAgentId);
console.log(`${summary.feedbackCount} reviews`);
console.log(`Score: ${summary.normalizedScore}/100`);
console.log(`Tags: ${summary.tags.join(', ')}`);

Submit Feedback

await agent.submitFeedback(targetAgentId, {
  score: 85,
  tags: ['fast', 'accurate', 'reliable'],
  comment: 'Excellent data quality and low latency',
});

Trusted Reviewers

Filter reputation to only count feedback from specific addresses:

const score = await agent.getReputationScore(targetAgentId, [
  '0xTrustedReviewer1...',
  '0xTrustedReviewer2...',
]);

Via ReputationClient (Standalone)

import { ReputationClient } from '@veridex/agentic-payments';
 
const reputation = new ReputationClient(provider, signer, { testnet: false });
 
await reputation.submitFeedback(agentId, { score: 90, tags: ['fast'] });
const summary = await reputation.getSummary(agentId, []);
const score = await reputation.getReputationScore(agentId);
 
// Revoke previous feedback
await reputation.revokeFeedback(agentId);

4. Trust-Gated Payments

The TrustGate enforces minimum reputation before the agent pays a merchant. This is configured via the erc8004.minReputationScore option.

Configure Trust Gate

const agent = await createAgentWallet({
  masterCredential: { /* ... */ },
  session: {
    dailyLimitUSD: 100,
    perTransactionLimitUSD: 10,
    expiryHours: 24,
    allowedChains: [10004],
  },
  erc8004: {
    enabled: true,
    testnet: false,
    minReputationScore: 30,       // Reject merchants below 30/100
    trustedReviewers: [           // Only count these reviewers
      '0xDeFiLlama...',
      '0xMessari...',
    ],
  },
});

How It Works

When agent.fetch() encounters a paywall:

  1. SDK detects the payment protocol (x402, UCP, ACP, AP2)
  2. TrustGate resolves the merchant's agent identity from the endpoint URL
  3. Queries the merchant's reputation score (filtered by trusted reviewers)
  4. If score < minReputationScore β†’ rejects the payment and throws an error
  5. If score >= threshold β†’ proceeds with payment
  6. After successful payment β†’ auto-submits reputation feedback

Manual Trust Check

const trust = await agent.checkMerchantTrust('https://data-provider.com');
 
if (trust.trusted) {
  console.log(`Trusted β€” score: ${trust.score}/100`);
} else {
  console.log(`Untrusted β€” reason: ${trust.reason}`);
}

5. Discover Agents

By Category

const sentimentAgents = await agent.discover({
  category: 'sentiment',
  minReputation: 30,
  limit: 10,
});
 
for (const a of sentimentAgents) {
  const score = await agent.getReputationScore(a.agentId);
  console.log(`Agent #${a.agentId}: ${a.name} β€” reputation: ${score}/100`);
}

Resolve from URL

const resolved = await agent.resolveAgent('https://oracle.example.com');
 
if (resolved) {
  console.log(`Found agent #${resolved.agentId}: ${resolved.name}`);
  console.log(`Reputation: ${resolved.reputationScore}/100`);
  console.log(`Resolved from: ${resolved.resolvedFrom}`); // 'url'
}

Resolve from UAI

The Universal Agent Identifier (UAI) extends CAIP-2 format:

eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432:42
β”‚       β”‚    β”‚                                          β”‚
β”‚       β”‚    β”‚                                          └── Agent ID
β”‚       β”‚    └── Identity Registry address
β”‚       └── Chain ID (Base mainnet)
└── Namespace (EVM)
const resolved = await agent.resolveAgent(
  'eip155:8453:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432:42'
);

Via AgentDiscovery (Standalone)

import { AgentDiscovery } from '@veridex/agentic-payments';
 
const discovery = new AgentDiscovery(identityClient, reputationClient);
 
// Resolve from any input type
const fromUrl = await discovery.resolve('https://my-agent.com');
const fromUai = await discovery.resolve('eip155:8453:0x8004A169...:42');
const fromAddr = await discovery.resolve('0x742d35Cc...');

6. Complete Example: Coordinator Agent

A coordinator agent that discovers specialist agents, checks their reputation, hires them, and submits feedback:

import { createAgentWallet } from '@veridex/agentic-payments';
 
async function coordinatorAgent() {
  const agent = await createAgentWallet({
    masterCredential: {
      credentialId: process.env.CREDENTIAL_ID!,
      publicKeyX: BigInt(process.env.PUBLIC_KEY_X!),
      publicKeyY: BigInt(process.env.PUBLIC_KEY_Y!),
      keyHash: process.env.KEY_HASH!,
    },
    session: {
      dailyLimitUSD: 200,
      perTransactionLimitUSD: 5,
      expiryHours: 8,
      allowedChains: [10004],
    },
    erc8004: {
      enabled: true,
      testnet: true,
      minReputationScore: 20,
    },
  });
 
  // 1. Register this coordinator
  const { agentId } = await agent.register({
    name: 'Research Coordinator',
    description: 'Orchestrates multi-agent research tasks',
    services: [{ name: 'coordinate', endpoint: 'https://coordinator.example.com/api' }],
  });
  console.log(`Registered as agent #${agentId}`);
 
  // 2. Discover sentiment analysis agents
  const sentimentAgents = await agent.discover({ category: 'sentiment' });
  console.log(`Found ${sentimentAgents.length} sentiment agents`);
 
  // 3. Pick the highest-reputation agent
  let bestAgent = null;
  let bestScore = 0;
  for (const a of sentimentAgents) {
    const score = await agent.getReputationScore(a.agentId);
    if (score > bestScore) {
      bestScore = score;
      bestAgent = a;
    }
  }
 
  if (!bestAgent) {
    console.log('No suitable agents found');
    return;
  }
 
  console.log(`Hiring agent #${bestAgent.agentId} (score: ${bestScore}/100)`);
 
  // 4. Call the agent's API (trust-gated fetch handles reputation + payment)
  const response = await agent.fetch(bestAgent.services?.[0]?.endpoint + '/analyze', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: 'Bitcoin just hit $100K!' }),
  });
 
  const result = await response.json();
  console.log('Sentiment result:', result);
 
  // 5. Feedback is auto-submitted after payment
  // Or submit manually with custom tags:
  await agent.submitFeedback(bestAgent.agentId, {
    score: 90,
    tags: ['fast', 'accurate'],
  });
}
 
coordinatorAgent().catch(console.error);

Canonical Registry Addresses

The same singleton addresses work on every EVM chain (deployed via CREATE2):

RegistryMainnetTestnet
Identity0x8004A169FB4a3325136EB29fA0ceB6D2e539a4320x8004A818BFB912233c491871b3d84c89A494BD9e
Reputation0x8004BAa17C55a88189AE136b182e5fdA19dE9b630x8004B663056A597Dffe9eCcC1965A193B7388713

Supported chains: Base, Ethereum, Polygon, Arbitrum, Optimism, Linea, MegaETH, Monad (+ all testnets).

Next Steps