--- eip: 8263 title: Onchain Proof Layer for AI Agents description: Anchor AI agent actions to Ethereum with cryptographic proofs and verifiable identity registry. author: Vincent Wu (@TruthAnchor-AI) discussions-to: https://ethereum-magicians.org/t/erc-8263-onchain-proof-layer-for-ai-agents/28577 status: Draft type: Standards Track category: ERC created: 2026-05-14 --- ## Abstract This proposal specifies a minimal on-chain proof layer for AI agents. It allows agents to anchor their actions (decisions, outputs, or state transitions) to the Ethereum blockchain by submitting a cryptographic commitment (typically a SHA-256 or Keccak-256 hash) together with an identity-scheme byte and a 32-byte agent identifier. This creates a verifiable, immutable timeline of agent activity, enabling trustless auditing and reputation systems for autonomous agents. ## Motivation As AI agents increasingly perform high-value tasks (e.g., DeFi trading, content creation, governance voting), there is a critical need for a standard way to prove that an agent performed a specific action at a specific time. Current agent activities are often opaque or stored in centralized databases. Implementations provide a decentralized "black box recorder" for AI agents, ensuring that their history cannot be retroactively altered. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ### Canonical Event The protocol contract MUST emit exactly one event for every successful anchor: ```solidity event AnchorProof( uint8 agentIdScheme, bytes32 indexed agentId, bytes32 indexed proofHash, address indexed operator, bytes aux ); ``` Event signature hash (topic0): `keccak256("AnchorProof(uint8,bytes32,bytes32,address,bytes)")` = `0x9fe832d83a52f83bd7d54181e4cc7ff8b4e227cc1d3a0144376894b5df6c23cc`. EVM caps indexed fields at three, so the topic layout is: | topic | field | rationale | |:-------:|:-------------|:-------------------------------------------------------------| | topic0 | event sig | hash above | | topic1 | `agentId` | per-agent audit trail (most common query) | | topic2 | `proofHash` | "was this exact payload anchored?" lookup | | topic3 | `operator` | transaction submitter (see *Canonical-form guards*; not an authorization claim) | `agentIdScheme` is deliberately not indexed: it lives in the data segment so indexers always decode it together with `agentId`. Decoupling them would let malformed clients produce ambiguous logs. Data decoding (for indexer authors): the non-indexed fields are ABI-encoded as the tuple `(uint8 agentIdScheme, bytes aux)`. In Solidity: `(uint8 scheme, bytes memory aux) = abi.decode(data, (uint8, bytes));`. The first 32-byte word is the padded `uint8`; the second is the offset to the `aux` bytes; the third onward holds aux length + contents. ### AgentId Scheme Registry | `agentIdScheme` | name | `agentId` derivation | |:-:|:--|:--| | `0x00` | ANONYMOUS | `bytes32(0)` (contract-enforced) | | `0x01` | REGISTRY | 32-byte registry record id ([ERC-8004](./eip-8004.md) or compatible) | | `0x02` | URI_HASH | `keccak256(canonical agent URI)` | | `0x03+` | reserved | rejected at contract level (see *Canonical-form guards*) | Higher-level conventions (e.g. `hf:owner/model`, `gh:owner/repo`, ENS handles) are URI shapes that resolve to scheme `0x02` via `keccak256` of the canonical URI string. They are not separate on-chain schemes — keeping the registry small avoids fragmentation while leaving room for future ERCs to register additional scheme bytes. ### Entrypoints The protocol contract MUST implement the following two entrypoints: ```solidity interface IOnChainProof { /// @notice Minimal anchor: empty aux, lowest calldata cost. function anchor( uint8 agentIdScheme, bytes32 agentId, bytes32 proofHash ) external; /// @notice Extended anchor with opaque aux bytes for adjacent protocols. function anchorWithAux( uint8 agentIdScheme, bytes32 agentId, bytes32 proofHash, bytes calldata aux ) external; } ``` `anchor` submits an anchor with empty `aux` and is the lowest-calldata path. `anchorWithAux` submits an anchor with optional `aux` bytes. **`aux` is explicitly non-normative.** It is an extension surface for adjacent protocols. Examples of (non-normative) uses: OCP digest commitments, session ids, parent-proof references. Indexers MUST treat `aux` as opaque bytes and MUST NOT derive proof semantics from it. Adjacent-protocol extraction is profile-defined. Adjacent protocols MAY define extraction conventions over `proofHash`, over `aux`, or over both — this ERC itself stays neutral and does not prescribe a mapping. A profile that treats `proofHash` as its own digest commits every anchor; a profile that treats `aux` (when present) as its carrier is opt-in. Both are valid composition shapes over the same event. ### Canonical-Form Guards The contract MUST enforce these invariants at write time: | Field / Scheme | Invariant | |:--|:--| | `proofHash` | `proofHash != bytes32(0)` (else revert) | | scheme `0x00` | `agentId == bytes32(0)` (else revert) | | scheme `0x01`,`0x02` | `agentId != bytes32(0)` (else revert) | | scheme `0x03+` | revert (reserved) | Indexers can rely on these basic canonical-form invariants without re-validating them, but MUST NOT infer authorization, agent identity correctness, or payload validity from the event alone — those concerns belong to higher-layer verifier profiles. `proofHash` hashing: the contract does not constrain the hash algorithm or the canonicalization that produces `proofHash`. Profile-level rules apply at the commitment layer; the contract only requires that `proofHash` be a non-zero 32-byte value. ### Data Schema The pre-image hashed into `proofHash` SHOULD follow a structured JSON format to ensure interoperability across implementations: ```json { "version": "1.0", "agent_id": "0x...", "action_type": "trade", "content": { "pair": "ETH/USDC", "amount": "1.5", "tx_hash": "0x..." }, "nonce": 12345, "timestamp": 1715673600 } ``` The contract itself is agnostic to this shape; the structure is a SHOULD at the application layer, not a contract-enforced invariant. ## Rationale ### SHA-256 vs Keccak-256 SHA-256 is recommended for compatibility with off-chain AI systems and web standards, although Keccak-256 is more common in Ethereum. The contract treats `proofHash` as an opaque non-zero `bytes32`, so either algorithm (or a profile-defined construction) is acceptable; the algorithm is a profile decision, not a contract decision. ### Single event, two entrypoints Both entrypoints emit the same `AnchorProof` event. Keeping a single canonical event means indexers and verifiers parse one topic0 across all anchors, regardless of whether `aux` was used. Splitting the event by entrypoint would fragment the indexer surface for no observable benefit at the commitment layer. ## Backwards Compatibility No backwards compatibility issues: this is a new proposal and defines a new event signature (topic0 above) that does not collide with prior Ethereum events. ## Reference Implementation Reference contracts verified on Etherscan, both implementing the interface above: - Ethereum Mainnet: `0xe95d6a15966984c209a62a2c188828555eb5ec3d` - Sepolia Testnet: `0x89EE9b68c3b2f50cbE9D0fC4Dc134939a0475c1C` Both deployments are verified on Etherscan and emit the `AnchorProof` event with the topic0 above, accepting the two entrypoints unchanged. Independent verifiers can confirm the contract surface by reading the verified source on the corresponding block explorers. ## Security Considerations Implementations MUST NOT store original action content on-chain — only the commitment hash. Storing plaintext leaks user data and exceeds practical gas limits. The `agentId` field MUST be validated against the chosen registry (e.g. [ERC-8004](./eip-8004.md) when `agentIdScheme = 0x01`) before reputation aggregation; unverified ids MUST be flagged. The following subsections are **informative**. They describe how this ERC composes with adjacent standards and how anchors can be independently verified. They introduce no new normative requirements on the contract surface; the canonical event and the scheme registry are unchanged. ### Compatibility with [ERC-8004](./eip-8004.md) This subsection describes a compatibility profile for deployments that compose anchors with [ERC-8004](./eip-8004.md) (Agent Identity Registry) identities. This ERC is identity-registry-agnostic by construction: the canonical event carries `(agentIdScheme, agentId)` as opaque bytes and assigns no on-chain meaning beyond the scheme registry. [ERC-8004](./eip-8004.md) is one such registry, and the two standards compose cleanly when an implementer chooses to use both. #### Scheme `0x01` compatibility bridge When an implementation chooses to populate anchors with [ERC-8004](./eip-8004.md) identities under `agentIdScheme = 0x01` (REGISTRY), the recommended encoding of the 32-byte `agentId` payload is a zero-padded [ERC-8004](./eip-8004.md) record id: ```text agentId = bytes32(uint256(erc8004AgentId)) ``` This is a recommendation, not a mandate. Implementations remain free to use other 32-byte derivations under scheme `0x01`, provided they document the derivation in the operator's deployment notes so verifiers can reproduce the same value. #### Resolution direction Resolution is one-way at the protocol layer. An anchor with scheme `0x01` can reference an [ERC-8004](./eip-8004.md) record; [ERC-8004](./eip-8004.md) does not need to reference this ERC in return. Indexers that want a bidirectional view can build a reverse index off-chain from the `AnchorProof` event log. #### Identity vs commitment separation - An anchor with `agentIdScheme = 0x00` (ANONYMOUS) is a valid anchor with no identity-registry dependency. - An anchor with `agentIdScheme = 0x02` (URI_HASH) binds to an off-chain canonical identity and does not require an [ERC-8004](./eip-8004.md) record. - The commitment carried in `proofHash` is independent of the identity scheme. The same `proofHash` can be re-anchored under a different `(agentIdScheme, agentId)` without breaking the commitment. ### Compatible Identity Registries This subsection generalizes the above to any identity registry that an implementer might compose with anchors, and describes an optional off-chain convention for resolving `(agentIdScheme, agentId)` back to a wallet address. The scheme registry is intentionally identity-system-agnostic. Scheme `0x01` is reserved for "REGISTRY" without naming a specific registry, so the same anchor format can compose with [ERC-8004](./eip-8004.md), with other on-chain agent registries (proprietary or future-standard), and with registry adapters that front external identity systems. #### Recommended resolution interface For an indexer or verifier to move from an anchor's `(agentIdScheme = 0x01, agentId)` pair to a wallet address, the underlying registry SHOULD expose a function with the following shape: ```solidity interface IAgentWalletResolver { /// @notice Resolve a registry record id to its current wallet. /// @param agentId The 32-byte agentId payload carried in the anchor. /// @return wallet The current wallet address bound to that agentId, /// or address(0) if no binding exists. function getAgentWallet(bytes32 agentId) external view returns (address wallet); } ``` `getAgentWallet` is the minimal convention an indexer or verifier needs to move from an anchor's `(agentIdScheme = 0x01, agentId)` pair to an authoritative wallet for that agent. The recommendation is SHOULD, not MUST: registries may expose richer interfaces (e.g., [ERC-8004](./eip-8004.md)'s full agent record), and verifiers that only need to display an anchor without resolving identity can ignore the registry entirely. #### Off-chain resolution flow A typical off-chain resolution flow for a scheme `0x01` anchor is: 1. Read the `AnchorProof` event log from a node (chain-state-only). 2. Parse `(agentIdScheme, agentId)` from the event topics. 3. If `agentIdScheme == 0x01`, look up the registry the implementation has bound to scheme `0x01` (a tooling-side choice; this ERC does not encode the registry address on-chain in scheme `0x01`). 4. Call `getAgentWallet(agentId)` on that registry to obtain the current wallet. Each step is independent and any verifier may stop at step 2 if it only needs to display the anchor. #### Reference implementation A public reference implementation of `IAgentWalletResolver` is operated at `gateway.ensub.org` as part of the [ERC-8004](./eip-8004.md) ecosystem and can be used as a model for additional registries. ### Independent Verification This subsection describes how an anchor can be verified end-to-end using chain state alone, with no dependency on the issuing service, the indexer, or any hosted gateway. #### What "independent" means here Independent verification means a verifier reproduces the assertion implicit in the anchor — "this `proofHash` was committed at this block for this `(agentIdScheme, agentId)`" — from chain state only. It does *not* depend on the operator that issued the anchor, the indexer that surfaced it, or any HTTP gateway in between. #### Five-step verification path 1. Verifier receives: `observation` + an anchor reference (`txHash` + `logIndex`, or any equivalent locator). 2. Verifier computes `proofHash' = H(observation)` where `H` is the hash function declared by the profile (see *Recommended proofHash Constructions*). 3. Verifier reads chain state directly (RPC, archive node, light client) to fetch the `AnchorProof` event at the referenced log. 4. Verifier compares `proofHash'` with the `proofHash` recorded in the `AnchorProof` event at the referenced log. 5. `agentId` is resolved according to `agentIdScheme`: for `0x01`, resolution goes through a compatible registry per *Compatible Identity Registries*; for `0x02`, `agentId == keccak256(canonical agent URI)`; for `0x00`, no identity resolution applies. #### Layer optionality Steps 3–5 are independent. A verifier that only cares about commitment integrity stops at step 4. A verifier that cares about identity continues to step 5. A verifier that cares about how the observation was produced may consult an adjacent observation-commitment standard (e.g. OCP); this ERC takes no dependency on whether such a verifier exists. #### Verifier observability reference A live reference verifier and indexer for the resolution path described above is operated at `gateway.ensub.org`, and can be consulted as a worked example of the steps in this subsection. > A subtle but important nuance for implementers: an [ERC-8004](./eip-8004.md) `agentId` denotes *ownership* of an agent record, not *attestation* of any specific anchor. A registry hot key can rotate; an anchor was emitted at a specific block. Verifiers that need attestation-strength binding should treat the anchor as the layer of truth and the registry as an L3 lookup that may be re-keyed without affecting the L4 chain-state record. The trustless floor of the verification path is the chain-state read in step 3; everything above it is a convenience layer. ### OCP Boundary Note This ERC is a write-side standard: it defines how an anchor is emitted on-chain. The Observation Commitment Protocol (OCP) is a read-side standard: it defines how a verifier extracts and re-checks observation commitments. This subsection states the boundary so neither standard is presented as a precondition for the other. #### What OCP is, and what it is not > OCP does not define identity, authorship, sanitization, or data availability. It defines an extraction interface for observation commitments and a re-checkable digest profile over them. Composition with identity layers, signing layers, or anchoring layers is out of OCP's scope. > — Damon Zwicker (OCP author) #### Independence from OCP This ERC does not import an OCP verification interface, does not require OCP-compatible verifiers, and does not assume an OCP-shaped verifier is deployed. Every anchor is OCP-extractable by construction (the extraction rule is a function of the event topology already defined in *Canonical Event*), but anchors remain valid for verifiers that ignore OCP entirely and check `proofHash` against their own profile. #### Out-of-scope boundary | Concern | In scope | Handled by adjacent layer | |:--|:-:|:-:| | Event shape, scheme registry | ✓ | — | | Identity binding under scheme `0x01` | — | identity registry ([ERC-8004](./eip-8004.md) and compatible) | | Observation extraction & re-check digest | — | OCP | | Input-provenance / pre-image discipline | — | profile (see *Recommended proofHash Constructions*) | | Output verification (zk, attested compute, etc.) | — | inference-verification layer (e.g. [ERC-8004](./eip-8004.md)-adjacent work) | | Data availability of the observation | — | application choice (archival service, IPFS, off-chain log) | ### Recommended proofHash Constructions This appendix is **informative**. It catalogues recommended constructions for the `proofHash` field. The contract does not detect or enforce a profile; profile selection is an off-chain declaration carried by the issuer in `aux` (when present) or in a higher-layer envelope. #### Profile: Single observation digest (default) The simplest profile: `proofHash = H(observation)` where `H` is a collision-resistant hash function (e.g. SHA-256 or Keccak-256) and `observation` is the canonical-form byte representation of the agent's action payload. A verifier with the observation recomputes `H(observation)` and checks equality against the `proofHash` in the `AnchorProof` event. This is the default extraction rule that OCP, when consulted, applies to anchors emitted by this ERC. #### Profile: Input-provenance with identity sentinel For workloads where the verifier needs to bind the anchor not only to an observation but also to the input that produced it, the profile commits to a structured pre-image: ```text preImage = canonical_encode({ input: H(input_bytes), output: H(output_bytes), identity: identitySentinel // see below }) proofHash = H(preImage) ``` `identitySentinel` is a profile-level constant (recommended value: `IDENTITY_SENTINEL = keccak256("erc8263.identity.passthrough.v1")`) used to mark pre-images in which identity is intentionally pass-through rather than bound at the commitment layer. Verifiers MUST branch on the sentinel: when present, identity is resolved at the chain layer via `(agentIdScheme, agentId)` and the pre-image identity slot carries the sentinel value verbatim; when absent, identity is bound at the commitment layer and the slot carries the binding payload directly. This invariant prevents silent identity confusion across implementations of the same profile. #### Profile: Merkle batch root A single anchor commits to a batch of observations through the Merkle root of their individual hashes. A verifier of this profile recomputes the root from the leaves it cares about and checks inclusion against the on-chain `proofHash`. The contract is unaware that the `proofHash` is a Merkle root; the profile carries that semantics. #### Profile selection is off-chain Across all three profiles above, the on-chain `proofHash` field is the same opaque `bytes32`. The contract performs no profile detection. A verifier that does not know which profile produced a given `proofHash` cannot verify the anchor without out-of-band profile metadata; the profile is declared by the issuer, typically through an auxiliary field or through a higher-layer envelope. Future revisions of this subsection may add new profiles. The core contract is unaffected by any such addition. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).