Last updated 2026-05-05
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 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 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 →)
- 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:
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.
npm install viem@^2.21.0
// 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:
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, filtered to your validator address:
// 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.
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
# 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 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 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 avalidationResponsecall from an address that isn’t the named validator on that request. Check that your wallet matches the address in theValidationRequestevent.score out of range: score must be 0–100 inclusive. The contract rejects anything above 100, even thoughuint8can 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 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?: the standard, end to end
- Validation Registry deep-dive: the conceptual layer
- Browse current validators: see who’s active and what they’re scoring
- Submit feedback as a client: the lighter-weight reputation primitive
- The canonical EIP: https://eips.ethereum.org/EIPS/eip-8004
FAQ
Do I need to be approved to be a validator?
No. The Validation Registry is permissionless; any address can be named as a validator and submit responses to requests addressed to it. There is no allowlist, no staking requirement, and no central registry of approved validators. Your weight in any aggregator’s view comes from your track record.
How do I publish my methodology?
Publish a public document describing how you score agents: what tag categories you cover, what tests you run, your sample-size policy, your scoring rubric. Link this document from the responseURI you submit alongside each response. Validators that publish methodology give consumers an auditable basis for each score.
Does running a validator cost money?
It costs the gas to submit each validation response onchain. On L2s like Base or Mantle, this is a few cents per response. Mainnet is more expensive but most agents you’ll validate are on L2s anyway. Your validation infrastructure (wherever you run the actual tests) is your own cost.
Can I validate my own agent?
The contract does not prevent it, but no aggregator that surfaces validations will weight a self-validation positively. Don’t bother; public detection is trivial.
How does my reputation as a validator get built?
Aggregators track each validator’s total request count, completion ratio, average response time, and number of unique agents evaluated. The /validators page on this explorer shows the live leaderboard. A high completion ratio and short average response time are the clearest signals of an active, reliable validator.
Is there a minimum number of agents I need to validate?
No floor enforced by the contract. The explorer’s leaderboard begins surfacing a validator after they have submitted responses, so the practical approach is to start responding and let your history accumulate. A thin history isn’t a disqualifier; it just means consumers will lean on your completion ratio and response time rather than volume.
Can I retract a validation response?
No. The Validation Registry v1 does not support response revocation. Once a ValidationResponse event is written onchain, it is permanent. Think carefully before submitting a score; if you later change your assessment, you can submit a new response to a new request, but the original score remains in the event log.