ERC-8257: Agent Tool Registry

Hi all! I’m opening discussion for a new ERC: Agent Tool Registry. Co-authored with @cody.

PR: Add ERC: Agent Tool Registry by ryanio · Pull Request #1723 · ethereum/ERCs · GitHub

Abstract

A permissionless onchain registry for AI agent tools. Each registration commits a metadata URI and a keccak256 content hash; invocation access is gated by an optional external predicate contract - the same “pluggable external contract” pattern used by Seaport zones, Uniswap v4 hooks, and ERC-4337 paymasters. Registrations are anchored to a canonical off-chain manifest via origin-binding (manifest served at a well-known path on the endpoint’s origin) plus creator self-attestation (the manifest declares which onchain address may register it). Pricing hints are protocol-agnostic and live in the manifest; the registry never touches funds.

Why now

AI agent tooling is fragmenting across proprietary catalogs. We want a chain-native directory agents can read permissionlessly, with extensible access control (NFT gating, subscriptions, allowlists, DAO votes, reputation) expressed as predicate contracts rather than baked into the registry.

Status

Reference implementation deployed to Base mainnet, exercised against a CLI/SDK and several example predicates (ERC-721, ERC-1155, subscription, composite).

Looking for feedback on

  • Manifest origin-binding + creator self-attestation as the trust anchor (vs. signed manifests, ENS resolution, etc.)
  • Predicate ERC-165 dispatch + composability (the CompositePredicate example combines up to 3 leaf predicates with AND/OR + per-term negation)
  • Protocol-agnostic pricing hints - is identifying the payment protocol by an opaque string too loose? Too tight?
  • Whether the registry should remain payment-free or absorb a minimum settlement primitive
6 Likes

interesting setup! need to test it

1 Like

Thanks for opening this. Quick comment on item 2 (predicate ERC-165 dispatch + composability): the existing pinned IRequirementTypes markers (IERC721Holding, IERC1155Holding, ISubscription) cleanly cover on-chain holdings — same EVM chain as the registry, single Solidity read. There’s a fourth shape that the data parameter on IAccessPredicate.hasAccess already supports natively but no marker yet describes: off-chain-signed wallet-state attestations.

The case for it as a distinct kind:

  1. Cross-chain wallet state cannot be expressed as a native EVM predicate. Wallet state on non-EVM chains (Solana, XRPL, Bitcoin) can’t be evaluated from a Solidity predicate. The natural shape is for an off-chain issuer to evaluate the condition set against the relevant chain data, sign a verdict, and have the on-chain predicate verify the signature. The spec’s data parameter (“Opaque context bytes (e.g., tokenId, proof, signature)”) is designed for exactly this.

  2. It’s distinct from the AccessProof pattern in §“Account Parameter Is Advisory.” That pattern is requester-self-signed — the wallet signs a challenge to prove it is account. The attestation pattern here is issuer-signed — an external service signs a verdict about account’s wallet state. Identity-binding and state-binding are orthogonal; a complete access scheme may want both, and conflating them at the marker layer would lose that distinction.

  3. Gas fits comfortably. ECDSA P-256 verification via the RIP-7212 precompile is ~3,450 gas; ABI decode of a seven-tuple proof is another few thousand. Well under the 200k cap.

Concrete proposal:

/// @dev kind for off-chain-signed wallet-state attestation requirements.
///      data = abi.encode(string issuerJWKSURI, bytes32 conditionHash)
///      interfaceId = 0x7a111640
interface IWalletStateAttestation {
    function walletStateAttestation() external;
}

Selector 0x7a111640 = bytes4(keccak256("walletStateAttestation()")), verified non-colliding with the three pinned IDs.

The getRequirements.data layout abi.encode(string issuerJWKSURI, bytes32 conditionHash) tells an agent two things: where to fetch the issuer’s public-key set (a JWKS document at a well-known URL — same trust-anchor pattern as the spec’s manifest origin-binding), and which condition set the predicate enforces. The hasAccess.data payload then carries the proof itself: abi.encode(bool pass, address wallet, bytes32 conditionHash, uint256 blockNumber, bytes32 r, bytes32 s, bytes32 messageHash).

A working reference predicate that matches this layout, verifies P-256 via RIP-7212, advertises IAccessPredicate + IERC165 for registration validation, and returns false (rather than reverts) on any check failure is at:

Tests in the same repo (InsumerAccessPredicate.t.sol) include a happy-path case that runs real ECDSA P-256 verification of a live signed attestation through the precompile, plus eight failure modes (tampered signature, tampered message hash, wrong account, wrong condition hash, pass=false, stale, future-dated, empty data).

