ERC-8312 · Standards Track · ERC

Bind agent intent to a verifiable state cursor, across every call.

A standard interface for on-chain envelopes — records that meter the authority an autonomous agent consumes across many actions and across protocols, not just one call at a time.

Authors: Matthias Hauser (@0x2kNJ), Simon Brown (@orbmis)

The Problem

No shared object binds later actions to aggregate protocol state.

Ethereum has rich standards for authorization, but they assume delegation to a single agent and a single dApp. Modern systems may have multiple agents using a wide range of standards, executing dynamic workflows across multiple protocols.

ERC-8312 is delegation for the modern era: it allows agents to be maximally autonomous while maintaining robust guardrails, acting as a single, shared substrate that connects upstream standards to downstream protocols in a way that maintains an agent's bounds over time.

Upstream zone

Authorization & routing

  • ERC-4337 — how a UserOperation reaches the chain
  • ERC-7579 — how a modular smart account composes modules
  • ERC-7710 — how a delegation is structured and redeemed
  • ERC-7715 — how a dApp requests a scoped permission
  • ERC-8211 — how operations are composed and batched
Envelope principal · capabilityRoot
cursor · expiry · status
Downstream zone

Invariant-maintaining contracts

  • A remaining budget across protocols
  • An identity binding that must hold
  • A reputation score updated over time
  • A privacy commitment or nullifier set
  • A dispute or contestation state

Without a shared primitive, every upstream system needs bespoke integration with every downstream substrate.

O(upstream × downstream) collapses to O(upstream + downstream)

Upstream systems create or reference envelopes; downstream substrates implement the state and validation semantics behind the cursor.


The Solution

An envelope is a written cursor — not a recomputed predicate.

An aggregate bound can be a predicate re-checked from public state, or a counter each action advances. A protocol using ERC-8312 can even allow two or more agents to co-create mutual delegation logic, moving the delegation logic to the asset itself, and away from any one specific account.

01

Meters authority, not calls

An envelope records how much of a bounded mandate an agent has consumed across all actions — where no single protocol maintains the aggregate.

02

A write supplies the order

advanceCursor records against one object, so two concurrent draws cannot both pass a bound their sum would exceed. A read cannot reproduce that serialization across surfaces.

03

Auditable by anyone

The public EnvelopeAdvanced log lets any party recompute whether cumulative consumption ever exceeded the cap — with no trust in the registry.

A narrow docking surface, not a substrate. The interface defines seven functions and three events. It deliberately omits capability issuance, intent submission, execution routing, and auditing. Different implementations compete on cursor semantics, proof systems, invariants, and trust assumptions while exposing the same surface.


Concepts

Four pieces: envelope, reference, substrate, registry.

Two terms carry the weight — the cursor, the advancing aggregate-state commitment, and the substrate, the implementation that maintains and validates it.

Envelope

An on-chain record a principal registers at the start of a bounded course of action: an id, a principal, an immutable capabilityRoot, a mutable cursorRoot, an expiry, and lifecycle status.

Envelope reference

A interoperability type — (chainId, registry, id) — that can be passed across applications and chains, letting many consumers query and update the same registry-maintained state.

Substrate

The contract system that maintains the state a cursor commits to and validates cursor advances. A budget substrate tracks spend; identity, reputation, privacy, and contestation substrates maintain their own invariants. Anyone can create a substrate.

Registry

A substrate-specific contract that stores envelopes and implements IBoundedAgentAction. Its address identifies both the envelope namespace and the implementation responsible for validating cursor transitions.


The Interface

Seven functions. Three events. One state machine.

A conforming implementation MUST implement IBoundedAgentAction and support ERC-165 discovery. The cursor representation, witness format, and enforced invariant are left to the substrate.

IBoundedAgentAction.sol
// SPDX-License-Identifier: CC0-1.0
interface IBoundedAgentAction is IERC165 {
  enum Status {
    None, Active, Completed,
    Contested, Revoked, Expired
  }

  struct Envelope {
    bytes32 id;
    address principal;
    bytes32 capabilityRoot;  // fixed
    bytes32 cursorRoot;      // advances
    uint64  createdAt;
    uint64  expiresAt;
    Status  status;
  }

