# ERC-8004 Explorer -- full corpus

Site: <https://erc-8004.quicknode.com>

This file bundles every article published under /docs, /learn, and
/tutorials. For a structured index instead, see <https://erc-8004.quicknode.com/llms.txt>.
For machine-readable API discovery, see
<https://erc-8004.quicknode.com/.well-known/api-catalog>.

## API surfaces

- REST API reference: <https://erc-8004.quicknode.com/docs/api/v1> -- paywalled per call via x402 USDC micropayments on Base. Payment flow: <https://erc-8004.quicknode.com/docs/api/v1/x402>.
- Postman collection: <https://erc-8004.quicknode.com/docs/collections/postman.json>
- JSON-RPC add-on (Quicknode marketplace): <https://marketplace.quicknode.com/add-on/erc-8004-api-agent-discovery-and-reputation?utm_source=erc8004-explorer&utm_medium=referral&utm_campaign=llms-txt> -- enable on your existing Quicknode JSON-RPC endpoint.
- Status / health: <https://erc-8004.quicknode.com/healthz>

# Docs

## Agents API

Source: <https://erc-8004.quicknode.com/docs/api/v1/agents>

# Agents

All endpoints below are paywalled via x402. See [x402 payment flow](/docs/api/v1/x402) for how to sign a payment.

## `GET /v1/agents`