Happy to fold the marker into a PR against IRequirementTypes.sol if it’s useful, or leave it as a third-party marker per the spec’s extension guidance — whichever fits the cadence you’re running.

1 Like

Thanks Douglas. This is a clean proposal and fits well within the spec’s extension model. You’re right that the data parameter on hasAccess was designed exactly for this kind of proof-carrying pattern, and the kind marker system is intentionally open so third-party requirement types like this don’t need spec-level changes.

A few thoughts:

  1. The spec already supports this as a third-party marker. You can publish IWalletStateAttestation independently, use its interfaceId as the kind, and any predicate implementing it will work with the existing registry. No changes needed to ERC-8257 itself.

  2. Rather than enshrining this as a pinned type in the spec’s IRequirementTypes, we think the right place for this is the tool-sdk written by @ryanio and myself. That’s where agents can discover and interact with known predicates. We put together a PR for review that decodes your kind (0x7a111640), extracts issuerJwksUri and conditionHash from requirement data, and displays attestation fields in the CLI inspect command. Does this look like what you’d expect? Happy to adjust based on your feedback.

  3. Your reference implementation and test coverage look solid. The approach of advertising IAccessPredicate + IERC165 is the right call.

In short: the onchain plumbing already works for your use case today. The gap is tooling, not spec. Let us know if the SDK approach looks right and we’ll get it merged.

1 Like

The decoder + types match the spec from post #3WALLET_STATE_ATTESTATION_KIND, the (string issuerJWKSURI, bytes32 conditionHash) decode, and the DecodedWalletStateAttestationRequirement shape are exactly what I’d expect. SKILLS.md framing is also right — third-party kind, not pinned in the spec.

One note on the CLI inspect path. decodeRequirement() itself is already kind-keyed and works for any predicate returning kind 0x7a111640, but the inspect command dispatches on predicateName === "WalletStateAttestationPredicate". The reference impl I linked returns "InsumerAccessPredicate" from name(), so the inspect renderer won’t match. Cleaner to dispatch the attestation-specific rendering on the requirement’s kind instead — the kind is what the decoder is already keyed on, and it’s a stable property of the requirement shape.

Beyond that the structure looks right.

— Douglas

2 Likes

We’ve been working on a project sponsored by the ENS MetaGov working group to develop a standard for appending structured metadata to ENS names. By attaching a JSON schema file that describes what metadata is expected to be found, any ENS name/subname can have rich metadata that is machine readable and indexable.

Schemas are self-describing, so even consumers who aren’t familiar with your standard can still ingest the data. And your schema can include a human-readable explanation so that any developers who come across the ENS name and want to understand the data know where to go to learn more.

We’ve already created a schema that is designed to work with ERC-8004, and it turns an ENS name into an agent’s on-chain identity. It can either replace ERC-8004 completely, or be used in tandem with it. Agents can now advertise their capabilities, tools, and services directly on their ENS name and be discovered through indexing (more details here). This could be expanded to add any other details that an agent may want to advertise.

Our spec is an ENSIP (since it’s ENS-specific, we haven’t gone the ERC route yet), and you can see the full document here: ENSIP: Node Classification And Metadata by jmacwhyte · Pull Request #64 · ensdomains/ensips · GitHub

We have a web UI, SDK, and CLI for managing on-chain metadata, and hope to see ENS names become the most powerful form of on-chain identity. I’d be curious to explore how the agent tool registry could also be integrated into our agent schema, and if there are some unique benefits ENS could offer (one benefit is that ENS is a canonical “single source of truth” instead of needing to look through many different registry contracts).

2 Likes

Appreciate your thoughts here - the ENSIP work looks great and it’s exciting to see folks thinking about how these pieces fit together. Two concrete integration points if you want to sketch something:

  1. The 8257 manifest is extensible via reverse-DNS namespaced keys (see the “Unknown Fields and Extensions” section), so an "app.ens.name" field on a tool manifest pointing back at the canonical ENS record is a no-spec-change path. Symmetrically, an ENS metadata entry referencing tools should standardize on the (chainId, registryAddress, toolId) tuple - that’s the canonical identifier in 8257.
  2. Worth being clear that this stacks with rather than replaces 8257’s trust model. 8257 anchors creator-attestation to the endpoint origin (well-known path) and the registering address; an ENS name resolving to the same address would be an additional human-readable handle for that identity, not a substitute for origin-binding.

If you’ve got a minimal example schema entry that references a tool registry record, I’d love to take a look.

Sorry for the delay in getting back to you, I wanted to take some time to dive into this!

