DEV Community

Priyanshu Rajput
Priyanshu Rajput

Posted on

ERC-8004: How AI Agents Prove Who They Are

Last time I wrote about x402 — agents paying for stuff over plain HTTP.

Two people asked me the same follow-up:

Cool, my agent can pay anyone in 2 seconds. But how does it know who it's paying?

Fair. Let's talk about that.

The plumber problem

Your kitchen sink is leaking. You find a number on a wall poster. The guy shows up and says: ₹3,000, cash, upfront.

What do you do?

flowchart TD
    A[Stranger wants your money] --> B{Is he a real plumber?}
    B --> C{Has he fixed sinks before?}
    C --> D{If he wrecks my kitchen, who do I tell?}
    D --> E[Only then: pay]
    style E fill:#2d7,stroke:#000,color:#000

You ask around. You check if he has a shop. You look at reviews. You call the guy your neighbour used.

Now here's the thing — your AI agent can't do any of that.

It has a wallet, a $50 budget, and twelve endpoints all claiming to do market analysis. All twelve will happily take the money.

flowchart LR
    AG[Your agent<br/>$50 budget] --> E1[endpoint 1 ✅ real]
    AG --> E2[endpoint 2 ❓]
    AG --> E3[endpoint 3 ❓]
    AG --> E4[endpoint 4 💀 scam]
    AG --> E5[...8 more ❓]

To your agent, all of these look exactly the same.

Why the old internet didn't have this problem

Because a company always vouched for the stranger.

You trusted... Because...
A seller on Amazon Amazon checked them
A payment Stripe verified the merchant
An npm package GitHub stars + download counts
A driver Uber ran a background check

Every trust signal you've ever used was rented from a platform.

Agents don't have a platform. An agent built on OpenAI's stack has no way to look up an agent built on someone else's stack. So far everyone solved it the ugly way: my platform only trusts my own agents.

ERC-8004 is the attempt to put that lookup somewhere nobody owns.

Where it sits

Think of hiring a freelancer:

flowchart TD
    subgraph S[" "]
    A["🗣️ How do we talk?<br/><b>MCP / A2A</b>"]
    B["🪪 Who are you? Any references?<br/><b>ERC-8004</b>"]
    C["💸 How do I pay you?<br/><b>x402 / AP2 / MPP</b>"]
    end
    A --> B --> C
    style B fill:#fd6,stroke:#000,color:#000

MCP and A2A are the phone call. x402 is the wallet. ERC-8004 is the ID card and the reference check.

It's an Ethereum standard (EIP-8004) written by people from MetaMask, the Ethereum Foundation, Google and Coinbase. Live on Ethereum mainnet since 29 January 2026, plus Base, BSC, Polygon, Arbitrum.

It's three contracts. That's the whole thing.

flowchart LR
    R1["1️⃣ Identity<br/><i>the licence plate</i>"]
    R2["2️⃣ Reputation<br/><i>the reviews</i>"]
    R3["3️⃣ Validation<br/><i>the lab test</i>"]
    R1 --- R2 --- R3

1️⃣ Identity — your agent is literally an NFT

Not a new standard. Not a DID. A plain ERC-721 token.

function register(string agentURI) external returns (uint256 agentId)
Enter fullscreen mode Exit fullscreen mode

Call it, get a token ID. That token ID is the agent. The token URI is the agent's profile.

Real life version: it's an auto-rickshaw permit. Numbered, hangs on the vehicle, points to the driver's details, and can be sold to another driver.

flowchart LR
    NFT["🎫 NFT #22"] --> URI["agentURI"]
    URI --> FILE["📄 registration file<br/>name, description,<br/>endpoints, price"]

Because it's an NFT, every agent on earth is instantly viewable in OpenSea and holdable in MetaMask. Zero new tooling. Beautiful.

The profile file, trimmed down:

{
  "name": "market-analysis-agent",
  "description": "Equity sentiment. $0.02 per query.",
  "services": [
    { "name": "A2A", "endpoint": "https://agent.example/..." },
    { "name": "MCP", "endpoint": "https://mcp.agent.example/" }
  ],
  "x402Support": true,
  "supportedTrust": ["reputation", "tee-attestation"]
}
Enter fullscreen mode Exit fullscreen mode

See x402Support? These two protocols were built to hold hands.

Two small details I really liked

The payment address needs a signature.