  // — reads (side-effect free) —
  function getEnvelope(bytes32 id)
    external view returns (Envelope memory);
  function getCursor(bytes32 id)
    external view returns (bytes32);
  function isActive(bytes32 id)
    external view returns (bool);

  // — writes —
  function registerEnvelope(
    address principal,
    bytes32 capabilityRoot,
    uint64  expiresAt,
    bytes calldata initData
  ) external returns (bytes32 id);

  function advanceCursor(
    bytes32 id,
    bytes calldata witness
  ) external returns (bytes32 newCursor);

  function setStatus(bytes32 id, Status s)
    external;
}

registerEnvelope

Creates a unique envelope with status Active. If the principal is not the caller, the implementation MUST verify authorization (EIP-712, ERC-1271, or an upstream grant) and revert otherwise.

advanceCursor

Atomic with the substrate state it represents. Rejects non-Active or expired envelopes. Validates an opaque witness bound to at least (id, prevCursor) so it cannot be replayed. Never open to arbitrary callers.

getCursor · isActive

Reads that fold expiry into their result: a stored Active status on an expired envelope cannot mislead a consumer. Unknown ids MUST revert, never return a zero value.

setStatus

Transitions follow the lifecycle state machine and emit EnvelopeStatusChanged. The metered party MUST NOT be able to move an envelope out of Active to escape an unmet bound.

ERC-165 discovery

0x3985961d for the base interface, 0x021ca455 for the Budget Substrate, 0xe664d441 for the Contestable extension. A consumer confirms a named profile before interpreting a cursor it did not define.


Composability

Docks with the standards already shipping.

Carry the registry address together with the envelope id — and use (chainId, registry, id) when the reference may leave the current chain context. Non-normative integration patterns:

Final
ERC-7710

A caveat enforcer carries a reference in its terms and advances the cursor in its afterHook — only when execution actually occurs.

Final
ERC-7715

A wallet returns an envelope reference as opaque permission context; a dApp queries the registry before exercising it.

Final
ERC-4337

A paymaster gates aggregate budget, rejecting operations against inactive or exhausted envelopes. Reading a cursor in validation requires staking.

Final
ERC-7579

A modular smart account installs a module that queries or advances an envelope during execution — as a consumer, not an author of cursor semantics.

Final
ERC-7521

An intent flow carries the reference as bounded-action context; solvers query and advance the cursor as part of atomic settlement.

Draft
ERC-8211

A fetcher or batch step queries getCursor for aggregate state used across a composed execution flow.

Draft
ERC-8004

A registration or validation request associates agent identity or validation activity with substrate-maintained state.

Draft
ERC-8274

An inference-proof verification result becomes the witness for advanceCursor: per-action verification, aggregate metering.

HTTP
x402

An integration includes an envelope reference in an implementation-defined HTTP field to bind a payment request to an aggregate budget.


Budget Substrate Profile

A ready-made budget substrate you can implement today.

Any registry that advertises the budget profile must follow it. That way, reading the cursor always means the same thing — how much of the agent's spending limit is left — no matter which registry you ask.

IBudgetSubstrate.sol
interface IBudgetSubstrate is IBoundedAgentAction {
  // capabilityRoot = keccak256(cap, asset)
  function bound(bytes32 id)
    external view returns (uint256 cap, address asset);

  // cursorRoot = keccak256(spent)
  function spent(bytes32 id)
    external view returns (uint256);

  // cap - spent, or 0 if not active
  function remaining(bytes32 id)
    external view returns (uint256);
}

spent + amount ≤ cap

The witness is abi.encode(amount, authorization). advanceCursor rejects the advance unless it stays within the cap, then sets spent to spent + amount and recomputes the cursor.

Monotonic & bounded

A budget registry MUST maintain spent ≤ cap while Active, and spent MUST be monotonically non-decreasing — the interoperable guarantee consumers rely on.

=

Typed & committed agree

keccak256(cap − remaining(id)) MUST equal getCursor(id) while Active — the accessor and the commitment cannot diverge.

Zero is ambiguous

A remaining of zero denotes either an exhausted bound or an inactive envelope; a consumer MUST consult isActive to tell them apart.

Help shape the standard.

ERC-8312 is a Draft. Read the specification, review the reference implementation, and share feedback on Ethereum Magicians.