We’ve also created a reference “Application” schema, which we envision being used to identify and secure official software services. If someone like Uniswap were to register all of their smart contracts as subnames under the uniswap.eth name, users could use that info to know which contracts are legitimate; they could also register something like web.uniswap.eth and attach the Application schema to it to declare the URL of their official web app, together with checksums or signatures to allow consumers to avoid phishing sites and detect if something has been modified.

In the same way that we added ERC-8004 to our Agent schema, it sounds like adding ERC-8257 to the Application schema makes perfect sense. Just like with our Agent schema, an array of registrations[*] could list all of the service’s 8257 registrations (in CAIP-19 format), which would allow any client to query (for example) web.uniswap.eth and iterate through each entry to discover every tool that Uniswap provides. Like you said, each registration could also point back to the ENS name to verify the two-way link.

We also have a new spec we are working on for lightweight ENS attestations to prove ownership of various resources. The current use case is to publish attestation signatures directly in an attestations[{resouce name}] map, but we could modify the spec to also allow attestations[{CAIP-19 of 8257 registration}] to instead return a URL that points to the attestation hosted on the client’s web server.

The consumer’s journey would then be:

  1. Knowing I want to use Uniswap, retrieve uniswap.eth and traverse all subnames.
  2. Discover web.uniswap.eth classed as an “Application”
  3. Iterate through all registrations on that subname to discover every tool that is registered on chain.
  4. For each tool discovered, query attestations[{CAIP-19}] to find the URL of an attestation hosted at the same origin as the tool endpoint described in the 8257 registration.
  5. If the attestation can be verified as being signed by the owner of the ENS name, we can trust the veracity of the tool registration (the 8257 record doesn’t actually need to point back to the ENS name, but it could be helpful for discovery that is going the other way).

The ERC-8257 records would still be completely self-contained and verifiable, but ENS could become a powerful discovery path for humans and agents who are starting with a known service and want to discover what services they offer (and avoid imposters).

Let me know your thoughts!

How would renewals and or usage-based tools work? I see the spec currently mentions NFT-driven expiration, however, I would recommend an extension mechanism (unless I overlooked it!)

Really appreciate the deep dive. The discovery flow you sketched is exactly the shape I was hoping this would compose into.

I’d lean toward keeping ENS out of the 8257 record itself. The registry is intentionally neutral about naming systems: the canonical identifier stays (chainId, registryAddress, toolId), and the verification anchor stays origin-binding. The nice thing about your proposal is that nothing actually needs to change in 8257 for the two-way link to work. Your Application schema carries registrations[*] in CAIP-19, and a verifier can independently look up each entry, hit the tool’s origin attestation, and cross-check that the same origin is attested to by the ENS owner via your attestations[{CAIP-19}] map. If both attestations resolve to the same domain, the loop is closed.

What probably makes sense on our side is a small helper in tool-sdk (the helper sdk for this ERC) that performs this traversal: given an ENS name, walk subnames, discover Application schemas, enumerate registrations, fetch each tool from the 8257 registry, and verify origin attestations on both ends. That keeps ENS as a discovery overlay without the registry needing to know it exists. Drafted a quick PR in tool-sdk with this helper if you want to take a look and compare notes.

@VrilLabs good question, and you didn’t miss anything. The spec intentionally doesn’t define a renewal or extension mechanism at the registry layer: expiration and renewal are properties of the access model, not the registry, so they live entirely in the predicate.

The NFT-with-expiration flow is just one reference predicate (SubscriptionPredicate). It gates on ERC-5643 subscription NFTs, which already define renewSubscription / cancelSubscription, so renewals happen at the NFT contract and the predicate simply re-reads expiresAt on each access check.

Usage-based tools fit the same pattern with a different predicate: a counter or prepaid-credits contract that decrements on each grant, refilled by a top-up transaction. Because predicates are arbitrary IAccessPredicate contracts, anything expressible as a function of (toolId, account, data) works: metered quotas, time-windowed grants, allowlist plus expiry, DAO-gated extensions. The registry stays minimal, and the access model is where extension semantics live.

Amazing timing — I just opened [Draft ERC] Universal AI Inference
Verification Registry (link below) and your tool registry is the
missing other half.

Concretely they compose like this:

  • ERC-8257 answers “which tools is this agent allowed to call”
  • Our draft answers “did the agent actually produce this output,
    and against which model + prompt + tool-call trace”

So a verifier can do: resolve agent → fetch allowed tool set from
8257 → check the inference proof’s tool-call manifest is a subset →
attest. Zero extra registry, both standards stay minimal.

One question for you: are tool identifiers in 8257 chain-scoped
or globally addressable (CAIP-like)? That decides whether our
manifest field needs a chainId prefix or not.

Cross link: [Draft ERC] Universal AI Inference Verification Registry - #22 by Damonzwicker