Verified Agent Rails

World proved who the agent is. We enforce what it can spend, at the asset layer. Live on Arc.

Verified Agent Rails

Created At

ETHGlobal New York 2026

Project Description

Verified Agent Rails (VAR) is the missing leash for agentic finance: scoped, revocable spending authority for AI agents, enforced by the asset itself.

The problem: an autonomous agent can't safely touch compliant finance. Nothing links it to an accountable human, and nothing stops it from overspending. ERC-8004 and World's AgentKit answer "who is behind this agent," but no standard answers "what is this agent allowed to spend," enforced where the money actually moves.

VAR closes that gap. A human verifies once with World ID (orb-verified) and registers their agent on World Chain via AgentKit. An attestor then signs an EIP-712 mandate that scopes the agent's authority: a per-transaction cap, a cumulative per-period cap, an expiry, an allowed asset, and instant revocability. A compliant token (GatedUSD on Arc) routes every transfer through a single on-chain view call, checkTransfer, and refuses to move unless the agent holds a valid mandate, reverting with a machine-readable reason (OVER_CAP, OVER_PERIOD_CAP, REVOKED, EXPIRED). The agent ships no kill switch; revoke flips one storage slot the asset already reads, so the next transfer reverts by the following block.

The full arc is proven live on Arc testnet with real components: real World ID identity, a real Dynamic MPC agent wallet signing real on-chain spends, and attestor-signed mandates enforced on-chain. Grant, gated payment, over-cap block, over-period block, and revoke all verified as real transactions. Three roles stay cleanly separated: the verified human (principal), the agent, and the attestor.

VAR composes with ERC-8004 as the asset-layer spending primitive the agent-standards stack is missing, and will be published as an open spec submitted through the EIP process.

How it's Made

Contracts (Foundry, Solidity, OpenZeppelin 5, on Arc testnet). The core is two contracts. DelegationMirror is the registry and policy engine: it holds each agent's mandate (principal, World ID nullifier reference, per-tx cap, per-period cap, expiry, allowed token, revoked flag) and exposes checkTransfer, a single view that returns a machine-readable reason code. GatedUSD is an ERC-20 whose every transfer routes through that gate in its _update hook, reverting on a non-OK code. We implement the ERC-7943 canTransfer surface so the eligibility check is a standard interface, not a bespoke one.

The security model is the part we're proud of. Mandates can only be created by an attestor's EIP-712 signature with a strictly-increasing per-agent nonce. There is no permissionless path, which kills an agent-squatting attack where a third party could otherwise register an unclaimed agent or reopen a revoked mandate with their own caps. We caught and fixed that during the build and wrote a regression test that performs the attack and asserts it reverts.

Cumulative caps without a second contract. Per-period spend is accumulated by having GatedUSD call recordSpend on the mirror after a successful transfer, authorized by msg.sender == the mandate's own allowedToken. That binds recording authority to the already-attestor-signed mandate, so there's no separate token registry and no extra owner-managed surface. The view and the writer share one period-rollover helper so checkTransfer and recordSpend can never disagree.

Partner tech, and where it earned its place. World ID (orb) plus AgentKit's AgentBook on World Chain is the root of trust: lookupHuman resolves a human-backed agent, and that becomes the mandate's proofRef. Dynamic provides the agent's wallet, a real MPC server wallet that signs every on-chain spend (not a deployer key), which is what makes "autonomous agent" credible rather than scripted. Circle's Arc is the settlement layer, with the compliance decision as one on-chain view on the hot path.

The hacky/hard-won bits. AgentKit's CLI is hardcoded to World Chain, so a proof generated there reverts NonExistentRoot on Base Sepolia (the root was never bridged): we proved this empirically and moved identity to World Chain, keeping enforcement on Arc, with our backend as the bridge between them. Arc's USDC is gas at 18 decimals but ERC-20 at 6 (same balance, two interfaces), so we wrapped all amounts in a parseUSDC helper and banned raw parseUnits, because parseUnits("1", 18) on an ERC-20 transfer would send a trillion dollars. Tests: 54 passing including invariant and fuzz suites; the full grant/gate/pay/over-cap/over-period/revoke arc verified as real on-chain transactions.

background image mobile

Join the mailing list

Get the latest news and updates