Where the agent gets paid is stored on-chain, but you can't just type in a new address — you have to prove you control it with a signature.

And when the agent NFT is sold, the payment address is wiped automatically. New owner has to re-prove it.

flowchart LR
    A["Agent #22<br/>pays to 0xAAA"] -->|sold| B["Agent #22<br/>pays to ❌ nothing"]
    B -->|new owner signs| C["Agent #22<br/>pays to 0xBBB"]
    style B fill:#fdd,stroke:#000,color:#000

Real life version: you buy a shop, the old owner's bank account doesn't come with it.

The address on the licence must match the actual shop.

Anyone can claim their agent lives at bigbank.com. So the domain has to answer back — you publish a small file at bigbank.com/.well-known/agent-registration.json pointing to the same agent ID.

flowchart LR
    ON["⛓️ On-chain: I am agent #22 at bigbank.com"] <-->|both must agree| OFF["🌐 bigbank.com: yes, that's me, #22"]

If you've ever added a DNS record to prove you own a domain, same move.


2️⃣ Reputation — Amazon reviews, but nobody owns Amazon

Anyone can leave feedback on any agent. On-chain. Forever.

giveFeedback(agentId, value, valueDecimals, tag1, tag2, endpoint, feedbackURI, feedbackHash)
Enter fullscreen mode Exit fullscreen mode
  • value + valueDecimals = a number with a decimal point (9977 + 2 → 99.77%)
  • tag1 / tag2 = what you're rating: starred, uptime, successRate, responseTime
  • Agent owners can't review their own agent
  • You can revoke your own review
  • Anyone can reply to a review — an agent posting a refund proof, a spam-detector flagging junk

Nothing gets deleted. The trail only grows.

The verified purchase badge

This is my favourite part. A review can carry the receipt of the x402 payment that paid for the work:

"proofOfPayment": { "toAddress": "0x...", "txHash": "0x...", "chainId": "1" }
Enter fullscreen mode Exit fullscreen mode
flowchart LR
    A["⭐ Some address<br/>says it's good"] -->|+ txHash| B["⭐ Someone who<br/><b>actually paid $2</b><br/>says it's good"]
    style B fill:#2d7,stroke:#000,color:#000

Amazon's little "Verified Purchase" tag, on-chain.

The weirdest line in the whole spec

To read an agent's rating, you must pass a list of reviewers you trust:

getSummary(agentId, address[] clientAddresses /* MUST be non-empty */, tag1, tag2)
Enter fullscreen mode Exit fullscreen mode

There is no "just give me the average rating" function. The spec refuses to write one, and says why: an unfiltered average is wide open to fake reviews.

Real life version: imagine Zomato deleted the star rating and instead said —

flowchart TD
    Z["🍽️ 4.2 ⭐ (1,847 reviews)"] --> X["❌ not available"]
    X --> Y["✅ Whose opinion do you<br/>actually trust?<br/>Pick them. I'll average those."]
    style Y fill:#fd6,stroke:#000,color:#000

Annoying. Also completely correct — as we're about to see.


3️⃣ Validation — for when reviews aren't good enough

Reviews tell you what happened to other people, in the past.

For a $2 API call, fine. For $50,000, or a medical answer? Not fine.

Real life version:

Buying street food Choosing a surgeon
Look at the queue Check the board certification
Reviews are enough You want the actual credential

So agents can ask for their work to be independently checked:

validationRequest(validatorAddress, agentId, requestURI, requestHash)
validationResponse(requestHash, response /* 0-100 */, responseURI, responseHash, tag)
Enter fullscreen mode Exit fullscreen mode

The registry deliberately doesn't say how validation works. It's just a socket. Plug in whatever:

flowchart TD
    V["Validation Registry<br/><i>just a socket</i>"] --> A["🔁 Someone re-runs the job<br/>and gets slashed if they lie"]
    V --> B["🔐 zkML proof"]
    V --> C["🖥️ TEE attestation<br/>hardware proves what code ran"]

This is exactly where the confidential-compute folks (Oasis ROFL and friends) plug in — and it's what both commenters on my last post were pointing at.

Tiered trust — the actual big idea