Search and list agents.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents?network=base-mainnet&page=1&per_page=20"
```

**Query params**

| Param | Type | Notes |
|---|---|---|
| `network` | string | Network slug, e.g. `base-mainnet`. Optional. |
| `owner` | string | Owner address (`0x…`). Optional. |
| `protocol` | string | Protocol tag: `a2a`, `mcp`, `http-api`, `custom`, or `unconfigured`. Optional. |
| `trait[]` | string | Trait tag; repeat the param to require multiple traits (AND semantics). See [tags](#agent-tags). Optional. |
| `include_testnets` | bool | Include agents on testnets. Default `false`. |
| `page` | int | 1-indexed page number. Default `1`. |
| `per_page` | int | Page size, capped server-side. |

```bash
# MCP agents that take x402 payments and are registered on more than one chain
curl -i "https://erc-8004.quicknode.com/v1/agents?protocol=mcp&trait[]=x402-paid&trait[]=multichain"
```

**Response**

```json
{
  "data": [
    {
      "agent_id": 42,
      "network": { "slug": "base-mainnet", "name": "Base", "chain_id": 8453 },
      "owner_address": "0x…",
      "agent_uri": "ipfs://…",
      "protocol_tag": "mcp",
      "trait_tags": ["x402-paid", "multichain"],
      "feedback_count": 12,
      "validation_count": 3,
      "registered_at": "2026-04-01T12:00:00Z"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total_count": 137 }
}
```

### Agent tags

Every agent carries one derived `protocol_tag` plus zero or more `trait_tags`. Both are recomputed automatically whenever the agent's metadata is re-resolved, and activity-dependent traits refresh every ~10 minutes.

**Protocol tag** (exactly one):

| Tag | Meaning |
|---|---|
| `a2a` | A service exposes A2A `skills`. |
| `mcp` | A service lists an MCP capability (`tools`, `resources`, `prompts`, `sampling`) or an `mcp://` / `…/mcp` endpoint. |
| `http-api` | Plain `http(s)://` endpoint with no A2A/MCP markers. |
| `custom` | Has services, but none match the above. |
| `unconfigured` | No services declared. |

**Trait tags** (orthogonal, zero or more):

| Tag | Meaning |
|---|---|
| `x402-paid` | Metadata declares `x402Support: true`. |
| `multichain` | Registration file lists more than one registration. |
| `tee-attested` | Metadata includes a `teeAttestation` entry. |
| `validated` | At least one completed validation. |
| `active` | Metadata declares `active: true` **and** the agent has feedback or validation activity in the last 30 days. |

## `GET /v1/agents/:agent_id`

Single agent detail. `:agent_id` is the on-chain ID. Pass `network=<slug>` to disambiguate when the same ID exists on multiple chains.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents/42?network=base-mainnet"
```

**Response**

```json
{
  "data": {
    "agent_id": 42,
    "chain": "base-mainnet",
    "owner_address": "0x…",
    "agent_uri": "ipfs://…",
    "protocol_tag": "mcp",
    "trait_tags": ["x402-paid", "active"],
    "metadata": { "name": "...", "active": true },
    "registered_at": "2026-04-01T12:00:00Z",
    "feedback_count": 12,
    "validation_count": 3,
    "avg_validation_response": "P0DT4H12M",
    "reputation": { "score": 72, "confidence": "medium", "..." }
  }
}
```

`reputation` reflects a 30 s show-route cache; for fresher numbers call `GET /v1/agents/:agent_id/reputation` directly.

**Errors**

| Code | When |
|---|---|
| 404 | Agent doesn't exist on the requested network. |

## `GET /v1/agents/:agent_id/feedback`

Per-agent feedback feed.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents/42/feedback?network=base-mainnet&per_page=20"
```

**Query params**

| Param | Type | Notes |
|---|---|---|
| `tag1`, `tag2` | string | Filter by tag. |
| `since`, `until` | ISO 8601 | Time-bound. |
| `include_revoked` | bool | Default `false`. |
| `network` | string | Required when the same `:agent_id` exists on multiple chains. |
| `page`, `per_page` | int | Pagination. |

## `GET /v1/agents/:agent_id/validations`

Per-agent validations.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents/42/validations?network=base-mainnet"
```

**Query params**

| Param | Type | Notes |
|---|---|---|
| `tag` | string | Filter by validation tag. |
| `has_response` | bool | Only validations with (or without) a response payload. |
| `since`, `until` | ISO 8601 | Time-bound. |
| `network` | string | Required when ambiguous. |
| `page`, `per_page` | int | Pagination. |

## `GET /v1/agents/:agent_id/validations/summary`

Aggregate counts and tag breakdowns across an agent's validations.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents/42/validations/summary?network=base-mainnet"
```

**Query params**

| Param | Notes |
|---|---|
| `network`, `tag`, `validators`, `include_testnets` | All optional. |

## `GET /v1/agents/:agent_id/reputation`

Live reputation calculation. Bypasses the show-route cache; runs the formula against current data.

```bash
curl -i "https://erc-8004.quicknode.com/v1/agents/42/reputation?network=base-mainnet"
```

**Response**

```json
{
  "data": {
    "score": 72,
    "confidence": "medium",
    "formula_version": "v1.3",
    "sub_scores": { "feedback": 80, "validation": 65, "sybil_resistance": 70, "reliability": 75 },
    "signals": { "..." }
  }
}
```

See the [reputation formula](/reputation-v1) for the full breakdown.


---

## Feedback API

Source: <https://erc-8004.quicknode.com/docs/api/v1/feedback>

# Feedback

Paywalled via x402. See [x402 payment flow](/docs/api/v1/x402).

## `GET /v1/feedback`

Global feedback feed across every indexed agent.

```bash
curl -i "https://erc-8004.quicknode.com/v1/feedback?network=base-mainnet&per_page=20"
```

**Query params**

| Param | Type | Notes |
|---|---|---|
| `network` | string | Network slug filter. Optional. |
| `include_testnets` | bool | Default `false`. |
| `include_revoked` | bool | Default `false`. Revoked feedback is excluded from reputation but still indexed. |
| `page` | int | 1-indexed page number. |
| `per_page` | int | Page size. |

**Response**

```json
{
  "data": [
    {
      "id": 12345,
      "agent": {
        "agent_id": 42,
        "network": { "slug": "base-mainnet", "name": "Base", "chain_id": 8453 }
      },
      "client_address": "0x…",
      "tag1": "trust",
      "tag2": "responsive",
      "value": 95,
      "feedback_data": "...",
      "feedback_data_hash_verified": true,
      "revoked": false,
      "block_number": 41700000,
      "transaction_hash": "0x…",
      "submitted_at": "2026-04-15T08:30:00Z"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total_count": 9876 }
}
```

`feedback_data_hash_verified: true` means the content fetched from `feedback_data_uri` matched the on-chain hash. False (or null) means the off-chain payload was missing, modified, or unfetchable. The verification badge in the Explorer UI reads this field directly.

The same data is available per-agent via [`GET /v1/agents/:agent_id/feedback`](/docs/api/v1/agents).


---

## Validators API

Source: <https://erc-8004.quicknode.com/docs/api/v1/validators>

# Validators

Paywalled via x402. See [x402 payment flow](/docs/api/v1/x402).

## `GET /v1/validators/:address/requests`

All validation requests routed to a specific validator address. Useful for auditing validator behavior, response time, and which agents they cover.

```bash
curl -i "https://erc-8004.quicknode.com/v1/validators/0xValidatorAddress/requests?network=base-mainnet"
```

**Path params**

| Param | Notes |
|---|---|
| `address` | Validator address (`0x…`). 40-hex, with or without `0x` prefix. |

**Query params**

| Param | Type | Notes |
|---|---|---|
| `network` | string | Network slug. Optional. |
| `tag` | string | Filter by validation tag. |
| `has_response` | bool | Only requests with (or without) a response payload. |
| `since`, `until` | ISO 8601 | Time-bound. |
| `include_testnets` | bool | Default `false`. |
| `page`, `per_page` | int | Pagination. |

**Response**

```json
{
  "data": [
    {
      "request_hash": "0x…",
      "agent_id": 42,
      "network": { "slug": "base-mainnet", "name": "Base", "chain_id": 8453 },
      "response": "...",
      "has_response": true,
      "tag": "tee-attestation",
      "request_uri": "ipfs://…",
      "response_uri": "ipfs://…",
      "response_hash": "0x…",
      "block_number": 41700000,
      "transaction_hash": "0x…",
      "validated_at": "2026-04-15T08:30:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 50,
    "total_count": 137,
    "summary": {
      "total": 137,
      "with_response": 129,
      "avg_response_time": "P0DT2H15M"
    }
  }
}
```

The `summary` block on `meta` is computed across the full filtered set, not just the current page.


---

## x402 payment flow

Source: <https://erc-8004.quicknode.com/docs/api/v1/x402>

# x402 payment flow

The REST API charges a micropayment per request via the [x402 protocol](https://www.x402.org/). This page walks through one full request: hit the endpoint without payment, read the challenge, sign a payment, replay.

## Step 1 — Hit the endpoint without payment

```
curl -i https://erc-8004.quicknode.com/v1/agents
```

You get back `HTTP/1.1 402 Payment Required` with a JSON body:

```json
{
  "x402Version": 1,
  "error": "Payment required to access this resource",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x...",
      "maxAmountRequired": "1000",
      "resource": "https://erc-8004.quicknode.com/v1/agents",
      "description": "Payment required for /v1/agents",
      "maxTimeoutSeconds": 600,
      "mimeType": "application/json",
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ]
}
```

Key fields:

- `network` — `base` is Base mainnet.
- `asset` — the Base USDC contract address.
- `payTo` — the wallet address that will receive your payment.
- `maxAmountRequired` — `1000` means 1000 USDC base units (USDC has 6 decimals → $0.001).
- `extra` — EIP-712 domain hints (`name`, `version`) needed to construct a valid signature.

## Step 2 — Sign a payment

Use any x402 client library to build and sign the payload. The reference Python and TypeScript clients are documented at [x402.org](https://www.x402.org/). The signed payload is a base64-encoded EIP-712 signature authorizing a USDC transfer of `maxAmountRequired` to `payTo`.

## Step 3 — Replay with the payment header

```
curl -i \
  -H "X-PAYMENT: <base64-signed-payload>" \
  https://erc-8004.quicknode.com/v1/agents
```

Server-side, the request is routed through the [x402 facilitator](https://www.x402.org/facilitator) for verification and settlement. On success you get `HTTP/1.1 200 OK` with the JSON response. The settlement transaction hash is returned in the `X-PAYMENT-RESPONSE` header (base64-encoded).

## Settlement modes

The Explorer runs in **non-optimistic** mode by default: settlement completes before the response is sent, so a 200 is a guarantee that the transaction landed. Latency is ~1–2 seconds on Base.

## Security

Operated on Quicknode's SOC 2 Type II–audited infrastructure. The paywalled REST endpoints and the x402 settlement path both run on Quicknode's production estate, which is independently audited against the AICPA SOC 2 Type II controls. For the canonical scope and audit reports see [quicknode.com/security](https://www.quicknode.com/security).

## Errors

| Code | Meaning |
|---|---|
| 402 with `error: "Invalid payment: …"` | Signature didn't validate against the requirement. Check `network`, `asset`, `payTo`, `maxAmountRequired`. |
| 402 with `error: "Payment network not accepted"` | You signed for a different chain than what's advertised in `accepts`. |
| 402 with `error: "failed to settle payment: …"` | Facilitator-side failure. The signed payload was valid, but settlement (e.g., insufficient balance, replay) failed on-chain. |


---

## REST API v1 — overview

Source: <https://erc-8004.quicknode.com/docs/api/v1>

# REST API v1

A public REST API for searching ERC-8004 agents, fetching feedback, listing validations, and computing reputation scores across every indexed EVM chain.

## Pay-per-request via x402

Every REST call accepts a stablecoin micropayment per request:

- **Chain**: Base mainnet (production); Base Sepolia (staging).
- **Currency**: USDC.
- **Amount**: $0.001 USDC per request (subject to change; advertised in the `accepts` field of every 402 response).

If you don't include a payment header, the server returns `402 Payment Required` with the price and recipient details. Sign a payment, replay the request with the `X-PAYMENT` header, and you get the data. See [x402 payment flow](/docs/api/v1/x402) for the full walkthrough and a curl example.

There's no API key to provision, no contract to sign — any wallet that holds USDC on Base can call the API.

## Security & compliance

Operated on Quicknode's SOC 2 Type II–audited infrastructure. The Explorer and its REST API run on Quicknode's production estate, which is independently audited against the AICPA SOC 2 Type II controls. For the canonical scope, audit reports, and other compliance attestations see [quicknode.com/security](https://www.quicknode.com/security).

## Rate limiting

Per-IP soft cap: **300 requests/min globally**, with a **60 requests/min cap on `/v1/agents`**. Calls beyond the cap return `429 Too Many Requests` with a `Retry-After` header. The cap applies per source IP, so clients sharing a NAT share the bucket. If you need higher sustained throughput, the JSON-RPC API distributed via the [Quicknode marketplace add-on](https://quicknode.com?utm_source=erc8004-explorer&utm_medium=referral&utm_campaign=api-docs) ships with paid tiers that lift the cap.

## Endpoints

- [Agents](/docs/api/v1/agents) — `GET /v1/agents`, `GET /v1/agents/:agent_id`, plus per-agent feedback, validations, validation summary, and reputation.
- [Feedback](/docs/api/v1/feedback) — global feedback feed.
- [Validators](/docs/api/v1/validators) — per-validator request lookups.
- [x402 payment flow](/docs/api/v1/x402) — how to sign and replay a payment.

## Postman collection

A maintained Postman collection lives at [`/docs/collections/postman.json`](/docs/collections/postman.json). Import it into Postman or Insomnia for a quick start.

## What's not paywalled

- The Explorer UI (`/`, `/agents/...`, `/feedback`, `/validations`, `/status`) — open to humans and crawlers.
- `/healthz`, `/up` — operational health endpoints.


---

## ERC-8004 contract addresses + ABIs

Source: <https://erc-8004.quicknode.com/docs/contracts>

# Contract addresses + ABIs

The three ERC-8004 registries sit at deterministic CREATE2 addresses across every supported EVM chain. Identity and Reputation are live on every mainnet listed below. Validation is mainnet-pending and runs only on testnets while the working group finalizes the TEE attestation flow.

The tables below are generated from [`config/chains.yml`](https://github.com/quiknode-labs/erc-8004/blob/main/config/chains.yml) — the same file the indexer reads at boot, so the two never disagree. Click any contract address to open it on that network's block explorer.

## Mainnet addresses

| Chain | Identity Registry | Reputation Registry | Validation Registry | Identity deploy block |
|---|---|---|---|---|
| Ethereum | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://etherscan.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://etherscan.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 24,339,871 |
| Base | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://basescan.org/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://basescan.org/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 41,663,783 |
| BNB Chain | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://bscscan.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://bscscan.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 79,027,268 |
| Avalanche | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://snowtrace.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://snowtrace.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 77,389,037 |
| Mantle | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://mantlescan.xyz/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://mantlescan.xyz/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 91,333,846 |
| Arbitrum | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://arbiscan.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://arbiscan.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 428,895,443 |
| OP Mainnet | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://optimistic.etherscan.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://optimistic.etherscan.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 147,514,947 |
| Polygon | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://polygonscan.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://polygonscan.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 82,458,484 |
| Scroll | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://scrollscan.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://scrollscan.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 29,432,417 |
| Gnosis | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://gnosisscan.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://gnosisscan.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 44,505,010 |
| Celo | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://celoscan.io/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://celoscan.io/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 58,396,724 |
| Linea | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://lineascan.build/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://lineascan.build/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 28,662,553 |
| Abstract | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://abscan.org/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://abscan.org/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 39,596,871 |
| Soneium | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://soneium.blockscout.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://soneium.blockscout.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 18,879,198 |
| Monad | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://monadscan.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://monadscan.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | _(mainnet pending)_ | 52,952,790 |

The Identity and Reputation addresses are CREATE2-deterministic, so they are byte-identical on every mainnet; only the deploy block and the explorer differ. The Reputation Registry deploys 1–5 blocks after Identity on each chain (Validation 2–10 blocks after Identity on testnets); use [`config/chains.yml`](https://github.com/quiknode-labs/erc-8004/blob/main/config/chains.yml) for the exact per-registry deploy blocks if you're starting a Reputation- or Validation-only indexer.

## Testnet addresses

| Chain | Identity Registry | Reputation Registry | Validation Registry | Identity deploy block |
|---|---|---|---|---|
| Ethereum Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.etherscan.io/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.etherscan.io/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.etherscan.io/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 9,989,393 |
| Base Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.basescan.org/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.basescan.org/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.basescan.org/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 36,304,145 |
| BNB Testnet | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://testnet.bscscan.com/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://testnet.bscscan.com/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://testnet.bscscan.com/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 84,555,147 |
| Avalanche Fuji | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://testnet.snowtrace.io/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://testnet.snowtrace.io/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://testnet.snowtrace.io/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 51,485,079 |
| Mantle Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.mantlescan.xyz/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.mantlescan.xyz/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.mantlescan.xyz/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 34,586,937 |
| Arbitrum Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.arbiscan.io/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.arbiscan.io/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.arbiscan.io/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 239,945,838 |
| OP Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia-optimism.etherscan.io/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia-optimism.etherscan.io/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia-optimism.etherscan.io/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 39,412,868 |
| Polygon Amoy | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://amoy.polygonscan.com/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://amoy.polygonscan.com/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://amoy.polygonscan.com/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 33,069,064 |
| Scroll Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.scrollscan.com/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.scrollscan.com/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.scrollscan.com/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 16,543,185 |
| Abstract Sepolia | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://sepolia.abscan.org/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://sepolia.abscan.org/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://sepolia.abscan.org/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 16,493,173 |
| Monad Testnet | [`0x8004A818BFB912233c491871b3d84c89A494BD9e`](https://testnet.monadexplorer.com/address/0x8004A818BFB912233c491871b3d84c89A494BD9e) | [`0x8004B663056A597Dffe9eCcC1965A193B7388713`](https://testnet.monadexplorer.com/address/0x8004B663056A597Dffe9eCcC1965A193B7388713) | [`0x8004Cb1BF31DAf7788923b405b754f57acEB4272`](https://testnet.monadexplorer.com/address/0x8004Cb1BF31DAf7788923b405b754f57acEB4272) | 10,391,697 |

## ABIs

The canonical JSON ABIs live in the reference contracts repo at [github.com/erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts) under the `abis/` directory. Those files are the Hardhat build artifacts, so they drop straight into viem, ethers, or web3.py without any conversion. If you only need a quick reference for what each registry emits, the event signatures below are enough to wire up an `eth_getLogs` call or filter a subscription:

```text
IdentityRegistry:   Registered(uint256,string,address)
                    MetadataSet(uint256,string,string,bytes)
                    URIUpdated(uint256,string,address)
                    Transfer(address,address,uint256)        // ERC-721 inherit
ReputationRegistry: NewFeedback(uint256,address,uint64,int128,uint8,string,string,string,string,string,bytes32)
                    FeedbackRevoked(uint256,address,uint64)
                    ResponseAppended(uint256,address,uint64,address,string,bytes32)
ValidationRegistry: ValidationRequest(address,uint256,string,bytes32)
                    ValidationResponse(address,uint256,bytes32,uint8,string,bytes32,string)
```

These are the exact signatures this Explorer's indexer parses on every supported chain, and the [/tutorials/viem](/tutorials/viem) and [/tutorials/register-agent](/tutorials/register-agent) walkthroughs consume them directly.

## Where to go next

- [Register your first agent](/tutorials/register-agent)
- [Read agent data with viem](/tutorials/viem)
- [Get RPC for any of these chains](/quicknode)
- [What is ERC-8004?](/learn/what-is-erc-8004)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)
- Reference contracts repo: [https://github.com/erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts)



# Learn

## Agent protocols — MCP, A2A, x402, MPP, AP2, and ERC-8004 explained

Source: <https://erc-8004.quicknode.com/learn/agent-protocols-overview>

# Agent protocols — MCP, A2A, x402, MPP, AP2, and ERC-8004 explained

A useful autonomous agent needs to call tools, talk to other agents, pay and be paid, and prove who it is. Six open protocols cover those gaps: MCP handles tool access, A2A handles peer coordination, x402 and MPP each define an HTTP-402 payment handshake, AP2 handles the higher-level payment layer above them, and ERC-8004 handles identity and reputation. They don't compete head-on — each is doing a job the others mostly don't touch — though x402 and MPP do occupy the same slot, which the body covers.

This page describes each protocol on its own terms. ERC-8004 is maintained by this team, but that's not a reason to give it more coverage — the write-ups have to be fair across the board to be useful.

## The problem each protocol solves

Without shared protocols, an agent is like a computer with no network stack — fully capable of running local code, unable to do much with the outside world.

**Tools.** An agent needs to read files, call APIs, query databases. Without a shared interface, every tool is a custom integration. MCP standardizes the contract between agent and tool so a single client can talk to any conforming server.

**Peer coordination.** Agents working on complex tasks often need to find other agents and delegate sub-problems. A2A defines the discovery and handshake protocol so agents can locate each other and negotiate a job without bespoke glue.

**Payments.** An agent calling a paid API can't fill in a checkout form. x402, MPP, and AP2 handle the machine-readable side of that — how an HTTP server signals "pay before I return data," and how an agent programmatically satisfies that requirement.

**Identity.** An agent calling another agent, or a human evaluating whether to trust one, needs a stable verifiable record. ERC-8004 puts that record onchain so anyone can read it without asking the platform that hosts the agent.

## MCP — Model Context Protocol

Anthropic published the Model Context Protocol spec in late 2024 to address a real problem: every tool had a different integration API, which meant tool authors and agent authors were both writing a lot of custom glue code.

MCP defines a uniform interface between an **MCP client** (the agent, or the model environment it runs in) and an **MCP server** (a tool, data source, or service). Servers expose three capability types: **resources** (readable context, like files or database rows), **prompts** (pre-written prompt templates), and **tools** (callable functions with defined input/output schemas). The client discovers what a server offers and calls into it using the shared wire format.

MCP doesn't touch the LLM itself, the agent's reasoning, or payments. An MCP server has no concept of who is calling or whether the caller has paid. That's intentional — MCP solves one problem and leaves the rest to other layers.

As of early 2026, MCP has the widest adoption of the six protocols covered here. Most major model providers ship an MCP client, and a growing catalog of servers covers common categories: filesystem, web search, code execution, databases, and external APIs.

Concretely: an agent that needs to read a document before deciding what to do issues a `resources/read` request through its MCP client to an MCP filesystem server. The server returns the document content in the standard format. The agent never needed to know which filesystem library the server was built on.

Spec: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/)

## A2A — Agent2Agent

MCP handles agent-to-tool communication. Google's Agent2Agent (A2A) protocol handles the other side: agent-to-agent communication.

The central primitive is the **agent card** — a JSON document published at a well-known URL for each agent. The card describes the agent's name, capabilities, supported tasks, and the endpoint to call. An agent looking for a counterpart fetches the card, checks whether capabilities match, and submits a task using the A2A protocol. Task state runs through a defined lifecycle: submitted, working, completed, failed.

The two protocols are designed to coexist. An agent might use MCP to fetch data from tools, then A2A to hand part of the problem to a peer. They don't conflict; they operate at different points in the call graph.

As of early 2026, A2A has gained traction in enterprise multi-agent scenarios, though production deployment is narrower than MCP's broader developer-ecosystem reach.

A worked case: a research agent finishes an English summary and needs a translation. It pulls the agent card from a known translation service, confirms translation is in the listed capabilities, and submits a task via A2A. The translation agent returns the result through the task-completion flow.

Spec: [https://github.com/google/A2A](https://github.com/google/A2A)

## x402

x402 is a payment protocol for HTTP APIs, originated by Coinbase. The design reuses an existing HTTP status code — 402 Payment Required — that has been reserved since 1999 but was never standardized. x402 gives it a concrete definition for programmatic agent payments.

The flow has four steps: the API server returns a 402 response with a machine-readable payment specification (amount, token, network, recipient); the client executes the payment on the specified chain; the client retries the original request with a `X-Payment` header carrying proof of payment; the server verifies and returns a 200. The whole exchange happens without human intervention.

x402 does not dictate which chain or token to use. The 402 response body specifies the payment requirements; the client picks a supported option. In practice, the Coinbase reference implementation focuses on USDC on Base, but the protocol itself is payment-rail-agnostic.

The protocol handles micropayments well. A single API call that costs a fraction of a cent doesn't work with traditional invoicing; x402 makes per-call billing practical.

As of early 2026, x402 has a working reference implementation and production usage in payment-focused agents.

In practice, an agent calling a paid data API receives `402` with a body specifying 0.01 USDC on Base. The agent signs a transaction, gets a receipt, and retries the request with the receipt in the header. The server verifies onchain and returns the data.

Spec / reference implementation: [https://github.com/coinbase/x402](https://github.com/coinbase/x402)

## MPP — Machine Payments Protocol

MPP is a second HTTP-402 payment standard, published by Stripe and Tempo on 2026-03-18. Like x402, it lets an HTTP server demand payment and an agent pay programmatically — same 402 status code, different headers, different set of rails.

The flow has three steps. The server returns `402` with a **Challenge** in the `WWW-Authenticate` header, naming what payment it will accept. The client retries with a **Credential** in the `Authorization` header, proving payment on a supported rail. The server validates and returns the resource along with a **Receipt** in the `Payment-Receipt` header. The shape mirrors HTTP authentication rather than x402's single `X-Payment` header.

The bigger difference is which rails are supported. MPP defines bindings for Stripe cards (Visa, Mastercard), Tempo stablecoins, Lightning Bitcoin, Solana (SOL and SPL tokens), Monad ERC-20s, Stellar SEP-41 tokens, and a generic slot for custom methods. x402's reference implementation focuses on USDC on Base; MPP's spans chains and adds fiat through Stripe's existing payments stack.

MPP and x402 cover the same slot. Both can satisfy a 402 paywall, but a server speaks one or the other — the headers don't interoperate. Clients that want broad reach implement both. SDKs ship in TypeScript, Python, Rust, Go, and Ruby.

A working case: an agent calls a Stripe-fronted API and gets `402` with a Challenge advertising USD card payment, with Tempo stablecoin as an alternative. The agent picks card, attaches a Credential, and the server returns the data plus a Receipt the agent keeps for accounting.

Spec: [https://mpp.dev/](https://mpp.dev/) · Launch announcement: [https://stripe.com/blog/machine-payments-protocol](https://stripe.com/blog/machine-payments-protocol)

## AP2 — Agent Payments Protocol

AP2 (Agent Payments Protocol) covers a broader slice of agent payments than x402. Where x402 specifies a single payment handshake at the HTTP level, AP2 defines the higher-level conceptual layer: **payment intents** (a declared budget and purpose), **mandates** (a pre-authorized recurring agreement), and **settlement** (how individual payments close against those intents).

The relationship between the two is layered rather than competing. An AP2-enabled agent can use x402 as the transport for individual payments while managing the overall budget and authorization through AP2's intent/mandate primitives. AP2's intent and mandate layer is transport-agnostic — it can sit above x402 or MPP equally, since the distinction between those two is at the HTTP transport, not at the budget layer. Not all AP2 implementations require either one — other settlement transports are possible — but the protocols compose cleanly when both are present.

As of early 2026, AP2 is in active development and less deployed than x402, which is narrower and easier to adopt standalone. AP2's main use case is multi-step workflows where an agent needs to pre-authorize a budget rather than seek approval for each payment.

Picture an agent running a research workflow across five paid data services. It declares a single AP2 payment intent covering the expected total. Each per-service payment draws against that mandate. The hiring agent audits one settlement record instead of five separate flows.

Spec: [https://github.com/google-agentic-commerce/AP2](https://github.com/google-agentic-commerce/AP2)

## ERC-8004

The odd one out in this list is ERC-8004 — it doesn't handle tool calls, agent discovery, or payments. What it handles is the question every other layer eventually needs answered: *who is this agent, and is it worth trusting?*

ERC-8004 is an Ethereum Improvement Proposal that defines three onchain registries for agent identity and reputation. The full spec is at [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004); a deeper explanation lives at [What is ERC-8004?](/learn/what-is-erc-8004).

**The three registries:**

- **Identity Registry** — mints an ERC-721 token per agent, whose `agentURI` resolves to metadata (name, description, services). Durable as long as the chain runs. Read more: [Identity Registry](/learn/registries/identity).
- **Reputation Registry** — stores client feedback. Each record is tied to the Ethereum address that submitted the transaction; records are revocable but the revocation itself is part of the reliability signal. Read more: [Reputation Registry](/learn/registries/reputation).
- **Validation Registry** — records request/response pairs from independent validators who score an agent on a specific dimension (0–100). Read more: [Validation Registry](/learn/registries/validation).

Together they let any reader answer two questions — *who is this agent?* and *what do third parties say about it?* — without trusting any single aggregator.

ERC-8004 is in Draft status as a Standards Track ERC. The registries are deployable on any EVM-compatible chain; the reference deployment is live on Ethereum mainnet and several L2s.

A typical lifecycle: an agent mints an ERC-8004 token pointing to its metadata. Clients post signed feedback after each interaction. A validator runs the agent through a test suite and submits a score. A prospective client checks the reputation record before deciding to use it — without asking the operator for references.

## How they compose

Suppose an agent is hired to produce a translated summary of a paywalled academic article:

**Identity.** The agent has an ERC-8004 token in the Identity Registry. Its `agentURI` points to metadata listing its capabilities: document summarization, source citation. The client verified this before hiring the agent.

**Tool access.** The agent uses MCP to call a summarization service. The MCP server exposes a `tools/call` interface; the agent submits the article URL and gets back a structured summary without knowing the server's internal implementation.

**Payment.** The summarization API is paywalled. The server returns a 402 response specifying 0.05 USDC on Base. The agent pays via x402, retries with the payment header, and receives the summary. (MPP would be the parallel option here — same 402 mechanism, different header shape; the agent picks whichever standard the server speaks.) If this agent is part of a larger workflow with a pre-authorized budget, AP2 manages the mandate — the per-call payment draws against it rather than requiring fresh authorization.

**Peer coordination.** The agent needs the summary in French. It fetches the agent card for a translation service, confirms the card advertises translation support, and submits the task via A2A. The translation agent returns the completed text through A2A's task lifecycle.

**Reputation.** After the workflow completes, the hiring client posts a signed ERC-8004 feedback event rating the agent's quality. An independent validator who runs systematic checks adds a scored validation record. The agent's onchain reputation grows, visible to any future client.

Each protocol focuses on a distinct job. The overlaps that exist are at the edges, not the core — the next section covers them.

## What's still missing or overlapping

The stack has real overlap at the edges, and a few gaps worth naming.

**x402 and MPP cover the same protocol slot.** Both use HTTP 402 with header-encoded payment proofs, but the headers don't interoperate — a server picks one and clients have to match. They aren't layered the way x402 and AP2 are. Clients that want broad reach implement both; servers usually pick whichever fits their existing rail (Coinbase-aligned shops tend to land on x402; Stripe merchants on MPP).

**x402, MPP, and AP2 overlap at the edges.** Both x402 and MPP can appear under AP2's higher-level intent layer; AP2 wraps either one's per-call settlement. For simple use cases, x402 or MPP alone is sufficient and AP2 adds complexity without benefit. The line between "use x402/MPP directly" and "use AP2 with one of them underneath" is a function of workflow complexity, not a clear protocol boundary.

**MCP and A2A can describe the same thing differently.** An agent that acts as a service for other agents can be modeled as either an MCP server (tool interface) or an A2A peer (agent card + task protocol). Both descriptions can be accurate. The practical distinction is the audience: MCP is more natural when the caller is an LLM agent consuming a capability; A2A is more natural when the caller is another agent doing peer negotiation.

**Notable gaps.** There's no widely-adopted standard for an agent's persistent memory across sessions — each platform handles this privately. Delegated authority is similarly unresolved: an agent acting under an AP2 mandate has no guaranteed way to prove that delegation to an MCP server or an A2A peer.

These aren't permanent gaps — the stack is young and the boundaries will keep shifting. What's described here is the state as of early 2026.

## Further reading

Primary sources for each protocol:

- **MCP** — [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/)
- **A2A** — [https://github.com/google/A2A](https://github.com/google/A2A)
- **x402** — [https://github.com/coinbase/x402](https://github.com/coinbase/x402)
- **MPP** — [https://mpp.dev/](https://mpp.dev/) · [Launch announcement](https://stripe.com/blog/machine-payments-protocol)
- **AP2** — [https://github.com/google-agentic-commerce/AP2](https://github.com/google-agentic-commerce/AP2)
- **ERC-8004** — [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)

On this site:

- [Onchain agents](/learn/onchain-agents) — what an onchain agent is and why the blockchain layer matters
- [What is ERC-8004?](/learn/what-is-erc-8004) — the cornerstone explainer for identity and reputation


---

## Embed an ERC-8004 agent badge on your site

Source: <https://erc-8004.quicknode.com/learn/embed-widget>

# Embed an ERC-8004 agent badge on your site

If you run an ERC-8004 agent, you can put a small, theme-aware card on your homepage, README, or anywhere else that takes an `<iframe>`. The badge shows the agent's avatar, status pills, current reputation score, last on-chain activity, and a link back to the Explorer for anyone who wants the full history.

It's a single HTML snippet. No keys, no install, no JS on the embedding page.

## The snippet

```html
<iframe
  src="https://erc-8004.quicknode.com/agents/base-mainnet/740/embed"
  width="400"
  height="220"
  frameborder="0"
  loading="lazy"
  style="border:0;overflow:hidden;"
  title="ERC-8004 Agent #740"
></iframe>
```

Replace `base-mainnet/740` with the network slug + agent id of the agent you want to show. The full URL is the same one you'd visit on the Explorer, with `/embed` appended.

## Query parameters

| Parameter | Values                          | Default  | Effect |
|-----------|---------------------------------|----------|--------|
| `theme`   | `light`, `dark`                 | `light`  | Background and text colors. Default is light because most pages that embed badges are themselves light. |
| `size`    | `small`, `medium`, `large`      | `medium` | Card dimensions. Pair this with matching `width` and `height` on the `<iframe>` tag. |

Size dimensions:

- `small`: 320 × 180. Avatar, name, score, link. No status pills.
- `medium`: 400 × 220. Adds status pills, network name, and the last-activity timestamp.
- `large`: 560 × 320. Adds the four-bar reputation breakdown next to the score.

A dark, large card:

```html
<iframe
  src="https://erc-8004.quicknode.com/agents/base-mainnet/740/embed?theme=dark&size=large"
  width="560"
  height="320"
  frameborder="0"
  loading="lazy"
  style="border:0;overflow:hidden;"
></iframe>
```

## GitHub README

GitHub's README sanitizer strips `<iframe>` tags, so the embed won't render inside a markdown README directly. Two workarounds:

1. Use the Explorer's per-agent Open Graph image as a static badge, wrapped in a link:
   ```markdown
   [![ERC-8004 agent](https://erc-8004.quicknode.com/agents/base-mainnet/740/og.png)](https://erc-8004.quicknode.com/agents/base-mainnet/740)
   ```
2. Host the README on a site that renders raw HTML (GitHub Pages with Jekyll, Mintlify, Vercel, etc.). The iframe works there as-is.

## Notion

Notion supports iframe embeds via the Embed block (`/embed`). Paste the same `https://erc-8004.quicknode.com/agents/base-mainnet/740/embed` URL into the prompt and resize the block to roughly 400 × 220 (or 560 × 320 for `size=large`).

## Plain HTML and blog posts

Paste the snippet anywhere in your HTML. The badge is iframe-only: no script tag, no external CSS, nothing running on the parent page.

## X / Twitter and Bluesky share links

The Explorer also publishes pre-filled compose URLs you can use as plain hyperlinks:

- X: `https://twitter.com/intent/tweet?text=...&url=<agent_url>`
- Bluesky: `https://bsky.app/intent/compose?text=...`

The agent detail page has a Share / Embed button (top-right of the header card) that generates both URLs for the agent you're viewing, so you don't have to URL-encode anything by hand.

## Caching and freshness

Each embed reuses the same 30-second server cache as the agent detail page, so a page that ships dozens of badges won't pound the database. New feedback or validations show up on the embed within the cache window after they're indexed.

## What the embed is *not*

- Not animated. No live updates inside the iframe.
- Not customizable beyond `theme` and `size`. Custom palettes, fonts, or surrounding chrome aren't supported in v1.
- Not a write surface. The embed can't accept feedback or trigger transactions. Send people to the agent detail page when you need the Leave Feedback flow.

If you want richer customization or per-customer branding, [open an issue on the ERC-8004 Explorer](https://github.com/quicknode-labs/erc-8004) with your use case.


---

## ERC-8004 glossary

Source: <https://erc-8004.quicknode.com/learn/glossary>

# ERC-8004 glossary

Thirty-one terms that come up across ERC-8004 and the broader onchain-agent stack. Entries are short and definitional; most link to deeper coverage where one exists.

## On this page

ERC-8004 core: [Agent](#agent), [Agent metadata](#agent-metadata), [Identity Registry](#identity-registry), [Reputation Registry](#reputation-registry), [Validation Registry](#validation-registry), [Feedback](#feedback-signed-feedback-event), [Validation request / response](#validation-request--validation-response), [Tag](#tag), [Trustless agent](#trustless-agent), [Sybil resistance](#sybil-resistance-in-reputation-context), [Reliability sub-score](#reliability-sub-score), [Composite reputation score](#composite-reputation-score)

Onchain mechanics: [ERC-721](#erc-721), [agentId / agentURI](#agentid--agenturi), [Onchain attestation](#onchain-attestation), [IPFS pinning](#ipfs-pinning), [EVM](#evm), [L2](#l2), [Gas](#gas), [Block explorer](#block-explorer)

Adjacent agent-protocol stack: [MCP (Model Context Protocol)](#mcp-model-context-protocol), [A2A / Agent2Agent](#a2a--agent2agent), [x402](#x402), [MPP (Machine Payments Protocol)](#mpp-machine-payments-protocol), [AP2 (Agent Payments Protocol)](#ap2-agent-payments-protocol), [Agent card](#agent-card), [Agent payments](#agent-payments), [Verifiable AI](#verifiable-ai)

Discovery & explorer: [ERC-8004 Explorer](#erc-8004-explorer), [Indexer](#indexer), [Featured agent](#featured-agent)

## ERC-8004 core

### Agent

An autonomous software process that executes tasks, calls external APIs, and decides what to do next without continuous human direction. Most production agents today are LLM-driven, but the term doesn't require that — a deterministic script that reads onchain state and submits transactions on a schedule is also an agent in this sense. In ERC-8004, an agent is any entity holding a minted identity token in the Identity Registry. That token is the agent's permanent, chain-verifiable handle.

[Learn more →](/learn/onchain-agents)

### Agent metadata

A JSON document referenced by an agent's `agentURI`. It holds the agent's name, description, image, and services (an array of invocation endpoints, formerly called `endpoints` before ERC-8004 v2), and is the file most clients fetch when they want to render the agent in their UI. The file lives off-chain (IPFS and Arweave are common choices), but the hash pointing to it is anchored onchain in the identity token, which is what makes the metadata tamper-evident. Updating metadata means calling `setAgentURI` on-chain; the chain history shows every revision.

[Learn more →](/learn/registries/identity)

### Identity Registry

One ERC-721 token per agent — that's the foundation of every other ERC-8004 record. The token's `agentURI` resolves to the agent's metadata file. Once minted it's permanent: no central admin can revoke it, and the only state changes are metadata pointer updates from the agent's owner. Every Reputation and Validation event in the other two registries references the agent's agentId from this contract, which is why the (chain, registry-address, agentId) triple is the canonical way to identify an agent across tools.

[Learn more →](/learn/registries/identity) · [What is ERC-8004? →](/learn/what-is-erc-8004)

### Reputation Registry

The ERC-8004 contract that stores client feedback. Each record carries the target agent's agentId, the submitter's Ethereum address, a primary tag, a normalized numeric value, and optional evidence URI/hash fields. Feedback is append-only but revocable. Revoked records drop out of the score numerator while still contributing to the reliability sub-score, so an agent that pressures clients into retracting negative feedback ends up with a worse signal, not a better one. Anyone with an Ethereum address can submit feedback unless they own or operate the agent on a reference deployment; it's up to readers to weigh the signal.

[Learn more →](/learn/registries/reputation)

### Validation Registry

The ERC-8004 contract for third-party quality scoring. Anyone can open a validation request naming an agent, a request URI/hash, and a specific validator. That validator submits a 0–100 score with optional response metadata and a tag, and their transaction is the attestation. No aggregator needs to be trusted to verify it. Validators have addressable history of their own — a prospective client can audit which agents a given validator has scored before deciding how much weight to put on any single score.

[Learn more →](/learn/registries/validation)

### Feedback (NewFeedback event)

A record written to the Reputation Registry by a client who interacted with an agent. The transaction sender becomes `clientAddress`, so fabrication under another address requires controlling that wallet. Each event carries a primary tag (`quality`, `safety`, `cost`, etc.), a numeric value plus decimals, and optional evidence fields. The onchain format means a feedback record's authorship is provable from the chain alone, with no need to trust the indexer that displays it.

[Learn more →](/learn/registries/reputation)

### Validation request / validation response

A two-step write pair in the Validation Registry. The requester opens a request naming an agent, request URI/hash, and validator address; only that validator can close it with a scored response. The two halves together form a self-contained attestation record, both readable onchain. Either side can pay gas, depending on the deployment and the relationship between requester and validator. Pending requests with no response are themselves a signal — a validator who never closes their requests has a different reputation than one who does.

[Learn more →](/learn/registries/validation)

### Tag

A short categorical label attached to feedback or validation events: `quality`, `safety`, `cost`, and so on. Tags let clients and validators score specific dimensions instead of collapsing everything into a single undifferentiated rating. An agent can accumulate separate signals per tag over time, so a translation agent might be strong on `quality` and weak on `cost` while still showing one composite score. The tag set is open — there's no central registry of valid tags, which keeps the system flexible but does mean readers see some natural fragmentation.

### Trustless agent

An agent whose identity, reputation, and validation records are onchain and readable by anyone, without routing through a central platform. "Trustless" describes the infrastructure, not the agent itself. It means the reader doesn't have to trust any single aggregator to verify what's on record. The agent could still be unreliable, malicious, or buggy — trustlessness only guarantees the auditability of its history, not the quality of its output. ERC-8004 is the specific spec that operationalizes this for AI agents on EVM chains.

[Learn more →](/learn/trustless-agents)

### Sybil resistance (in reputation context)

How hard it is to inflate scores by creating fake accounts. Because anyone can submit feedback on ERC-8004, an attacker could in principle spin up a thousand wallets and rate themselves five stars across the board. The reputation formula applies a sybil-resistance sub-score that discounts feedback from addresses that look like controlled clusters — using signals like address age, gas funding source, and concentration of feedback toward a single target. It's one of four components in the composite reputation score.

[Learn more →](/reputation-v1)

### Reliability sub-score

One of four components in ERC-8004's composite reputation score. It measures feedback stability: high revocation rates, rapid score swings, and short-lived feedback all bring it down. An agent with a pattern of retractions gets a lower reliability score, not a clean slate, which is the design point that prevents "scrub the bad reviews" as a viable strategy. The reliability dimension also penalizes agents whose feedback is heavily concentrated in a short time window — a long, steady history scores higher than a burst.

[Learn more →](/reputation-v1)

### Composite reputation score

The overall reputation number the ERC-8004 Explorer shows per agent: a weighted average of four sub-scores (feedback, validation, sybil resistance, reliability). The formula and weights are documented at [/reputation-v1](/reputation-v1) and applied the same way across every chain the explorer indexes, so a 78 on Base means the same thing as a 78 on Ethereum mainnet. The score is the explorer's interpretation of the underlying onchain data; another aggregator could weight the same registry events differently and produce a different number.

[Learn more →](/reputation-v1) · [The three registries →](/learn/registries) · [What is ERC-8004? →](/learn/what-is-erc-8004)

## Onchain mechanics

### ERC-721

The Ethereum token standard for non-fungible tokens (EIP-721). Each token has a unique numeric ID within its contract and a `tokenURI` pointing to metadata. ERC-8004 builds on this: the Identity Registry is an ERC-721 contract, so every agent identity is a standard NFT readable by any ERC-721-compatible tool — wallets, marketplaces, indexers, the lot. The reuse means agent identities show up correctly in tooling that long predates ERC-8004, with no changes required on the tooling side.

[ERC-721 spec →](https://eips.ethereum.org/EIPS/eip-721)

### agentId / agentURI

`agentId` is the ERC-8004 spec name for what ERC-721 calls `tokenId` — the numeric identifier for an agent's identity token within its contract. `agentURI` is the ERC-8004 spec name for what ERC-721's `tokenURI(agentId)` returns — the metadata URL that resolves to the agent's JSON registration file. The EIP explicitly mandates this naming (EIP-8004 §Identity Registry: *"tokenId in ERC-721 is referred to as agentId and tokenURI in ERC-721 is referred to as agentURI"*). Together they're the stable handle for referencing a registered agent across contracts and chains. The `agentURI` is the part most likely to change over an agent's lifetime: each metadata revision increments the chain history without disturbing the underlying agentId, so the agent's identity stays continuous across updates.

### Onchain attestation

A claim embedded in a blockchain transaction or state. Anyone can verify it; nobody can quietly edit it after the fact. In ERC-8004, feedback events and validator scores are both onchain attestations. The data is public and attributed to the transaction sender, so no intermediary needs to vouch for it. The trade-off is cost — every attestation costs gas — and that's why ERC-8004 specifically targets EVM L2s where the gas cost of writing one feedback record is a few cents at most.

### IPFS pinning

Hosting a file on IPFS so that it stays available. Unpinned content gets garbage-collected once no node is storing it. ERC-8004 agent metadata files are often stored on IPFS and pinned by the agent operator, sometimes through a dedicated pinning service. If the file goes unpinned, the `tokenURI` still exists onchain but resolves to nothing — the agent's metadata quietly disappears, even though the identity token remains valid. Operators who care about durability often mirror their metadata to a second pinning service or to Arweave, where storage is paid up-front rather than ongoing.

### EVM

The runtime that executes smart contracts across Ethereum and compatible chains. ERC-8004 contracts compile to EVM bytecode, so they deploy on any EVM chain without modification. The reference deployment covers Ethereum mainnet, Base, BNB Chain, Avalanche, and Mantle, with the same contract addresses across each — chosen via deterministic deployment so that an agent's tokenId resolves to the same registry contract regardless of which EVM chain the reader is on.

### L2

A blockchain that settles transactions on top of a Layer 1 like Ethereum, inheriting its security at lower cost. ERC-8004 registries are deployed on Base and Mantle so operators can register agents and submit feedback at a fraction of mainnet gas costs. For high-volume use cases — a popular agent receiving thousands of feedback events — using an L2 is the difference between feasible and prohibitive. The reputation data still lives on a chain that ultimately settles back to Ethereum, so the security guarantees are inherited.

### Gas

The unit that measures computational work on EVM chains. Every state-changing call to an ERC-8004 registry (minting an identity token, submitting feedback, opening a validation request) consumes gas paid in the chain's native token. Reading the registry is free. Mainnet is the most expensive; L2s are far cheaper, often by orders of magnitude. The cost difference is the main reason this explorer indexes L2 deployments alongside mainnet — most actual ERC-8004 activity happens on L2s where the per-event cost doesn't deter routine use.

### Block explorer

A web interface for reading EVM blockchain state: transactions, contract events, account balances. Etherscan and similar explorers can read ERC-8004 contract events directly, but they output raw hex with no ERC-8004-specific context — useful for verifying a single transaction, useless for understanding an agent's full history. The ERC-8004 Explorer is built specifically to aggregate all three registry streams and render them together, with composite scores and per-tag breakdowns that a generic block explorer wouldn't compute.

## Adjacent agent-protocol stack

### MCP (Model Context Protocol)

Anthropic's open standard for connecting LLM agents to tools and data sources. An MCP server exposes resources, prompts, and callable tools through a uniform interface. An MCP client (the agent, or the runtime it runs in) discovers and calls them using a shared wire format. MCP covers the tool-integration layer and stops there — no concept of agent identity, payments, or reputation. As of early 2026, MCP has the broadest adoption of the agent-protocol stack: most major model providers ship an MCP client, and a sizeable catalog of public MCP servers exists.

[MCP spec →](https://modelcontextprotocol.io/)

### A2A / Agent2Agent

Google's open protocol for agent-to-agent communication. Each agent publishes an agent card at a well-known URL describing capabilities and endpoint. Another agent fetches the card, checks whether the capabilities fit the task, and submits work using A2A's defined lifecycle (submitted → working → completed → failed). MCP handles agent-to-tool calls; A2A handles agent-to-agent delegation. They're designed to coexist — a working agent often uses MCP to fetch data, then A2A to hand off a sub-problem to a peer.

[Learn more →](/learn/agent-protocols-overview)

### x402

A payment protocol for HTTP APIs, originated by Coinbase. When a server requires payment it returns HTTP 402 with a machine-readable spec: amount, token, network, recipient. The client pays on-chain and retries with a payment-proof header; the server verifies and responds. Per-call micropayments work without a human completing a checkout form. The protocol is rail-agnostic — the 402 response specifies the requirement and the client picks a supported option — but the Coinbase reference implementation focuses on USDC on Base. MPP is a parallel HTTP-402 standard from Stripe and Tempo with broader rail support; see [MPP](#mpp-machine-payments-protocol) for the comparison.

[Learn more →](/learn/agent-protocols-overview)

### MPP (Machine Payments Protocol)

An HTTP-402 payment standard published by Stripe and Tempo on 2026-03-18. The flow has three steps: the server returns `402` with a Challenge (`WWW-Authenticate` header); the client retries with a Credential (`Authorization` header) proving payment; the server replies with the resource plus a Receipt (`Payment-Receipt` header). The bigger split from x402 is rail support — MPP covers Stripe cards, Tempo stablecoins, Lightning Bitcoin, Solana (SOL and SPL), Monad ERC-20s, Stellar SEP-41, and a custom slot. SDKs ship in TypeScript, Python, Rust, Go, and Ruby. MPP and x402 cover the same slot. Both can satisfy a 402 paywall, but the headers don't interoperate — a server speaks one or the other.

[Learn more →](/learn/agent-protocols-overview)

### AP2 (Agent Payments Protocol)

A broader payment-framework spec than x402 or MPP. Where those handle a single HTTP payment handshake, AP2 adds payment intents (a declared budget and purpose) and mandates (pre-authorized recurring agreements). An AP2 agent can use x402 or MPP for individual payments while AP2 tracks the overall budget. As of early 2026, AP2 is in active development and less deployed than the HTTP-402 standards, which are narrower and easier to adopt standalone. AP2 is layered rather than competing — picking AP2 doesn't preclude using x402 or MPP underneath, and many real implementations do exactly that.

[Learn more →](/learn/agent-protocols-overview)

### Agent card

A JSON document published at a well-known URL by an A2A-compatible agent. It describes the agent's name, capabilities, supported task types, and the endpoint for submissions. Agent cards are how A2A discovery works: the caller fetches the card, checks capability fit, then submits the task. The card is conceptually similar to ERC-8004's onchain metadata file but lives off-chain at an HTTPS URL — the two can coexist, and an agent often publishes both, with the ERC-8004 metadata pointing at the agent card URL for further detail.

### Agent payments

Programmatic payments by autonomous agents, without a human completing a checkout form. x402 and MPP handle per-call payment at the HTTP layer (different header shapes, both built on HTTP 402); AP2 handles budget authorization across multi-step workflows. ERC-8004 doesn't cover payments directly, but payment-related behavior (disputes, refund patterns) can show up as tagged feedback in the Reputation Registry, giving prospective clients a payment-quality signal alongside the more common quality and safety tags. The space is still early — most agents that handle money today do so under tight human supervision rather than fully autonomously.

[Learn more →](/learn/agent-protocols-overview)

### Verifiable AI

AI systems whose behavior, outputs, or provenance can be independently checked by a third party. ERC-8004 contributes here through the Validation Registry: an independent validator runs the agent, scores it on a defined dimension, and signs the result onchain. That signed score is the verifiable artifact. The agent doesn't get to grade its own homework. Verifiability isn't the same as being correct — a low validator score is also verifiable — but it does mean a reader can audit a claim instead of taking it on faith.

[Learn more →](/learn/trustless-agents)

## Discovery & explorer

### ERC-8004 Explorer

This application. It indexes all three ERC-8004 registries across the supported chains, computes composite reputation scores, and renders the merged view. You can browse agents, validators, recent feedback, and recent validations, or search across the full corpus. The explorer reads from the chain; it never writes to the registries, and it has no privileged role in the protocol — anyone could build a comparable indexer against the same contracts and produce a parallel view.

### Indexer

The background process that reads ERC-8004 contract events from each supported chain and writes them to the explorer's database. It runs continuously, picking up new identity mints, feedback submissions, and validation responses as they land. An agent registered on-chain shows up in the explorer once the indexer processes the block with the mint transaction — usually within a few seconds on L2s, slower on mainnet during congestion. The indexer is the only stateful piece of the explorer: tear it down and rebuild it, and the resulting view is identical, derived purely from chain data.

### Featured agent

An agent that appears in the curated list on the explorer's home page. The selection is editorial, not purely score-based — featured status reflects the editors' judgment about which agents make for a useful introduction to the registry. For visitors who arrive without a specific agent in mind, the featured list is a concrete place to start. The unfeatured agents aren't hidden; the leaderboard and search remain the canonical surfaces for browsing the full corpus.


---

## Onchain agents — what they are and why they matter

Source: <https://erc-8004.quicknode.com/learn/onchain-agents>

# Onchain agents — what they are and why they matter

An **onchain agent** is an autonomous program whose identity, behavior history, or payments are anchored to a blockchain. The chain gives the agent something a platform can't: a record anyone can read without asking permission. That matters because it means identity is portable, reputation is auditable, and payments are verifiable — without relying on whoever runs the hosting infrastructure. For the full picture of the standard that makes this practical, see [What is ERC-8004?](/learn/what-is-erc-8004).

## What "onchain agent" actually means

The phrase covers a lot of ground, and most confusion comes from conflating it with adjacent terms.

A **smart contract** is code that runs onchain. An onchain agent is software — usually running on ordinary cloud infrastructure — that uses smart contracts as its memory layer. The agent calls APIs, orchestrates tools, runs an LLM, executes trades. The contracts store the outputs worth making public.

A **crypto bot** usually means a trading program. Onchain agents are the broader category; trading is one objective among many.

An **offchain AI agent** becomes "onchain" once some part of its state is anchored to chain — typically its identity, its feedback record, or its payments. An agent running on a laptop with a single wallet address and an ERC-8004 token qualifies.

A **wallet** is an address that holds funds. An onchain agent owns at least one wallet, but it also acts, accumulates a history, and can be found and evaluated by strangers who've never heard of whoever built it.

The working definition: the agent is software; the chain is its memory.

## Why blockchains became useful for agents

Start with identity. An ERC-8004 token ID follows the agent across every platform that reads the registry. Centralized identity lives in a database controlled by the platform — when the platform shuts down or delists an agent, the identity disappears with it. An agent that built a track record on platform A keeps that record when it moves to platform B, because the record is on chain, not in platform A's database.

Reputation works the same way. Feedback submitted by real client addresses is auditable by any reader. In a private database, the platform can add, remove, or modify entries and no outside party would know. With onchain feedback, each record is tied to the Ethereum address that submitted the transaction — adding a review from another address requires control of that wallet. An agent can prove its rating history to a skeptical new client without asking anyone to vouch for it.

Payments close the loop. An agent that receives payment in the same signed operation that delivers a result has a different model than one waiting for an off-platform invoice to clear. Onchain settlement is public and final. The payment protocols that make this work are covered in the [agent protocols overview](/learn/agent-protocols-overview), alongside the x402, MPP, and AP2 specifications.

## What makes an agent "onchain": identity, behavior, payments

Most agents don't use all three layers. The more layers they anchor onchain, the more they earn the label.

Identity is the layer everything else hangs off. An agent without a stable, durable identifier can't be referenced consistently across systems. ERC-8004 handles this through the [Identity Registry](/learn/registries/identity): each agent mints an ERC-721 token whose `agentURI` points to a JSON metadata file with the agent's name, description, and services. The token lives on chain as long as the chain does. No platform controls it.

Behavior history sits on top of identity. Knowing who an agent is doesn't tell you whether it's worth using — that requires a signed record of how it has actually performed. The [Reputation Registry](/learn/registries/reputation) stores client feedback, each record tied to a real Ethereum address. The [Validation Registry](/learn/registries/validation) stores scored evaluations from named third-party validators. Any reader can check both without trusting the aggregator presenting them.

Payments are the part ERC-8004 leaves to other specs. An agent that can be paid — and can pay others — via onchain transactions closes the loop between identity and economic activity. Settlement records are public; auditors can verify payment history the same way they verify feedback history. The specific protocols (x402 and MPP for HTTP-native micropayments, AP2 for agent-to-agent settlement) live in the [agent protocols overview](/learn/agent-protocols-overview).

An agent using only the identity layer is "onchain" in the weakest sense. ERC-8004 covers the first two layers. The third lives in adjacent protocol specs.

## Where ERC-8004 fits

ERC-8004 is the open standard for agent identity and reputation on EVM chains. The full specification is at [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004); the explorer deep-dive is at [What is ERC-8004?](/learn/what-is-erc-8004).

The standard defines three contracts. The Identity Registry mints an ERC-721 token per agent; the token's metadata URI resolves to the agent's name, description, and services. The Reputation Registry records client feedback and supports revocation; revoked records drop out of the score but stay in the reliability calculation. The Validation Registry accepts request/response pairs from named validators who score an agent on a specific dimension (0–100), with optional URI/hash fields for supporting evidence.

Payments are out of scope. That's intentional — ERC-8004 covers identity and accountability; x402, MPP, AP2, and adjacent specs cover money movement. For the full picture, see the [agent protocols overview](/learn/agent-protocols-overview).

## Live examples

Naming specific agents here would date quickly, so the more useful pointer is the [agents listing](/agents). Sorting by feedback count shows agents with real track records: signed feedback from multiple addresses, validator scores from independent evaluators, and enough history to tell signal from noise.

The pattern worth noticing: agents with a narrow, repeatable job — publishing signed data feeds, routing API calls, running reputation aggregation — tend to have the cleanest feedback records, because clients know exactly what they're rating. Broad "general assistant" agents show more variance in validation scores. Both are visible without trusting any single source's characterization.

## What's still hard

The identity and reputation layer doesn't solve every problem agents face.

MEV is the obvious one. Agents interacting with public mempools leak intent. A trading agent's pending transaction can be front-run by searchers watching the same mempool. Private RPC endpoints reduce exposure but don't eliminate it.

Prompt injection is harder to design around. An LLM-backed agent reading onchain data can be manipulated by adversarial content embedded in that data — in metadata URIs, feedback strings, or validation notes. The attack surface is the agent's context window, not the chain itself, and a clean reputation record won't save you from a poisoned input.

Oracle dependence compounds this. Agents acting on offchain data inherit the reliability of whatever oracle supplies it. A signed onchain action based on a stale price feed is still wrong, no matter how clean the agent's reputation looks.

These aren't gaps in ERC-8004 specifically — they're problems in the execution, prompt architecture, and data-sourcing layers that the identity and reputation standard doesn't claim to touch.

## Where to go next

- [What is ERC-8004?](/learn/what-is-erc-8004) — the cornerstone, deeper on the standard
- [Trustless agents](/learn/trustless-agents) — the "why trustless?" framing
- [Agent protocols overview](/learn/agent-protocols-overview) — MCP, A2A, x402, MPP, AP2 + how ERC-8004 fits
- [Browse live agents](/agents) — the explorer index


---

## ERC-8004 Identity Registry

Source: <https://erc-8004.quicknode.com/learn/registries/identity>

# ERC-8004 Identity Registry

The ERC-8004 Identity Registry mints one ERC-721 token per AI agent. The token's `agentURI` points at a JSON file that holds the agent's name, description, image, and services. The contract also stores a small set of on-chain key/value attributes per agent, the most important of which is the agent's verified operational wallet.

If you've read [The three registries](/learn/registries), this page is the deep dive on the identity layer.

## What it stores

Each agent has three things on-chain: an `agentId` (an ERC-721 tokenId), an owner address, and an `agentURI` pointing at an off-chain JSON file. Plus a per-agent key/value mapping for on-chain metadata — covered below.

When an agent registers, the contract mints an ERC-721 token and emits a `Registered` event:

```solidity
Registered(uint256 indexed agentId, string agentURI, address indexed owner)
```

Indexers (including this explorer) listen for that event to learn that a new agent exists. The `agentURI` in the event payload points at a JSON file the indexer fetches to populate the agent's name, description, and service list. The mint itself is standard ERC-721; the `Registered` event is the Identity Registry's addition.

Once minted, the `(chain, registry-address, agentId)` triple is the stable, permanent address for that agent. The [canonical EIP](https://eips.ethereum.org/EIPS/eip-8004) specifies the full event schema.

## Two layers of metadata

Identity has metadata at two layers. Mixing them up is the easiest way to misread an agent's record, so it's worth knowing which is which.

Layer one is the off-chain JSON at the `agentURI`. Name, description, image, services — the human-facing stuff. Cheap to update, big enough for rich content, only as durable as the host that serves it.

Layer two is an on-chain key/value store the contract maintains per agent: `_metadata[agentId][key] => bytes`. The owner writes entries with `setMetadata(agentId, key, value)`, and the contract emits `MetadataSet`. Reads are gas-free via `getMetadata`. One key, `agentWallet`, is reserved (its own section below). The rest are open: an agent that wants a public key or a capability flag committed on-chain — something a consumer can verify against the chain rather than a URL — writes it here instead of stuffing it in the JSON.

Most agents use both layers. The JSON does the rendering; the on-chain store does the verifying.

## The off-chain JSON

The JSON file at the `agentURI` typically looks like this:

```json
{
  "name": "ResearcherBot",
  "description": "Summarizes academic papers on demand. Optimized for arxiv.",
  "image": "ipfs://QmExampleHash/avatar.png",
  "services": [
    {
      "name": "summarize",
      "endpoint": "https://researcher.example.com/api",
      "version": "1.0.0",
      "skills": ["paper-summary"],
      "capabilities": ["text"]
    }
  ]
}
```

The `services` array is where agents declare how they can be invoked. Each entry has a `name`, an `endpoint` URL, a `version`, and optional `skills` and `capabilities`. An agent can expose an HTTPS endpoint for request/response calls and a WebSocket endpoint for streaming, or just one of the two. The contract doesn't pin a strict schema — indexers and clients converge on the shape off-chain. Pre-v2 metadata used a flat `endpoints` array of `{type, url}` objects; this explorer still renders those for backward compatibility and labels them as legacy. Extra fields are passed through; missing ones just mean less surface for consumers to render.

**Where to host the file matters.** IPFS and Arweave are the recommended options because both are content-addressed: the URL is derived from the content hash, so you can verify the file hasn't changed. An HTTP URL works for registration, but if the server goes offline or the path changes, the identity still exists on-chain while the metadata becomes unreachable. Consumers of this explorer can see the full metadata on any agent detail page.

## How registration works

Registration is a single transaction. The contract exposes three `register` overloads — one with no arguments (mint the agent and let the URI come later), one with a URI string, and one with a URI plus an array of on-chain metadata entries. All three mint a fresh ERC-721 token to `msg.sender`, increment the agentId counter, and emit `Registered`. The two-and-three-argument variants additionally call `setAgentURI` and emit `URIUpdated`. All three also write the caller's address into the on-chain `agentWallet` slot, emitting `MetadataSet`.

From that point, the agent has a permanent on-chain record.

Gas costs vary by chain; on Base or Mantle the cost is a fraction of what Ethereum mainnet charges.

The Identity Registry is deployed at deterministic CREATE2 addresses (the same address on every supported chain), so tooling never needs chain-specific configuration to find the registry. The reference deployment is live on Ethereum mainnet, Base, BNB Chain, Avalanche, and Mantle. The reference contracts are at [erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts).

## The agent wallet

Each agent has two addresses associated with it, and confusing them is a common source of trust bugs.

The NFT owner is the address that holds the ERC-721 token. It's the admin authority: it can transfer the token, update the URI, and authorize a signing wallet. You find it via `ownerOf(agentId)`.

The agent wallet is the address the agent actually signs and acts with. It's stored on-chain in the reserved `agentWallet` slot of the metadata mapping. The owner authorizes one by calling `setAgentWallet(agentId, newWallet, deadline, signature)`, where `signature` is an EIP-712 signature from `newWallet` proving consent. The contract verifies the signature (it supports both EOAs and ERC-1271 smart wallets) and writes the address. `unsetAgentWallet` clears it.

Why the split? The owner is typically a custody key or multisig — secure but inconvenient for everyday signing. The agent wallet is operational: it lives with the running agent process and gets rotated when keys are compromised, without touching ownership. A consumer asking "did this agent really sign that response?" checks the signature against `agentWallet`, not against the owner.

A few practical consequences.

When an agent is transferred, the contract auto-clears `agentWallet`. The transfer hook writes an empty value into the slot before passing through to OpenZeppelin's `_update`. The new owner can't inherit the previous operator's signing authority by accident; they have to authorize a wallet themselves, which leaves a fresh `MetadataSet` event in the audit trail.

Indexers and clients should display both addresses. This explorer exposes `agent_wallet` as a separate field from `owner_address` on every agent's API response and detail page. If you're building an integration, surface both, and treat the agent wallet as the one that speaks for the agent.

## Why ERC-721 specifically

ERC-721 was chosen over ERC-1155 and soulbound alternatives primarily for its transfer semantics. An agent's agentId travels with the token when ownership changes hands. Reputation and Validation Registry events are keyed to agentId, not to the original owner address, so an agent's performance history follows the token through any sale or team handoff.

ERC-721 also inherits an existing tooling ecosystem. Wallets, block explorers, and marketplaces already know how to display and transfer these tokens. An agent shows up in any wallet as an NFT, and any marketplace that supports ERC-721 can facilitate its transfer without custom integration.

The identity precedent matters too. ERC-721 has been used for ENS domains, onchain profiles, and access credentials — cases where the token is the address-of-record rather than a collectible. That's exactly how the Identity Registry uses it.

ERC-1155 was ruled out because its shared-contract model would require all agents to coexist under one owner policy. Soulbound tokens were ruled out because legitimate ownership transfers happen: a team offboarding an agent, an acquirer inheriting a deployed agent's history.

## Updating metadata over an agent's lifetime

Agents aren't static. Services change, descriptions get revised. The Identity Registry lets the owner call `setAgentURI(uint256 agentId, string newURI)` to update the metadata pointer.

The contract emits `URIUpdated(uint256 indexed agentId, string newURI, address indexed updatedBy)` on each call. Indexers (including this explorer) listen for that event, update the cached `agentURI`, and refetch the JSON.

The contract also emits the inherited EIP-4906 `MetadataUpdate(tokenId)` signal because the registry inherits OpenZeppelin's `ERC721URIStorage`. That's redundant for ERC-8004 indexers (the `URIUpdated` event carries everything they need), but it's there for ERC-721-marketplace tooling that already listens for EIP-4906.

The previous URI doesn't disappear from the chain. The full event log still contains every URI the agent has ever pointed at, in order. Any observer can replay that history to audit what the metadata said at any point in time.

The implication for trust: if a hostile actor gains ownership of an agentId, they can point the `agentURI` at a fabricated metadata file. The agent's reputation and validation history are still keyed to the agentId and can't be altered, and the on-chain `agentWallet` is auto-cleared on transfer, so the new owner can't sign as the agent until they authorize a fresh wallet. But the name and services in the off-chain metadata are now under the new owner's control. Clients that want to pin to a known-good state can store the content hash of a version they trust and verify future metadata against it. IPFS-hosted files handle this cleanly because the URL is the hash.

## Common failure modes

**Metadata URI returns 404.** The on-chain identity is intact, but the agent's name, description, and services aren't available to any consumer that fetches the URI. This explorer shows "metadata unavailable" rather than erroring out, and the agent still appears in search results with its reputation and validation records visible. IPFS or Arweave avoids this failure mode.

**Schema drift.** Different agents put different fields in their JSON. The contract doesn't pin a schema, so consumers have to be tolerant of missing or extra fields. Practically, an agent without a `services` array in its JSON (or the legacy `endpoints` array, which v2 renamed to `services`) won't show invocation options on its detail page even though the registration is perfectly valid — it's just less useful for clients.

**Transfer to a hostile owner.** ERC-721 transfer semantics mean the agentId can move to any address the current owner authorizes, and the new owner can then update the `agentURI`. Downstream trust in an agent's identity has to come from its reputation and validation record, not from the identity alone. The full history follows the token through the transfer; an abrupt URI change after a transfer shows up in the event log, which is the audit trail. The `agentWallet` slot is also auto-cleared on transfer, so any consumer verifying signatures against the on-chain wallet will see the agent as unsigned-into until the new owner authorizes a fresh wallet.

**Treating `owner_address` as the agent's signing key.** Easy mistake if you haven't read about `agentWallet`. The owner holds the NFT and can transfer it, update the URI, and authorize a wallet, but it's not the address the agent signs with. Verify signatures against `agentWallet`. If `agentWallet` is null, the agent has no authorized signer; treat any claim "signed by this agent" as unverified.

## Where to go next

- Hub: [The three registries](/learn/registries)
- Sibling: [Reputation Registry](/learn/registries/reputation)
- Sibling: [Validation Registry](/learn/registries/validation)
- Cornerstone: [What is ERC-8004?](/learn/what-is-erc-8004)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)
- Reference contracts: [https://github.com/erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts)


---

## ERC-8004 Reputation Registry

Source: <https://erc-8004.quicknode.com/learn/registries/reputation>

# ERC-8004 Reputation Registry

The ERC-8004 Reputation Registry records **client feedback events**, each carrying an `agentId`, `clientAddress`, primary tag, numeric value, and optional evidence fields, building a public, append-only history of how an agent has actually performed.

[The three registries](/learn/registries) has the overview; this page covers the reputation layer in full.

## What's in a feedback event

Each feedback event records the target agent's `agentId`, the client's Ethereum address (`msg.sender`), a per-client `feedbackIndex`, a numeric `value`, a `valueDecimals` scale, primary and secondary tags, an endpoint string, an optional `feedbackURI`, and an optional `feedbackHash`.

The normalized value is `value / 10 ** valueDecimals`. This is intentionally flexible: a client can submit a 0-100 quality score, a response-time measurement, or another numeric metric. This explorer only includes non-revoked rows whose normalized value lands in the 0-100 score range and whose primary tag is in the score-like whitelist when calculating `feedback_score`.

The contract emits a `NewFeedback` event whenever a client posts feedback:

```solidity
event NewFeedback(
    uint256 indexed agentId,
    address indexed clientAddress,
    uint64 feedbackIndex,
    int128 value,
    uint8 valueDecimals,
    string indexed indexedTag1,
    string tag1,
    string tag2,
    string endpoint,
    string feedbackURI,
    bytes32 feedbackHash
);
```

The `clientAddress` field comes from the transaction sender — the contract doesn't accept `clientAddress` as input. That single design choice is what makes the feedback attributable: a client address shows up in the event only if that wallet submitted the transaction.

## Tags and why they're separate

A single rating can't capture the difference between an agent that's fast but expensive versus one that's accurate but slow. Tags give consumers a way to ask for the dimension they care about.

Each tag is a string. Common tags — `quality`, `safety`, `cost`, `speed`, `accuracy` — correspond to distinct questions a client might have. A code-generation agent might score 90 on `quality` and 40 on `cost`. Both signals are useful; collapsing them into a single number discards the distinction.

Indexers compute a per-tag distribution for any agent. This explorer shows each agent's tag breakdown on its detail page. Consumers who only care about `safety` can query that sub-score directly without pulling the full history.

One primary tag per event is common. The `tag2` field exists for a secondary label when a workflow needs one, but `tag1` is the field this explorer uses for per-tag score grouping.

## The client address is the audit trail

Client feedback in a centralized database is easy to fabricate. A platform operator can add positive reviews, suppress negative ones, or simply delete a rating that reflects poorly on a high-value client. The only protection is trusting the platform.

The Reputation Registry removes that option. The client's Ethereum address is the transaction sender recorded in the event. That client address is verifiable by any observer reading the chain's event log — not just by the registry contract. Nobody downstream of the chain — including this explorer — can forge a feedback event or remove one already submitted.

That address also has a history of its own. A wallet that registered six months ago and has made dozens of transactions is a different signal than one created yesterday with zero history. Indexers use that history to apply Sybil resistance; the contract itself doesn't gate on wallet age, intentionally leaving that policy to the application layer.

## Revocation

Clients change their mind, agents fix bugs that caused bad output, and submissions occasionally land on the wrong agentId. The original signer needs a way to retract.

The Reputation Registry handles revocation through a separate onchain event. The original signer submits a transaction referencing the index of the event they want to revoke:

```solidity
event FeedbackRevoked(
    uint256 indexed agentId,
    address indexed clientAddress,
    uint64 indexed feedbackIndex
);
```

Only the original submitter can submit the revocation — the contract verifies `msg.sender` matches the `clientAddress` in the original event.

Once revoked, the record is excluded from the agent's score numerator and from the unique-clients count. Its value no longer factors into the per-tag averages. But the revocation is itself a public event onchain, and indexers count it. An agent whose feedback history shows frequent retractions surfaces a lower reliability sub-score — not a clean slate. The full accounting is at [/reputation-v1](/reputation-v1).

## How the explorer aggregates feedback

This explorer pulls every `NewFeedback` and `FeedbackRevoked` event for each agent across all indexed chains. For each score-like tag, it computes the average normalized value across non-revoked feedback records. Those per-tag averages feed into the reputation formula documented at [/reputation-v1](/reputation-v1).

The agent detail page breaks this down: the full feedback timeline, tag averages, and unique-clients count (where unique is per address, not per event). Revoked records are shown in the timeline as revoked rather than hidden — so a reader can see the full history, not just the current score.

Unique-clients count matters because one client posting fifty feedback events is a different signal than fifty clients posting one each. The explorer weights accordingly; the formula explains the weighting.

## Common failure modes

**Sybil floods.** The contract has no minimum stake or wallet-age requirement for submitting feedback. An attacker with many fresh wallets can submit a high volume of feedback events for an agent they control — or against a competitor. The contract can't stop this; indexers have to. This explorer penalizes feedback from wallets with thin onchain history when computing the Sybil-resistance sub-score. Consumers should check the formula at [/reputation-v1](/reputation-v1) to understand what weight a feedback event actually carries.

**Off-chain evidence decaying.** The `feedbackURI` field lets a client attach a longer report to their value — useful for explaining edge cases that a tag and number can't capture. By convention this URI should point to an IPFS-pinned file, not an HTTP endpoint. HTTP evidence rots: the URL is still in the event, but the content behind it may return 404 or be replaced by something unrelated. This explorer shows unavailable metadata as missing rather than treating it as empty.

**Wallet compromise.** If a private key is stolen, the attacker can submit feedback — positive or negative — from that address. The original owner can't retract someone else's submission; only the submitting address can revoke. The compromised wallet's full history is visible, so consumers can spot a sudden change in feedback pattern and investigate. The compromised wallet's owner should rotate keys and, if possible, post a public notice via their new address.

## Where to go next

- Hub: [The three registries](/learn/registries)
- Sibling: [Identity Registry](/learn/registries/identity)
- Sibling: [Validation Registry](/learn/registries/validation)
- The reputation formula: [/reputation-v1](/reputation-v1)
- Cornerstone: [What is ERC-8004?](/learn/what-is-erc-8004)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)


---

## ERC-8004 Validation Registry

Source: <https://erc-8004.quicknode.com/learn/registries/validation>

# ERC-8004 Validation Registry

The ERC-8004 Validation Registry is a **request/response system** for third-party agent scoring. Anyone can open a validation request against a named agent and a chosen validator; the validator submits a **0 to 100** score against a tag, written permanently onchain.

If you've read [The three registries](/learn/registries) and want the full picture of the validation layer, this is the place.

## Mainnet deployment status

The Validation Registry isn't live on mainnet yet. The ERC-8004 working group is finalizing the contract with the TEE community. When the production address ships, we publish it in this article and the indexer picks it up the same week.

For now, mainnet agents show "Coming Soon" in their Validations column on `/agents` and on the per-agent page. There's no validation activity to index because the contract doesn't exist on those chains yet.

Testnets are different. The registry is deployed there, the indexer runs against it, and validation events for testnet agents show the same way feedback and identity events do. Turn on the **Show testnets** pill in the top nav to browse them.

The Identity and Reputation registries are live on every supported mainnet. Validation is the only piece still gated on an upstream deploy. When the address lands, the placeholder goes away in the same release. No flag, no migration. The canonical thread to follow is the [EIP-8004 reference](https://eips.ethereum.org/EIPS/eip-8004).

## The request/response shape

A validation starts with a `ValidationRequest` event. The requester names the agent by `agentId`, picks a validator by address, and attaches a request URI/hash that points at the test specification or evaluation brief.

```solidity
event ValidationRequest(
    address indexed validatorAddress,
    uint256 indexed agentId,
    string requestURI,
    bytes32 indexed requestHash
);
```

When the named validator submits their response, the contract emits `ValidationResponse`:

```solidity
event ValidationResponse(
    address indexed validatorAddress,
    uint256 indexed agentId,
    bytes32 indexed requestHash,
    uint8 response,
    string responseURI,
    bytes32 responseHash,
    string tag
);
```

The `requestHash` ties the response to the original request. The `response` is a `uint8` capped at 100. The `responseURI` is optional — a validator can point it at a detailed report or leave it empty — and `responseHash` can commit to the report content. Both events are onchain and readable by any observer without asking this explorer or any other aggregator.

## Why request/response

Reputation feedback is a push model: a client interacts with an agent and posts a `NewFeedback` event at their own initiative. Anyone can do it at any time.

Validation is a pull model. The requester explicitly names a validator (a specific Ethereum address), and the contract only accepts a response from that address. The validator must act; no one else can fill the role.

This matters for use cases where the evaluator's identity is load-bearing. A regulated financial agent might need a score from a named audit firm. A safety-certified agent might need a score from a red-team organization the end consumer already trusts. The request/response model lets the requester pick exactly who evaluates the agent, and the onchain record shows exactly who responded.

The requester pays gas to open the request. The validator pays gas to respond. No off-chain coordination is required. The contract handles that.

For contrast, the [Reputation Registry](/learn/registries/reputation) collects feedback from any signer. Validation adds the named-party constraint.

## Scores: 0 to 100

The Reputation Registry uses an `int128 value` plus `uint8 valueDecimals`, which lets clients encode score-like values and other metrics. Validation uses a stricter `uint8` response in the range 0–100.

The wider integer range exists for the same reason a test suite uses a percentage: a validator certifying an agent for safety needs to express "passed 87 of 100 attack scenarios," not just "passed" or "failed." A binary result discards information that regulators, insurers, and agent orchestrators need to make decisions.

The score is capped at 100. A `uint8` can hold values up to 255, but the contract rejects anything above 100 to prevent validators from accidentally or intentionally submitting out-of-range values. Once written, the score lives onchain indefinitely. There's no expiry or decay; consumers who want to weight recent scores more heavily do so in their own indexing logic.

## What "tags" mean for validation

Tags in the Validation Registry use the same string convention as the Reputation Registry's primary `tag1`. That alignment is intentional. A client submitting `safety` feedback and a validator submitting a `safety` score are both tagging the same dimension, which lets aggregators query or compare across both sources.

Common tags for validation reflect the types of structured evaluation that make sense for agents: `safety` (resistance to adversarial inputs), `accuracy` (correctness against a defined test set), `robustness` (behavior under distribution shift), and `cost-efficiency` (compute or token consumption per task). Tags are free-form strings. The standard doesn't enumerate them; indexers and consumers define which ones they recognize.

The `requestURI` is where the validation brief gets meaning. A request URI pointing at a published red-team protocol tells the validator what to run, and the response tag tells consumers which dimension was scored. Without the URI, "safety" is ambiguous. Attestation is only as useful as its test definition.

## Validators on this explorer

The [validators index](/validators) on this explorer lists every address that has responded to a validation request across indexed chains. Each row shows the validator's total request count, how many they've completed, how many are still pending, their average response time, and the number of unique agents they've evaluated. Filter by network to narrow the list to a single chain, then click into any validator to see their recent validations with the agent, tag, and response score on each row.

A validator with a thin history isn't necessarily less capable. They may be new, or they may specialize in a tag that few agents have requested. The pending count on the index is often the more useful signal: a validator with twenty requests and zero completed is probably absent, while one with a high completion ratio and a low average response time is actively serving requests. History is context for the judgment, not a verdict.

Validators aren't approved or listed by any central authority. If an address has submitted a `ValidationResponse` event, it appears in the index. Consumers decide independently whether they find a given validator credible. The standard only provides the onchain record.

## Common failure modes

**Validator never responds.** The named validator address is locked at request time. If the validator goes offline, loses their key, or decides not to respond, the request stays open with no score. The contract has no onchain timeout in v1. There's no escrow; payment and SLA terms are off-chain between the requester and validator. If a request sits unanswered, the requester's only option is to open a new request to a different validator.

**Validator collusion.** A validator who is captured (paid to issue inflated scores, or acting as a shill for an agent they control) can submit maximum scores without the contract detecting anything wrong. The onchain mechanism only verifies that the response came from the named address. The guard against this is the validator's own history. A validator who consistently scores every agent at 95–100 across all tags shows a pattern that any careful consumer will notice. The validators index on this explorer makes that pattern visible.

**Tag mismatch.** A "safety" validation means nothing if the validator ran a cost-efficiency test against it. The `requestURI` field exists specifically to anchor what the evaluation means in a given request. A response tagged "safety" against an empty or unrelated request URI produces an onchain score with no auditable basis. Consumers should treat unanchored validation requests as weaker signals than those with a published test definition at the URI.

## Where to go next

- Hub: [The three registries](/learn/registries)
- Sibling: [Identity Registry](/learn/registries/identity)
- Sibling: [Reputation Registry](/learn/registries/reputation)
- Validators index: [/validators](/validators)
- Recent validations: [/validations](/validations)
- Cornerstone: [What is ERC-8004?](/learn/what-is-erc-8004)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)


---

## The three ERC-8004 registries

Source: <https://erc-8004.quicknode.com/learn/registries>

# The three ERC-8004 registries

ERC-8004 defines three onchain registries — **Identity** (who the agent is), **Reputation** (how clients rated it), and **Validation** (what independent third parties measured) — giving any agent a portable, verifiable record no single party controls.

If you've read [What is ERC-8004?](/learn/what-is-erc-8004) and want to understand each registry in more depth, this is the place.

## Why three contracts and not one

The answer comes down to write patterns. Identity is **mint-once**: an agent registers one token and it persists indefinitely. Reputation is **append-only**: any client can add a feedback event after interacting with the agent. Validation is **request/response**: a requester opens a job, a named validator closes it.

Those three patterns don't fit well in a single contract. Mint-once needs no ongoing write permissions. Append-only needs every client able to write. Request/response needs writes restricted to a named validator. Splitting the contracts keeps each one's access control simple enough to audit.

## Identity Registry

The Identity Registry mints an **ERC-721 token** for each agent. The token's `agentURI` resolves to a JSON metadata file, typically on IPFS or Arweave, that holds the agent's name, description, image, and services.

The mint is a one-time operation by the agent's deployer. Once the token exists, the (chain, registry-address, agentId) triple is the stable identifier that contracts and aggregators use to reference the agent. There's no central directory and no admin who can revoke it.

Every Reputation and Validation event references an agentId from this registry. Without an Identity Registry token, an agent doesn't exist in either downstream registry.

[Read the full Identity Registry write-up →](/learn/registries/identity)

## Reputation Registry

The Reputation Registry stores **client feedback events**. Each record includes the agent's agentId, the submitting client's Ethereum address, a primary tag like `quality`, `safety`, or `cost`, and a normalized numeric value.

Off-chain ratings have a familiar failure mode: they're locked to the platform that collected them. An agent that builds a strong track record in one marketplace carries nothing to the next. Onchain feedback is public and readable by any aggregator.

Clients can revoke feedback they submitted. A revoked record drops out of the score numerator and the unique-clients count but stays visible in the reliability sub-score. An agent with a pattern of retractions shows reduced reliability, not a clean slate.

[Read the full Reputation Registry write-up →](/learn/registries/reputation)

## Validation Registry

The Validation Registry works as a **request/response pair**. Anyone can open a validation request for a given agent, naming a tag and a specific validator. Only that validator can submit the scored response.

Validators publish a 0–100 score and optional metadata for each tag they evaluate. The validator's transaction is the attestation, so anyone reading the chain can verify the score independently of this explorer or any aggregator.

The validator's history of past responses is itself queryable, so consumers can audit a validator's track record before trusting any single score.

[Read the full Validation Registry write-up →](/learn/registries/validation)

## How they compose

The three registries share one join key: **agentId**. Take any agent, say agent #42. Its identity was minted in the Identity Registry when it was deployed. Clients posted feedback events keyed to agentId #42 in the Reputation Registry. Validators submitted scored responses, also keyed to agentId #42, in the Validation Registry. Any agent detail page on this explorer pulls all three streams and renders them in one view.

## Where to go next

- The cornerstone: [What is ERC-8004?](/learn/what-is-erc-8004)
- The trust framing: [Trustless agents](/learn/trustless-agents)
- The reputation formula: [Reputation v1](/reputation-v1)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)


---

## Trustless agents — what the term means and what it requires

Source: <https://erc-8004.quicknode.com/learn/trustless-agents>

# Trustless agents — what the term means and what it requires

A **trustless agent** is one whose identity, reputation, and behavior can be verified by any observer, onchain, without relying on a central provider to vouch for it. ERC-8004 makes that practical with three composable primitives: a **verifiable identity**, an **auditable reputation trail**, and an **independent attestation mechanism**. Each maps to one of the three onchain registries the standard defines.

If you've read [What is ERC-8004?](/learn/what-is-erc-8004) and want the conceptual framing for why the standard matters, this is the place.

## Why "trustless" is a load-bearing word

The word gets misread constantly. "Trustless" does not mean "don't trust the agent." It means you don't need to trust the **platform** the agent runs on.

A centralized platform can fabricate ratings, suppress negative reviews, revoke an agent's identity, or quietly swap out the model behind a public endpoint. With no independent record, users have no way to catch any of that.

Trustless infrastructure removes that dependency. The verification layer — who the agent is, what its history looks like, what third parties have certified about it — lives on a public chain that any reader can inspect without asking anyone's permission. The agent's output can still be wrong. It can still be biased, slow, or expensive. "Trustless" says nothing about quality. What it does is give you a public, unforgeable record so you can actually measure quality for yourself.

## The three properties of a trustless agent

A trustless agent needs exactly three things. Anything less and some part of the verification still requires trusting an intermediary.

1. **Verifiable identity.** A unique, durable identifier that no platform can revoke. Anyone holding the identifier can look it up and confirm the agent's metadata — name, services, description — without relying on the platform's API being up or the platform being honest. ERC-8004 delivers this through the [Identity Registry](/learn/registries/identity), which mints an ERC-721 token for each agent. The token lives onchain as long as the chain does.

2. **Auditable reputation.** A wallet-attributed history of how the agent has actually performed, written by the clients who used it, public to anyone, verifiable by anyone. Each feedback record is tied to the transaction sender. No platform can add a review it didn't submit or remove one it doesn't like. ERC-8004 delivers this through the [Reputation Registry](/learn/registries/reputation), which records `NewFeedback` and `FeedbackRevoked` events for each agent.

3. **Independent attestation.** A way for third parties to certify specific properties of an agent — safety, accuracy, cost-efficiency — without needing the platform's approval to do it. The attestation is signed by the certifying party's Ethereum address, so it's attributable and public. ERC-8004 delivers this through the [Validation Registry](/learn/registries/validation), where anyone can request a scored evaluation from a named validator.

ERC-8004 implements all three; the next section catalogs what it doesn't claim to do.

## What this is NOT

The boundary here is worth drawing sharply, because the term "trustless" attracts over-broad claims.

**Not decentralized compute.** A trustless agent's model can run on AWS, GCP, or any other centralized infrastructure. The compute layer is irrelevant to the identity and reputation layer. The agent's onchain record is trustless even if every GPU behind it is rented from a single vendor.

**Not provable correctness.** ERC-8004 does not cryptographically verify the agent's outputs. There is no zero-knowledge proof that the agent returned the right answer. Validation scores reflect a named evaluator's judgment, not a mathematical proof. An attestation from a trusted validator is meaningful; it is not a formal guarantee.

**Not free.** Every write to the three registries is an onchain transaction. Registering an agent, submitting feedback, requesting a validation, responding with a score — all cost gas. L2s like Base and Mantle make this cheap; mainnet Ethereum does not. The cost is real and the standard makes no attempt to hide it.

**Not Sybil-proof out of the box.** The onchain primitives don't prevent someone from creating many wallets and flooding an agent's reputation with fake feedback. That problem is not solved at the contract layer — it's pushed to indexers. This explorer penalizes feedback from wallets with thin onchain history. Other consumers can implement their own weighting. The standard intentionally leaves Sybil policy to the application layer, because there is no single policy that fits every use case.

None of these are gaps specific to ERC-8004. They're where onchain identity and reputation infrastructure stops and other layers — compute, formal verification, application policy — have to pick up.

## Why now

Agents have been a research topic for years. What changed recently is that agents started spending money.

An agent that summarizes documents or drafts emails makes a mistake and a person fixes it. An agent that transfers funds, signs contracts, authorizes API calls with quota costs, or routes traffic between services makes a mistake with a price tag. The cost of trusting the wrong agent is no longer measured in annoyed users; it's measured in dollars and liability.

The historical pattern for high-stakes systems is identity first, then reputation. Web1 had domain names but no public reputation layer. Web2 added platform ratings but locked them inside walled gardens where the platform controlled what it showed. Onchain identity-plus-reputation is the next step: the record exists independent of any platform and can be read by any consumer.

The standard arrived when agent use moved from "interesting demo" to "production system with budget."

## How ERC-8004 stacks against adjacent ideas

**MCP and A2A.** Model Context Protocol (Anthropic) and Agent-to-Agent (Google) define how agents communicate — how they exchange messages, invoke tools, and delegate subtasks. They don't define who the agent is or how to check its history. ERC-8004 sits underneath both as the identity and accountability layer. An agent that speaks MCP or A2A can also hold an ERC-8004 identity; the two layers don't conflict and can easily compose.

**Verifiable AI and proof-of-inference.** Projects in the zkML and TEE space (like Giza and ORA) work on proving that a specific computation was run honestly — that a model with a known hash produced a specific output. This is about correctness, not identity. The Validation Registry can record the result of a zk-verified evaluation as easily as a human one: a validator runs a zero-knowledge proof, then submits the outcome as a score with a `responseURI` pointing at the proof. The two approaches complement each other rather than compete.

**Web2 reputation systems.** Star ratings on platforms like G2, Product Hunt, or app stores work fine within the platform. The problem is portability: an agent can't carry its G2 rating to a different aggregator. The platform owns the data. ERC-8004 reputation is portable because the record is on a public chain — any consumer can read it, weight it, and display it without the platform's permission. The data is also auditable in a way platform ratings aren't: anyone can check whether a wallet submitting five-star feedback is real.

These approaches solve distinct problems. They compose rather than compete.

## What you can build on top

The three registries are primitives. A few patterns that fall out of them naturally:

An agent that moves from one platform to another can carry its full history — feedback, validations, revocation patterns — without the previous platform's cooperation. Consumers on the new platform look at the chain, not the platform's export.

A regulator could require agents in a restricted domain to hold a current validation from a named auditor. The onchain record is the compliance artifact, no private certificate database needed.

When agent A delegates a task to agent B, both identities are onchain and the delegation is recordable. That's useful not just for trust, but for debugging: when something goes wrong in a chain of agents, you can trace who authorized what.

An onchain reputation trail is also evidence for insurance. An insurer pricing a policy against agent failure has public claims history. After a loss, they have public data rather than the operator's say-so.

## Where to go next

- The cornerstone: [What is ERC-8004?](/learn/what-is-erc-8004)
- The three registries: [/learn/registries](/learn/registries)
- The reputation formula: [/reputation-v1](/reputation-v1)
- Live agents on this explorer: [/agents](/agents)
- Live validators: [/validators](/validators)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)
- Reference contracts: [https://github.com/erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts)


---

## What is ERC-8004?

Source: <https://erc-8004.quicknode.com/learn/what-is-erc-8004>

# What is ERC-8004?

ERC-8004 is the Ethereum standard for **trustless AI agents**. It defines three onchain registries — identity, reputation, and validation — that give an agent a portable, verifiable identity that no single party controls.

If you've heard about agents being "registered on chain" and wondered what that actually means, this is the long-form answer.

## The short version

An ERC-8004 agent has three onchain footprints:

1. **Identity** — an ERC-721 token, minted once per agent, whose `agentURI` points at agent metadata (name, description, image, services).
2. **Reputation** — a stream of client feedback events from wallets that used the agent. Each event carries a tag and a normalized numeric value.
3. **Validation** — request/response pairs from independent validators who scored the agent on a specific dimension. Each score is 0–100.

Those three signals together give an objective picture of who an agent is *and* how it actually behaves, without anyone playing gatekeeper in the middle.

## What problem this solves

Before ERC-8004, there was no shared standard for any of these things:

**Identity was siloed.** Every agent platform had its own user system. An agent on platform A couldn't be referenced from platform B even if both were solving the same problem.

**Reputation was unverifiable.** Agent ratings lived in private databases. No outside party could audit the data, check for Sybil behavior, or let an agent carry its track record to a new platform.

**Validation was handwavy.** "Trust me, the agent works" was more or less the standard. There was no onchain primitive for an external party to attest that an agent met a quality bar on a specific date.

## The three registries

### 1) Identity Registry

The Identity Registry mints an ERC-721 token for each agent. The token's `agentURI` resolves to a JSON metadata file (typically on IPFS or Arweave) with the agent's name, description, image, and services.

Once minted, the identity is durable. Other contracts and applications reference the agent by its (chain, registry-address, agentId) triple.

*[Read the full Identity Registry write-up →](/learn/registries/identity)*

### 2) Reputation Registry

The Reputation Registry accepts client feedback events. Each record includes the target agent's agentId, the submitting client address, a primary tag (`quality`, `safety`, `cost`…), and a normalized numeric value.

Clients can revoke feedback they submitted. A revoked record is excluded from the score numerator and from the unique-clients count, but is still counted in the reliability sub-score. An agent with heavy retraction churn shows reduced reliability rather than a clean slate.

*[Read the full Reputation Registry write-up →](/learn/registries/reputation)*

### 3) Validation Registry

The Validation Registry works as a request/response: anyone can request a validation for a given agent, naming a tag and a specific validator. The validator submits a score (0–100) plus optional metadata.

The validator's onchain signature is the attestation. No need to trust the explorer or any aggregator — the record is public.

*[Read the full Validation Registry write-up →](/learn/registries/validation)*

## A single agent, start to finish

1. Developer mints an Identity Registry token with a metadata URI.
2. Clients interact with the agent. Each client posts a signed feedback event afterward.
3. A validator runs the agent through a test suite and submits a scored response.
4. Aggregators — this explorer included — pull all three streams into one public reputation view.

Any agent detail page on this explorer shows the full picture. Grab one from the [Featured agents](/) list.

## How ERC-8004 differs from ERC-721

ERC-8004 builds on ERC-721 (the Identity Registry mints ERC-721 tokens), but adds two contracts ERC-721 doesn't define:

| | ERC-721 alone | ERC-8004 |
|---|---|---|
| What it tracks | Ownership and transfer | Ownership + reputation + validation |
| Contract count | One | Three composable |
| Metadata | Static | Live behavior signals |
| Best suited for | Art, collectibles | Autonomous agents |

ERC-721 answers "who owns this?" ERC-8004 also asks whether the agent is worth using, and shows its work.

## Where to look

- [All agents](/agents)
- [Validators](/validators)
- [Recent feedback](/feedback)
- [Recent validations](/validations)
- [Per-network breakdown](/networks)
- [Agent leaderboard](/leaderboard)

## Further reading

- [The EIP for ERC-8004](https://eips.ethereum.org/EIPS/eip-8004)
- [Registry contracts curated by the 8004 team](https://github.com/erc-8004/erc-8004-contracts)
- [The Reputation Formula this explorer uses](/reputation-v1)
- [How to register an ERC-8004 agent](/tutorials/register-agent)
- [An in-depth look at the three ERC-8004 registries](/learn/registries)
- [Why "trustless" actually matters](/learn/trustless-agents)



# Tutorials

## Become an ERC-8004 validator

Source: <https://erc-8004.quicknode.com/tutorials/become-a-validator>

# Become an ERC-8004 validator

Any Ethereum address can be named as a validator in the Validation Registry. No allowlist, no staking requirement, no application process. Watch for requests addressed to your wallet, run your tests, call `validationResponse` with a 0–100 score, and the onchain record is immediate and permanent. Your weight as a validator comes from your track record: completion ratio, response time, and whether you publish a methodology. For the conceptual layer behind all of this, read [/learn/registries/validation](/learn/registries/validation) first.

## What an ERC-8004 validator is (and isn't)

The word "validator" is overloaded in Ethereum. If you've worked around proof-of-stake, you're used to it meaning a **consensus validator**: an operator who stakes ETH to propose and attest blocks, securing the chain. That's a completely different role.

An ERC-8004 validator is an evaluator. You receive a validation request, run a defined test against the named agent, and submit a scored response onchain. The two usages of "validator" collide constantly in conversation, especially when ERC-8004 comes up in proof-of-stake developer communities.

In practice: a validator is an address that responds to `ValidationRequest` events, scores the agent against the request brief, and calls `validationResponse` to emit a `ValidationResponse` event with a score from 0 to 100 and a tag such as `safety`, `accuracy`, or `cost`. No permission required beyond a funded wallet.

## Prerequisites

> **New to ERC-8004?** Start with [Read onchain with viem](/tutorials/viem) first; it sets up the TypeScript client and project structure used across all tutorials.

- An RPC endpoint for a chain where the Validation Registry is deployed. Validation is currently testnet-only in the reference deployments; this tutorial uses Base Sepolia. ([Get one →](/quicknode))
- A funded address with the chain's native gas token to send each response transaction
- A validation methodology you can publish: what you score, how you score it, what sample size you use

That third item is the most skipped. A score with a linked test definition says "I ran these specific checks and got this result" rather than "I think it's 72." Aggregators surface the methodology URI alongside each score. Skip it and you're producing opinion, not attestation.

## The request/response flow

The Validation Registry is a request/response system. Someone opens a validation request by naming an agent (`agentId`), a validator address, and a request document URI/hash. The contract emits a `ValidationRequest` event:

```solidity
event ValidationRequest(
    address indexed validatorAddress,
    uint256 indexed agentId,
    string requestURI,
    bytes32 indexed requestHash
);
```

The `requestURI` is where the requester anchors the test specification or evaluation brief. The `requestHash` commits to that brief and is the key you use when submitting the response.

Watch for `ValidationRequest` events addressed to your validator address, run your tests, and submit your score. The contract only accepts a response from the named validator address. No one else can fill the slot. If you never respond, the request stays open indefinitely with no score written.

Proactive submission (scoring an agent without a prior request) is not part of the v1 contract model. The request/response design makes the evaluator's identity load-bearing and the test definition explicit. That's by design.

## Code: submit a response

The core write is one transaction: `validationResponse(requestHash, response, responseURI, responseHash, tag)`. The function takes the request hash you're answering, your 0–100 score, a URI pointing at your report, a hash for that report, and the tag you scored.

```bash
npm install viem@^2.21.0
```

```ts
// src/submit-response.ts
// ABI derived from the ERC-8004 Validation Registry reference contracts:
//   https://github.com/erc-8004/erc-8004-contracts
// Re-verify against the deployed bytecode at your registry address before
// running on mainnet.
import { createPublicClient, createWalletClient, http, parseAbi, zeroHash } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(process.env.QUICKNODE_RPC_URL!), // https://example.quiknode.pro/YOUR_KEY/
});

const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http(process.env.QUICKNODE_RPC_URL!),
});

const VALIDATION_REGISTRY = "0x8004Cb1BF31DAf7788923b405b754f57acEB4272"; // Base Sepolia

const abi = parseAbi([
  "function validationResponse(bytes32 requestHash, uint8 response, string responseURI, bytes32 responseHash, string tag)",
]);

// requestHash: replace this with the hash from the ValidationRequest event.
const requestHash =
  "0x0000000000000000000000000000000000000000000000000000000000000000";

// response: 0 to 100. Replace with the actual result from your evaluation pipeline.
const response = 85;

// responseURI: pin your report to IPFS or another durable host and use the resulting URI.
const responseURI = "ipfs://YOUR_REPORT_CID";
const responseHash = zeroHash; // replace with the report hash if you publish one

async function main() {
  const hash = await walletClient.writeContract({
    address: VALIDATION_REGISTRY,
    abi,
    functionName: "validationResponse",
    args: [requestHash, response, responseURI, responseHash, "safety"],
  });

  console.log("validationResponse tx:", hash);

  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  if (receipt.status !== "success") {
    throw new Error("validation response transaction reverted");
  }
}
main();
```

The `responseURI` is where your validation earns evidentiary weight. Pin your raw test outputs, the scoring rubric you used, and a reference to the request's `requestURI` on IPFS or any durable HTTP host, then pass the resulting URI. A score with a linked report is auditable: any consumer can fetch it, re-run your tests, and check whether the result holds. A score with no URI is just a number.

Validation responses are permanent. The contract emits `ValidationResponse(address indexed validatorAddress, uint256 indexed agentId, bytes32 indexed requestHash, uint8 response, string responseURI, bytes32 responseHash, string tag)`. Once written, there is no revocation in v1. Submit a score you can stand behind.

### Finding the requestHash

The `requestHash` is passed in by the requester when they call `validationRequest`. It is not generated by the contract. It's a commitment to the request document: `keccak256(requestURIContent)` in production, or `zeroHash` for testing. The contract uses it as the key to match responses, so the same hash cannot be reused for two requests to the same validator.

As a validator, you receive the `requestHash` from the `ValidationRequest` event:

```solidity
event ValidationRequest(
    address indexed validatorAddress,
    uint256 indexed agentId,
    string requestURI,
    bytes32 indexed requestHash
);
```

Two ways to recover it in practice:

**Option 1: getLogs with viem.** Use the same `getLogs` pattern from [/tutorials/viem](/tutorials/viem), filtered to your validator address:

```ts
// src/watch-requests.ts
import { parseAbiItem } from "viem";
import { client } from "./client.js"; // public client from /tutorials/viem

const validatorAddress = process.argv[2] as `0x${string}`;
if (!validatorAddress) {
  console.error("Usage: tsx src/watch-requests.ts <validatorAddress> [fromBlock]");
  process.exit(1);
}

const VALIDATION_REGISTRY = "0x8004Cb1BF31DAf7788923b405b754f57acEB4272"; // Base Sepolia

const event = parseAbiItem(
  "event ValidationRequest(address indexed validatorAddress, uint256 indexed agentId, string requestURI, bytes32 indexed requestHash)"
);

const head = await client.getBlockNumber();
const fromBlock = process.argv[3] ? BigInt(process.argv[3]) : head - 10_000n;
const toBlock = fromBlock + 9_999n < head ? fromBlock + 9_999n : head;

const logs = await client.getLogs({
  address: VALIDATION_REGISTRY,
  event,
  args: { validatorAddress },
  fromBlock,
  toBlock,
});

if (logs.length === 0) {
  console.log("No validation requests found in this block range.");
} else {
  for (const log of logs) {
    console.log("requestHash:", log.args.requestHash);
    console.log("agentId:", log.args.agentId);
    console.log("requestURI:", log.args.requestURI);
    console.log("---");
  }
}
```

Store each `(agentId, requestHash, requestURI)` tuple; you'll need all three to run your evaluation and submit your response.

**Option 2: This Explorer's API.** Every `ValidationRequest` event across supported chains is indexed here. Query `GET /api/v1/validators/{address}/requests` to pull open requests addressed to your validator, with `requestHash` included in each row.

## Create a test request

For end-to-end testing on testnet, open a validation request from a second wallet. In production the requester is a separate party. Call `validationRequest(validatorAddress, agentId, requestURI, requestHash)` on the Validation Registry. Pass `zeroHash` as the `requestHash`; the contract accepts it for testing. In production, `requestHash` is `keccak256(requestURIContent)` so the requester can prove the request spec hasn't changed.

```ts
await walletClient.writeContract({
  address: VALIDATION_REGISTRY,
  abi: parseAbi(["function validationRequest(address validatorAddress, uint256 agentId, string requestURI, bytes32 requestHash)"]),
  functionName: "validationRequest",
  args: [validatorAddress, agentId, "", zeroHash],
});
```

Decode the `ValidationRequest` event from the receipt to get the `requestHash` you'll need for `submit-response.ts`.

## Run it

```bash
# Watch for incoming requests to your validator address
QUICKNODE_RPC_URL=https://... npx tsx src/watch-requests.ts 0xYOUR_VALIDATOR_ADDRESS

# Submit a response (replace requestHash with the value from the ValidationRequest event)
PRIVATE_KEY=0x... QUICKNODE_RPC_URL=https://... npx tsx src/submit-response.ts
```

## What makes a validator credible

Publish your scoring rubric and reference it in every `responseURI`. A sequence of 85s means nothing on its own; attach the rubric and the test outputs, and each score becomes something a consumer can actually evaluate. This is the single biggest thing new validators skip.

Completion ratio matters more than volume. The [validators index](/validators) shows how many requests each address has been named on versus how many they've responded to. A high pending count reads as absent. Requesters check this before naming someone; a poor ratio means they'll name someone else next time.

Sample size shows in the report. Single-trial scores carry less weight than scores derived from repeated runs. For accuracy or robustness tags, a thin sample is visible to any consumer who reads the `responseURI`. Run enough trials that the score is defensible, not just a number you happened to get once.

## Reputation feedback loop

The explorer weights validator responses by track record. New validators start with lower weight; established validators with solid completion ratios and published methodology count for more in score aggregation. Respond promptly, attach a real `responseURI`, and your weight grows. Higher-weight responses contribute more to agent scores, which means requesters who care about quality start naming you more often.

The [validators index](/validators) shows the live leaderboard: total request count, completions, pending, average response time, and unique agents per validator. That's the public record you're building.

## Common errors

- `wrong validator`: the contract rejects a `validationResponse` call from an address that isn't the named validator on that request. Check that your wallet matches the address in the `ValidationRequest` event.
- `score out of range`: score must be 0–100 inclusive. The contract rejects anything above 100, even though `uint8` can hold up to 255. Off-by-one on the upper bound is the common mistake.
- `request already responded`: each request hash accepts exactly one response. You can't revise an existing submission; a second attempt reverts.
- `registry not deployed`: the Validation Registry is mainnet-pending in the reference deployments. Use one of the testnet registry addresses from [/docs/contracts](/docs/contracts) until mainnet deployments are live.
- `tag mismatch`: your response tag should match the test dimension described in the request brief. The contract doesn't enforce semantic consistency, but consumers who read both the request and the response will notice if you submitted a cost-efficiency score against a safety request.

## Next steps

- [What is ERC-8004?](/learn/what-is-erc-8004): the standard, end to end
- [Validation Registry deep-dive](/learn/registries/validation): the conceptual layer
- [Browse current validators](/validators): see who's active and what they're scoring
- [Submit feedback as a client](/tutorials/submit-feedback): the lighter-weight reputation primitive
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)


---

## How to register an ERC-8004 agent

Source: <https://erc-8004.quicknode.com/tutorials/register-agent>

# How to register an ERC-8004 agent

Register your agent onchain with a single contract call: write a JSON metadata file, pin it to IPFS, and send a `register(agentURI)` transaction to the Identity Registry. After the tx confirms, the agent shows up on this Explorer. Allow about thirty minutes and a hundred lines of TypeScript. This walkthrough uses viem v2, but the same call works from any web3 client.

## What you'll build

> **New to ERC-8004?** Start with [Read onchain with viem](/tutorials/viem) first; it sets up the TypeScript client and project structure this tutorial reuses.

By the end, you'll have a minted Identity Registry token on the chain of your choice, owned by a wallet you control, with a metadata URI that resolves to a JSON file describing your agent. The bulk of the code is the IPFS pin and the contract call; everything else is glue. This tutorial is the read-write counterpart to [/tutorials/viem](/tutorials/viem), which covers the read-only path. If you've worked through that one, reuse the `client.ts` file from it directly.

## Prerequisites

1. An RPC endpoint. Sign up at [Quicknode](/quicknode); the free tier is enough to register a single agent. Paid tiers unlock the archive depth and rate limits you'll want once you start indexing reputation events.
2. A funded EOA wallet that holds the chain's native token for gas. Ethereum mainnet costs dollars. Base, Mantle, BNB, and Avalanche cost pennies.
3. The Identity Registry address for your chain. See [/docs/contracts](/docs/contracts). Thanks to CREATE2 it's `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` on every reference deployment.
4. Node 20+ and a TypeScript project. Same toolchain as [/tutorials/viem](/tutorials/viem); if you worked through that tutorial, reuse its `client.ts`.
5. Quicknode IPFS for pinning and serving the metadata file. The metadata file lives off-chain; the registry only stores the URI. See [Quicknode IPFS](https://www.quicknode.com/ipfs?utm_source=erc8004-explorer&utm_medium=referral&utm_campaign=tutorial-register-agent) and the [IPFS REST API docs](https://www.quicknode.com/docs/ipfs?utm_source=erc8004-explorer&utm_medium=referral&utm_campaign=tutorial-register-agent).

## Step 1: Write the metadata file

Agent metadata is a small JSON document. The canonical schema is documented on the [Identity Registry page](/learn/registries/identity); the short version is four fields, two of them optional.

```json
{
  "name": "ResearcherBot",
  "description": "Summarizes academic papers on demand. Optimized for arxiv.",
  "image": "ipfs://QmExampleHash/avatar.png",
  "endpoints": [
    { "type": "https", "url": "https://researcher.example.com/api" },
    { "type": "websocket", "url": "wss://researcher.example.com/stream" }
  ]
}
```

Here's what each field does:

- `name`: human-readable, surfaced in the Explorer header and most aggregator UIs.
- `description`: one to three sentences. Searchable, and the surface most LLMs cite when asked about your agent.
- `image` (optional but recommended): an `ipfs://...` URL pointing to an avatar. Shown on agent detail pages and in social cards.
- `endpoints` (optional): how consumers actually talk to the agent. Indexers don't validate these; consumers do.

The schema is intentionally minimal. Fields beyond the canonical set are ignored, not rejected, so forward-compat is by additive convention rather than versioning. If you want a richer profile, add the fields and document them. Anything that conforms to the four canonical keys round-trips through every indexer.

A note on `endpoints`: the `type` field is a free-form string by convention. Consumers in the wild use `https`, `websocket`, `grpc`, `mcp`, and a handful of others. Pick whatever your callers expect; there's no enum the contract enforces. If your agent speaks more than one protocol, list them all and let consumers pick. The Identity Registry doesn't ping these URLs at registration time, so a typo here won't block your tx. It'll just leave clients staring at a dead endpoint until you update the metadata.

## Step 2: Pin the metadata to IPFS

You can pin programmatically with the Quicknode IPFS REST API or by hand from the Quicknode dashboard. Either way, the goal is a content-addressed URL of the form `ipfs://<cid>` that survives the host going offline.

Get your IPFS API key from the [Quicknode dashboard](https://dashboard.quicknode.com/) under the IPFS section and set it as `QUICKNODE_IPFS_KEY`.

```bash
npm install viem@^2.21.0
```

```ts
// src/pin-metadata.ts
const metadata = {
  name: "ResearcherBot",
  description: "Summarizes academic papers on demand. Optimized for arxiv.",
  image: "ipfs://QmExampleHash/avatar.png",
  endpoints: [{ type: "https", url: "https://researcher.example.com/api" }],
};

const body = new FormData();
body.append(
  "Body",
  new Blob([JSON.stringify(metadata)], { type: "application/json" }),
  "metadata.json"
);
body.append("Key", `metadata-${Date.now()}.json`);
body.append("ContentType", "application/json");

async function main() {
  const response = await fetch("https://api.quicknode.com/ipfs/rest/v1/s3/put-object", {
    method: "POST",
    headers: { "x-api-key": process.env.QUICKNODE_IPFS_KEY! },
    body,
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`Quicknode IPFS upload failed: ${response.status}\n${text}`);
  }

  const payload = await response.json() as { pin?: { cid?: string }; cid?: string };
  const cid = payload.pin?.cid ?? payload.cid;
  if (!cid) throw new Error("Quicknode IPFS response did not include a CID");

  const agentURI = `ipfs://${cid}`;
  console.log("agentURI =", agentURI);
}
main();
```

The `image` field above references its own IPFS hash. Pin the avatar separately first, then reference its CID inside the metadata JSON. Don't nest the image as a base64 data URL: search-engine and aggregator crawlers don't unpack base64 image fields, so your agent will look avatarless to the outside world.

If you'd rather click than script, the Quicknode dashboard does the same job: upload the avatar, copy its CID, paste it into the metadata JSON, upload the JSON, copy its CID. The result is identical to what the REST API returns. For one-off registrations the UI is faster; for anything you'll repeat, the API call wins because it's checked into git alongside the rest of your deploy script.

Whichever path you take, save the resulting `agentURI` somewhere you can paste it into the next step. If the URI is wrong, the registration tx might still succeed (the contract only validates shape, not reachability), and you'll have a permanently registered agent pointing at nothing. Verify the URI resolves to your JSON in a browser before sending the tx.

## Step 3: Send the registration transaction

The actual write is one transaction: a single `register(agentURI)` call against the Identity Registry. viem's `walletClient.writeContract` takes care of the nonce, gas estimation, signing, and broadcast in one shot.

> **Verify the contract ABI before running this snippet.** The example below uses `register(string agentURI) returns (uint256)`, which matches the canonical reference contracts at [erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts) (verified against commit `0463311` on 2026-04-26). The contract also exposes a no-arg `register()` and a three-arg `register(string, MetadataEntry[])` overload for advanced metadata. Cross-check the deployed bytecode at your registry address before running on mainnet; overloads make function selectors easy to mismatch.

```ts
// src/register.ts
// Function ABI verified against the canonical reference repo:
//   https://github.com/erc-8004/erc-8004-contracts (commit 0463311, 2026-04-26)
// If you point this at a non-canonical deployment, re-verify before running.
import { createWalletClient, http, parseAbi } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(process.env.QUICKNODE_RPC_URL!),
});

const IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";

const abi = parseAbi([
  "function register(string agentURI) returns (uint256)",
]);

async function main() {
  const hash = await walletClient.writeContract({
    address: IDENTITY_REGISTRY,
    abi,
    functionName: "register",
    args: ["ipfs://YOUR_PINNED_CID"],
  });

  console.log("registration tx:", hash);
}
main();
```

Onchain, two things happen: the contract mints an ERC-721 token to your wallet and emits `Registered(uint256 indexed agentId, string agentURI, address indexed owner)`. The minted `tokenId` is the agent's permanent ID. The function returns it, but `writeContract` only gives you back a tx hash, not the return value, so we pull the ID out of the event log instead.

The `Registered` event is what every indexer in the ecosystem listens for, including this Explorer. Anything that doesn't reach the event log doesn't exist as far as downstream consumers are concerned, so a tx that succeeds but doesn't emit `Registered` (which would only happen if you called the wrong function) leaves you with a transaction receipt and no agent. That's why the receipt-parse step below is the real confirmation, not the bare tx hash.

`waitForTransactionReceipt` blocks until the tx confirms, and decoding the event off the receipt gives us the agent ID:

```ts
// receipt-parse.ts (continuation of src/register.ts; reuses `hash` from above)
import { parseEventLogs, parseAbiItem } from "viem";
import { client } from "./client.js"; // public client from /tutorials/viem

async function parseReceipt(hash: `0x${string}`) {
  const receipt = await client.waitForTransactionReceipt({ hash });

  const registeredEvent = parseAbiItem(
    "event Registered(uint256 indexed agentId, string agentURI, address indexed owner)"
  );

  const [decoded] = parseEventLogs({
    abi: [registeredEvent],
    logs: receipt.logs,
    eventName: "Registered",
  });

  if (decoded) console.log("agentId =", decoded.args.agentId);
}
```

## Step 4: Verify on this Explorer

Once the tx confirms, your agent shows up at `https://erc-8004.quicknode.com/agents/<network-slug>/<tokenId>`, for example `/agents/ethereum-mainnet/42`. The page renders your agent's name, description, IPFS avatar, and endpoints list, plus an empty feedback and validations history that fills in once clients start submitting. If you have used a testnet, make sure you enable testnet filters on the right side of the navbar.

Indexer lag is typically under a minute on the supported chains. A longer delay usually means the chain itself is slow to finalize on the indexer's confirmation depth; per-chain values are listed on [status](https://erc-8004.quicknode.com/status). If five minutes go by and nothing shows up, look at the receipt. A `Registered` event in the logs means the indexer will catch up. No event means the registration didn't actually succeed.

Once the agent page renders, share its URL. The Explorer's social cards pull `name`, `description`, and `image` from your IPFS metadata, so a Twitter or Slack paste shows the avatar and bio without any extra setup. That's the same surface most aggregators read, which is why the metadata fields back in Step 1 matter: they're what the rest of the world sees. If you set the bio in a hurry and want to clean it up later, that's fine: pin a new JSON, send a `URIUpdated` tx, and indexers will replay it without losing your existing feedback or validations.

## Run it

```bash
# Step 2: pin your metadata JSON
QUICKNODE_IPFS_KEY=... npx tsx src/pin-metadata.ts

# Step 3: send the registration transaction (replace the agentURI placeholder first)
PRIVATE_KEY=0x... QUICKNODE_RPC_URL=https://... npx tsx src/register.ts
```

## Common errors

- The `register()` call reverts. Almost always a malformed `agentURI`: wrong scheme, missing CID, or past the length the contract accepts. Use `ipfs://`, `ar://`, or `https://`, and keep the URI short. Other revert causes (insufficient gas, an RPC dropping the tx mid-broadcast) are recoverable with a retry; a bad URI needs a code fix.
- The tx reverts with no error reason. Usually a gas estimation that came in low. Pass an explicit `gas` value 1.5× the estimate, or wait for a calmer block.
- The agent doesn't show up on the Explorer after five minutes. Check the receipt. A `Registered` log means the indexer will catch up. No `Registered` log means the tx didn't actually register anything, even if it confirmed. Confirmation depth values are on [status](https://erc-8004.quicknode.com/status); if everything looks right and the agent still hasn't appeared, file an issue.

## Next steps

- [Read agents back with viem](/tutorials/viem): the read-only counterpart to this tutorial
- [Production RPC for ERC-8004](/quicknode): Ready to ship? Get an RPC endpoint that won't choke under your agent's traffic
- [Contract addresses and ABIs](/docs/contracts)
- [What is ERC-8004?](/learn/what-is-erc-8004)
- [Identity Registry deep dive](/learn/registries/identity)
- [Reputation formula](/reputation-v1)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)
- Reference contracts: [https://github.com/erc-8004/erc-8004-contracts](https://github.com/erc-8004/erc-8004-contracts)


---

## Submit ERC-8004 feedback for an agent

Source: <https://erc-8004.quicknode.com/tutorials/submit-feedback>

# Submit feedback for an ERC-8004 agent

One transaction. Call `giveFeedback` on the Reputation Registry with the agent's `tokenId`, a primary tag, a score-like value, and optional evidence URI/hash fields. The sending wallet becomes the `clientAddress` on the event. Confirmed in a block, shows up on the explorer within seconds. See [Reputation Registry deep-dive](/learn/registries/reputation) for how that record factors into the score.

## Prerequisites

> **New to ERC-8004?** Start with [Read onchain with viem](/tutorials/viem) first; it sets up the TypeScript client and project structure used across all tutorials.

- An RPC endpoint on the chain where the agent lives. This tutorial targets Base; the Reputation Registry address is identical on every supported chain. ([Get one →](/quicknode))
- An Ethereum wallet with enough native token to cover gas
- The agent's `(network, tokenId)` pair, from any agent's detail page under `/agents/:network/:id`

The RPC connects to the chain. The wallet signs and sends the transaction, and `msg.sender` becomes the `clientAddress` on the feedback record. The `tokenId` is how the contract routes the feedback to the right agent.

## The feedback record

`giveFeedback` takes eight arguments:

- `agentId` (uint256): the agent's token identifier from the Identity Registry
- `value` (int128) and `valueDecimals` (uint8): the numeric value and decimal scale. For a 0-100 score, pass `85` and `0`.
- `tag1` (string): the primary rating dimension, such as `quality`, `safety`, `cost`, `speed`, or `accuracy`
- `tag2` (string, optional): a secondary label if your workflow needs one
- `endpoint` (string, optional): the service endpoint or interaction surface being rated
- `feedbackURI` (string, optional): URI for a longer rationale; use an IPFS URI if you want it to outlast your server
- `feedbackHash` (bytes32, optional): content hash for the off-chain evidence, or `0x00...00` if you have no evidence document

The contract stores tags as strings and indexes `tag1` as a hashed topic for efficient filtering. The ones this explorer tracks are `quality`, `safety`, `cost`, `speed`, and `accuracy`; each maps to a per-tag sub-score on the agent's detail page. Use a conventional tag and your score gets rolled into the cross-client aggregate. Use a custom tag and the explorer surfaces it, but nobody else's feedback matches until other clients adopt the same label.

## Code: TypeScript + viem

```bash
npm install viem@^2.21.0
```

```ts
// src/submit-feedback.ts
// ABI derived from the ERC-8004 reference contracts:
//   https://github.com/erc-8004/erc-8004-contracts
// Re-verify against the deployed bytecode at your registry address before
// running on mainnet.
import {
  createWalletClient,
  createPublicClient,
  http,
  parseAbi,
  zeroHash,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(process.env.QUICKNODE_RPC_URL!), // https://example.quiknode.pro/YOUR_KEY/
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(process.env.QUICKNODE_RPC_URL!),
});

// Mainnet address. Testnet deployments may differ (e.g. Base Sepolia: 0x8004B663056A597Dffe9eCcC1965A193B7388713).
// Confirm at /docs/contracts before running.
const REPUTATION_REGISTRY = "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63";

const abi = parseAbi([
  "function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
]);

// Target agent: find the tokenId by searching the agents index at /agents, then reading the number from the URL (e.g. /agents/base/42 means tokenId 42).
const agentId = 42n;

// value=85 and valueDecimals=0 means "85 / 10^0", a score of 85 out of 100.
async function main() {
  const hash = await walletClient.writeContract({
    address: REPUTATION_REGISTRY,
    abi,
    functionName: "giveFeedback",
    args: [
      agentId,
      85n,
      0,
      "quality",
      "",
      "https://researcher.example.com/api",
      "ipfs://YOUR_FEEDBACK_REPORT_CID",
      zeroHash,
    ],
  });

  console.log("feedback tx:", hash); // e.g. 0xabc...

  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  if (receipt.status !== "success") {
    throw new Error("feedback transaction reverted");
  }
  console.log("confirmed in block", receipt.blockNumber);

  // Decode the NewFeedback event to get your feedbackIndex (needed to revoke later).
  const { parseEventLogs, parseAbiItem } = await import("viem");
  const feedbackEvent = parseAbiItem(
    "event NewFeedback(uint256 indexed agentId, address indexed clientAddress, uint64 feedbackIndex, int128 value, uint8 valueDecimals, string indexed indexedTag1, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)"
  );
  const [decoded] = parseEventLogs({ abi: [feedbackEvent], logs: receipt.logs, eventName: "NewFeedback" });
  if (decoded) console.log("feedbackIndex =", decoded.args.feedbackIndex);
}
main();
```

When the transaction lands, the contract emits `NewFeedback` with the `agentId`, `clientAddress`, `feedbackIndex`, `value`, `valueDecimals`, `tag1`, `tag2`, `endpoint`, `feedbackURI`, and `feedbackHash`. To hook a dashboard update on confirmation, listen for `NewFeedback` filtered by `agentId` using `publicClient.getLogs` or `publicClient.watchContractEvent`. The viem [docs](https://viem.sh/docs/actions/public/watchContractEvent) cover the watch pattern.

On tag encoding: the contract stores `tag1` as a string and Solidity places `keccak256(tag1)` in the indexed topic. If you use viem's ABI event parser, pass the human-readable tag string in filters and let viem handle the topic encoding. Check the ABI at your target address before running on mainnet; non-canonical deployments may differ.

## Verify

After the transaction confirms, open `/agents/:network/:tokenId` for the agent. The feedback row shows up within one block: about two seconds on Base, around twelve on Ethereum mainnet. Each row shows client address, tag, value, timestamp, endpoint, and feedback URI where provided. The reputation score refreshes on the next indexer pass, which tracks block cadence.

To find any agent, search the [agents index](/agents) by name or address, or build the URL directly from the network slug and tokenId.

## Revoke a feedback you submitted

The original submitter can retract. You reference the record by `feedbackIndex`: the per-client index emitted in the original `NewFeedback` event.

```ts
// src/revoke-feedback.ts
const revokeAbi = parseAbi([
  "function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
]);

async function main() {
  const revokeHash = await walletClient.writeContract({
    address: REPUTATION_REGISTRY,
    abi: revokeAbi,
    functionName: "revokeFeedback",
    args: [agentId, 0n], // replace 0n with the feedbackIndex logged by giveFeedback
  });

  console.log("revoke tx:", revokeHash);
}
main();
```

Revoking drops the record from the feedback sub-score numerator and from the unique-clients count. The history stays. The revocation itself is a public event, and indexers count it toward the reliability sub-score. An agent with heavy revocation churn looks less reliable, not just cleaned up. Formula details at [/learn/registries/reputation](/learn/registries/reputation).

## Run it

```bash
PRIVATE_KEY=0x... QUICKNODE_RPC_URL=https://... npx tsx src/submit-feedback.ts
```

The script logs the transaction hash, confirms the block, and prints the `feedbackIndex` you'll need if you ever want to revoke.

## Common errors

- `self feedback rejected`: reference deployments reject feedback from the agent owner or an approved operator. Connect a different wallet.
- `agent not found`: wrong `tokenId`, or the wrong chain. Confirm both from the agent's URL on this explorer.
- `wrong feedbackIndex`: revocation keys off the per-client `feedbackIndex`, not the global log index. Pull it from the original `NewFeedback` event.
- `out-of-range value`: the contract can carry arbitrary `int128` values, but this explorer only aggregates non-revoked score-like rows whose normalized value is in `[0, 100]`.

## Next steps

- [What is ERC-8004?](/learn/what-is-erc-8004): the standard, end to end
- [Become a validator](/tutorials/become-a-validator): the heavier-weight reputation primitive
- [Reputation Registry deep-dive](/learn/registries/reputation): how scores are calculated
- [Browse live feedback](/feedback): see what other clients are submitting
- viem docs: [https://viem.sh](https://viem.sh)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)


---

## Read ERC-8004 onchain with TypeScript and viem

Source: <https://erc-8004.quicknode.com/tutorials/viem>

# Read ERC-8004 onchain with TypeScript and viem

Reading ERC-8004 onchain with viem is mostly a matter of pointing a `PublicClient` at an RPC and calling `getLogs` against the right registry addresses. This walkthrough does that on any EVM chain with viem v2 and a Quicknode endpoint. The walkthrough starts with a client, lists recent agents, fetches one agent's metadata off IPFS, and reads its feedback. Roughly 200 lines, no wallet needed.

## Prerequisites

> **Start here.** This is the recommended first tutorial. The TypeScript client and project structure set up below are reused by [register-agent](/tutorials/register-agent), [submit-feedback](/tutorials/submit-feedback), and [become-a-validator](/tutorials/become-a-validator).

1. Node 20+ and a TypeScript project (or `tsx` for one-off runs)
2. An RPC URL (sign up at [Quicknode](/quicknode) for the chain you want to read from)
3. The Identity / Reputation / Validation Registry addresses for that chain (see [/docs/contracts](/docs/contracts))
4. Patience for IPFS: agent metadata is content-addressed and fetched off-chain, so the first fetch from a cold gateway can take a couple of seconds
5. (Optional) A Quicknode IPFS gateway URL for fetching agent metadata, set as `QUICKNODE_IPFS_GATEWAY_URL`. Falls back to `ipfs.io` if not set. Find your dedicated gateway URL in the [Quicknode IPFS dashboard](https://dashboard.quicknode.com/).

## Install

```bash
npm install viem@^2.21.0
```

viem is the only runtime dep needed for this tutorial. The pin at major 2 is intentional: the v1 → v2 migration changed the public API enough that older snippets won't compile against v2, and `latest` would mean a future v3 silently breaks everything below.

## Connect to the chain

`createPublicClient` is viem's read-only client. You give it a `chain` (one of viem's named exports) and a `transport` (here, plain HTTP pointed at your Quicknode endpoint). One client handles every read below.

To switch chains, swap the `chain` import. viem ships named exports for `mainnet`, `base`, `bsc`, `avalanche`, `mantle`, and most other EVM chains. The RPC URL is the only other thing that has to change.

```ts
// src/client.ts
import { createPublicClient, http } from "viem";
import { mainnet, base, bsc, avalanche, mantle } from "viem/chains";

const QUICKNODE_RPC_URL = process.env.QUICKNODE_RPC_URL!; // e.g. https://abc.quiknode.pro/xyz/

export const client = createPublicClient({
  chain: mainnet, // swap for base / bsc / avalanche / mantle
  transport: http(QUICKNODE_RPC_URL),
});
```

## List recent agent registrations

Every agent registration emits a `Registered(uint256 indexed agentId, string agentURI, address indexed owner)` event from the Identity Registry. To list agents, call `getLogs` with that event signature and the registry address.

For Ethereum mainnet, the Identity Registry is at `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`. CREATE2 means it's byte-identical on every reference-deployment chain. The other addresses live in [/docs/contracts](/docs/contracts).

The `fromBlock` choice is the main knob. Use the deploy block (24339871 on Ethereum mainnet) to backfill the whole registry. Use something like `head - 10_000n` for a "what's new lately" view (~5–6 hours of Base history, or ~33 hours on Ethereum mainnet). Going earlier than the deploy block is wasted RPC budget.

viem decodes indexed topics and the non-indexed event data, so `agentId`, `owner`, and `agentURI` all come out of `log.args`:

```ts
// src/list-agents.ts
import { parseAbiItem } from "viem";
import { client } from "./client.js";

const IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";

const event = parseAbiItem(
  "event Registered(uint256 indexed agentId, string agentURI, address indexed owner)"
);

const head = await client.getBlockNumber();
const logs = await client.getLogs({
  address: IDENTITY_REGISTRY,
  event,
  fromBlock: head - 10_000n, // last ~10k blocks (Quicknode eth_getLogs cap); use 24339871n to backfill from deploy
  toBlock: "latest",
});

for (const log of logs) {
  console.log(log.args.agentId, "→", log.args.agentURI, "(owner", log.args.owner, ")");
}
```

> **Backfilling all historical registrations?** For deep history, use [Quicknode Streams](https://www.quicknode.com/streams): filtered event data pushed to your storage with built-in backfill.

## Fetch one agent's metadata

The `agentURI` field is typically `ipfs://<cid>`. Browsers and Node can't fetch `ipfs://` directly, so rewrite it to your Quicknode IPFS gateway first, then fall back to a public gateway like `https://ipfs.io/ipfs/<cid>` if needed. In production, set a short `AbortSignal.timeout(...)` on the fetch and fall back to a second gateway on timeout; gateways flap independently, so a retry on a different host fixes most transient failures.

The metadata JSON shape is `name`, `description`, `image`, and `endpoints`. The full schema is documented on the [Identity Registry page](/learn/registries/identity).

```ts
// src/fetch-metadata.ts
async function fetchAgentMetadata(agentURI: string) {
  const quicknodeIpfsGateway = (process.env.QUICKNODE_IPFS_GATEWAY_URL ?? "https://ipfs.io/ipfs/")
    .replace(/\/?$/, "/");
  const url = agentURI.startsWith("ipfs://")
    ? agentURI.replace("ipfs://", quicknodeIpfsGateway)
    : agentURI;

  const res = await fetch(url);
  if (!res.ok) throw new Error(`metadata fetch failed: ${res.status}`);

  return res.json() as Promise<{
    name: string;
    description: string;
    image?: string;
    endpoints?: Array<{ type: string; url: string }>;
  }>;
}

const agentURI = process.argv[2];
if (!agentURI) {
  console.error("Usage: tsx src/fetch-metadata.ts <agentURI>");
  process.exit(1);
}
const metadata = await fetchAgentMetadata(agentURI);
console.log(JSON.stringify(metadata, null, 2));
```

## Read feedback for an agent

Feedback events come from the Reputation Registry. The signature is long but the shape isn't bad. `agentId` and `clientAddress` are indexed. The third indexed slot is `keccak256(tag1)`, which is handy if you want to prefilter by tag without scanning every row. The numeric value is `value` (signed int128) divided by `10 ** valueDecimals`; this Explorer only includes rows whose normalized value lands in the 0-100 score range when calculating `feedback_score`.

The Reputation Registry on Ethereum mainnet is at `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`. Testnet deployments may use different addresses. Confirm at [/docs/contracts](/docs/contracts) before running. viem's `args` filter narrows the query to a specific agent without you having to hash the topic by hand:

```ts
// src/read-feedback.ts
import { parseAbiItem } from "viem";
import { client } from "./client.js";

const REPUTATION_REGISTRY = "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63";

const event = parseAbiItem(
  "event NewFeedback(uint256 indexed agentId, address indexed clientAddress, uint64 feedbackIndex, int128 value, uint8 valueDecimals, string indexed indexedTag1, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)"
);

async function listFeedback(agentId: bigint, fromBlock: bigint, toBlock: bigint) {
  return client.getLogs({
    address: REPUTATION_REGISTRY,
    event,
    args: { agentId },
    // To prefilter by tag, also pass: indexedTag1: "quality"
    // viem hashes the string into a bytes32 topic for you.
    fromBlock,
    toBlock,
  });
}

const agentIdArg = process.argv[2];
if (!agentIdArg) {
  console.error("Usage: tsx src/read-feedback.ts <agentId> [fromBlock]");
  process.exit(1);
}
const agentId = BigInt(agentIdArg);
const head = await client.getBlockNumber();
const fromBlock = process.argv[3] ? BigInt(process.argv[3]) : head - 10_000n;
const toBlock = fromBlock + 9_999n < head ? fromBlock + 9_999n : head;

const logs = await listFeedback(agentId, fromBlock, toBlock);
if (logs.length === 0) {
  console.log("No feedback found in this block range.");
} else {
  for (const log of logs) {
    const score = Number(log.args.value) / 10 ** Number(log.args.valueDecimals);
    console.log(`[${log.args.feedbackIndex}] score=${score} tag=${log.args.tag1} client=${log.args.clientAddress}`);
  }
}
```

`endpoint` and `feedbackURI` are optional; submitters may provide a score and tag alone. Filter out empty `feedbackURI` rows if you're rendering a review list.

Each feedback row's normalized value is `Number(value) / 10 ** valueDecimals`. Because `value` is `int128` (signed), a non-conforming submission can carry a negative number or a metric that is not score-like at all. Clamp for display, and exclude out-of-range values before aggregating. The Reputation Registry article explains how non-revoked rows roll into the explorer's [composite score](/reputation-v1).

One thing to handle on the read side: clients can pull a feedback record back by emitting `FeedbackRevoked(uint256 indexed agentId, address indexed clientAddress, uint64 indexed feedbackIndex)`. Any indexer that aggregates feedback has to subtract those revocations, otherwise the score lies. For a one-off read it's fine to ignore; for a real index, listen to both events and reconcile by `(agentId, clientAddress, feedbackIndex)`.

## Pagination and archive depth

Two things tend to bite once you move from prototype to production reads.

- **Block-range cap and retries.** RPC providers cap how wide a window a single `getLogs` call can sweep. Quicknode's cap is generous but if you hit "block range too wide", chunk `fromBlock`/`toBlock` into smaller windows and concatenate. While you're there, wrap each call in a 3-attempt retry with exponential backoff (RPCs flap and viem doesn't retry by default). For deep historical backfills, [Quicknode Streams](https://www.quicknode.com/streams) is the better path: filtered event data pushed to your storage with built-in backfill and no range cap.
- **Archive node.** Backfilling from the deploy block needs archive depth. Quicknode's paid tiers include archive (see [/quicknode](/quicknode)). Without archive history you're stuck reading from the chain head forward, so a brand-new index can never catch up to events older than your provider's retention window.

## Run it

```bash
# List recent agent registrations
QUICKNODE_RPC_URL=https://... npx tsx src/list-agents.ts

# Fetch metadata for one agent (pass an agentURI from the list above)
QUICKNODE_RPC_URL=https://... QUICKNODE_IPFS_GATEWAY_URL=https://... npx tsx src/fetch-metadata.ts ipfs://QmYourCID

# Read feedback for an agent (replace 32172 with an agentId from list-agents)
QUICKNODE_RPC_URL=https://... npx tsx src/read-feedback.ts 32172
```

## Next steps

- [What is ERC-8004?](/learn/what-is-erc-8004): conceptual overview
- [Register your first agent](/tutorials/register-agent): the write path
- [Get RPC for ERC-8004](/quicknode): Quicknode signup
- [Contract addresses](/docs/contracts)
- The Reputation formula: [/reputation-v1](/reputation-v1)
- viem docs: [https://viem.sh](https://viem.sh)
- The canonical EIP: [https://eips.ethereum.org/EIPS/eip-8004](https://eips.ethereum.org/EIPS/eip-8004)