flowchart TD
    Q{"How much money<br/>is at risk?"}
    Q -->|"$0.02 — a pizza"| L["Identity only. Just pay."]
    Q -->|"$50 — real money"| M["Identity + filtered reviews"]
    Q -->|"$50,000 — ouch"| H["Identity + reviews +<br/><b>validation before you release funds</b>"]
    style L fill:#dfd,stroke:#000,color:#000
    style M fill:#fd6,stroke:#000,color:#000
    style H fill:#fdd,stroke:#000,color:#000

Security proportional to what you'd lose. That framing — not any single registry — is the real contribution.


The whole loop, once

sequenceDiagram
    participant H as Your agent
    participant ID as Identity Registry
    participant RR as Reputation Registry
    participant W as Worker agent
    participant V as Validator

    H->>ID: who's out there? read profiles
    H->>RR: reviews — but only from addresses I trust
    H->>W: here's my task
    W-->>H: 402 Payment Required 💸
    H->>W: x402 signed payment
    W-->>H: here's your result
    W->>V: (big job only) please verify this
    V-->>H: verified: 95/100 ✅
    H->>RR: ⭐ review + payment receipt

What's the catch?

Now I have to be harder on this than I was on x402, because there's actual data.

In June 2026 researchers crawled all three registries across Ethereum, BSC and Base, from launch to May 2026.

The adoption number looks amazing: 170,000+ registered agents. 150,000+ reviews.

Then they checked if any of it was real.

Catch 1: almost nothing registered actually works

Share of registered agents with a valid profile and a live endpoint:

pie showData
    title Ethereum agents — real vs placeholder
    "Actually alive" : 3
    "Placeholder" : 97
Chain Actually alive
Ethereum 3%
BSC 4%
Base 15%

Registering is one cheap transaction. So people mint agent IDs the way they squat domain names.

Catch 2: the reviews are mostly fake

Reviewers showing coordinated fake-review behaviour: 73.5% (Ethereum), 59.2% (BSC), 90.6% (Base).

Strip those out, and the share of rated agents left with zero valid reviews:

flowchart LR
    E["Ethereum<br/>15.8%"] ~~~ B["BSC<br/>77.9%"] ~~~ BA["Base<br/>86.8%"]
    style E fill:#fd6,stroke:#000,color:#000
    style B fill:#fdd,stroke:#000,color:#000
    style BA fill:#fbb,stroke:#000,color:#000

Remember getSummary refusing to give you an average? This is why. The spec authors called it in advance.

Catch 3: the scores don't mean the same thing

Tags are free text. Decimals are per-review. So:

flowchart LR
    A["My review:<br/>87 · tag: starred"] --- Q["🤷 average these?"] --- B["Your review:<br/>9977 · tag: uptime"]

Both are "feedback". Neither is comparable to the other. Researchers' fix: a proper tag registry with declared units and ranges. Doesn't exist yet.

And a few more

flowchart TD
    A["🪪 Identity ≠ competence<br/><i>proves the profile is his,<br/>not that he can fix sinks</i>"]
    B["🎫 Reputation is sellable<br/><i>identity is an NFT — reviews<br/>stay attached when sold</i>"]
    C["📝 Still a Draft EIP<br/><i>deployed, not finalised</i>"]
    D["🔑 Keys are now targets<br/><i>never give unlimited allowances</i>"]

If you're building on it today

flowchart TD
    S1["1. Read the on-chain identity + profile.<br/>Verify the domain answers back."]
    S2["2. Never read reviews unfiltered.<br/>Pass your own reviewer allowlist."]
    S3["3. Prefer reviews with a payment receipt."]
    S4["4. Above ₹X value → require validation<br/>from a validator you picked in advance."]
    S5["5. Scope your approvals.<br/>Signed intents, not silent spending."]
    S1 --> S2 --> S3 --> S4 --> S5

The bottom line

x402 gave agents money. ERC-8004 gives them names, references, and a way to get caught lying.

And look at how boring the design is. ERC-721 for identity. Signatures for wallet proof. Hashes for file integrity. /.well-known/ for domains. Events for indexing.

Every piece already existed. The only new thing is agreeing on one neutral place to look.

Honest status today: registries are live, 170,000 agents are registered, and almost none of the trust signals are usable yet.

flowchart LR
    A["✅ Standard exists"] --> B["🚧 Reputation aggregators<br/>Reviewer scoring<br/>Validator networks<br/>Insurance pools"] --> C["🎯 Standard works"]
    style B fill:#fd6,stroke:#000,color:#000

That middle box is empty. The spec deliberately left it for someone else.

Probably you.


Top comments (0)