ERC-7710 on x402: MetaMask's delegation rail, audited
Two audits ago we showed that x402's client-side spend cap forgets each payment the moment it signs. One audit ago we showed that Coinbase's onchain budget contract never touches the x402 payment path. This week we audited the one stack that actually wires the two layers together — and found it processing 20,009 delegation redemptions a week on Base while its own documented client setup rejects every offer it makes.
The stack is MetaMask's. It has three layers: ERC-7710, a Draft ERC from May 2024 that standardizes smart contract delegation; ERC-7715, a Draft from four days later that gives dapps a wallet_requestExecutionPermissions RPC method to ask a wallet for a scoped permission; and the Delegation Framework, the Solidity implementation that turns both into deployed contracts. On top of those, an npm package called @metamask/x402 plugs delegations into the x402 exact scheme as a third asset transfer method next to EIP-3009 and Permit2.
As usual in this series, everything below is primary-source: we cloned the repos, read the contracts, verified bytecode over public RPCs, scanned a week of mainnet events, and installed the published packages to drive them against real offers. Dates are as of August 26, 2026.
The framework: 38 caveats, one manager, 24 chains
The delegation-framework repo was created on July 3, 2024 (MIT and Apache-2.0, 221 stars, 117 forks, 299 commits). HEAD on main is 9e7fd80 from August 7, 2026 — a deployment to Arc testnet, one of the chains we covered in our payments-chains audit. The repo carries twelve audit PDFs in-tree: seven by Cyfrin (March 2025 through May 2026) and five by Consensys Diligence (June 2024 through April 2025). That is a materially heavier audit trail than the two Cantina reports Coinbase's SpendPermissionManager shipped with.
The core object is a six-field Delegation struct: delegate, delegator, authority (a hash pointing at a parent delegation, which is how chains of re-delegation work), an array of caveats, a salt, and a signature. Each caveat is a triple — enforcer, terms, args — where the enforcer is a separate stateful contract that gets a hook before and after execution. The framework ships 38 enforcers. The ones that matter for agent budgets: ERC20PeriodTransferEnforcer (a per-period allowance), ERC20StreamingEnforcer (linear streaming allowance), MultiTokenPeriodEnforcer, and their native-token twins.
We read ERC20PeriodTransferEnforcer closely, because it is the exact counterpart of what we audited in Coinbase's SpendPermissionManager. The terms are 116 packed bytes: token address, periodAmount, periodDuration in seconds, startDate. The window is a fixed calendar indexed from startDate, not a rolling window — unused allowance is forfeited at each boundary, and the same boundary property applies: an agent can spend up to 2x the period amount straddling a period edge. Where Coinbase stores accounting inside the manager, MetaMask stores it inside each enforcer, keyed by (delegationManager, delegationHash). The enforcer trusts whatever manager calls it, which is what lets the same budget contracts serve any ERC-7710-compatible manager.
Deployment mirrors the pattern we measured last week, down to the tooling: CREATE2 with the salt "GATOR", one address per contract across every chain. DelegationManager v1.3.0 lives at 0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3 on 24 mainnets according to the @metamask/delegation-deployments package — including Tempo and Robinhood Chain, the agent-branded chains from our earlier coverage. We pulled the bytecode over public RPCs on Base, Optimism, Arbitrum and Linea: identical 11,503-byte runtime on all four, and a Base-vs-Optimism diff of exactly 34 bytes — the cached EIP-712 domain separator (32 bytes) plus the chain id (2 bytes). That is byte-for-byte the same diff signature we found between Coinbase's deployments. One curiosity: the repo tags v1.0.0 through v1.3.0, but the deployments package maps only 1.0.0, 1.1.0 and 1.3.0 — 1.2.0 (January 2025) never shipped as a supported deployment set.
The wire: a spec section the monorepo never implemented
The x402 side of this story is in the spec, not the code. Section 3 of scheme_exact_evm.md in the x402 monorepo defines assetTransferMethod: "erc7710" as the third transfer method for the exact scheme. The payment payload is three fields — delegationManager, permissionContext, delegator — and nothing else. No signature over the payment, no nonce, no validity window: the delegation inside permissionContext is the authorization, and the facilitator settles by calling redeemDelegations with an encoded transfer(payTo, amount).
The verification model is unusual enough to quote: verification "is performed entirely through simulation," and the simulation "serves as the sole verification mechanism — no trusted list of Delegation Manager implementations is required." The spec is honest about what that buys: a client can invalidate its delegation between simulation and settlement, so the facilitator eats the gas of a failed transaction; and a malicious delegation manager can be a gas trap, so facilitators must cap gas per redemption. Facilitator risk, not seller risk.
The section's history says a lot about where x402 governance stands. It entered the spec through PR #732, opened by Dan Finlay — MetaMask co-founder and co-author of both ERCs — on December 8, 2025, and merged March 13, 2026. His follow-up, PR #1837, "First draft of ERC-7710 exact scheme implementation" (14 files, +1,470 lines), was opened March 26 and has not been touched since the day it was opened. Five months later, the monorepo at HEAD bde6fe5 contains zero occurrences of redeemDelegations and zero of erc7710 outside the spec file. The implementation shipped elsewhere: @metamask/x402 1.0.0, published August 13, 2026 — 308 lines of TypeScript across five files, deliberately importing no x402 packages and typing everything structurally so it can sit on either side of the version churn.
The live rail: one facilitator, three signers, 20,009 redemptions
MetaMask runs its own facilitator for this method — an endpoint family called tx-sentinel. The Base mainnet instance answers GET /supported with 16 kinds, every one of them scheme exact with assetTransferMethods: ["eip3009", "permit2", "erc7710"], across networks including Ethereum, Base, Polygon, Arbitrum, BSC, Sei, Monad, MegaETH and Tempo, plus a gasPayment extension. Two details stood out. First, the production response advertises the team's dev signer address, while the three production signers appear only in the SDK source, in a constant named METAMASK_FACILITATOR_ADDRESSES. Second, the supported list includes eip155:1337 — the default local development chain id — served from a production endpoint.
Then we measured the chain. Over Base blocks 50,170,490 to 50,472,890 — 302,400 blocks, roughly August 19 to 26 — the DelegationManager at 0xdb9B…dB3 emitted 20,011 events: 20,009 RedeemedDelegation, 2 DisabledDelegation, and zero EnabledDelegation, spread over 19,865 transactions from 7,233 distinct root delegators and 146 distinct redeemers.
For scale: the same week-long window measured in last week's audit caught 547 spends through Coinbase's SpendPermissionManager on the same chain. MetaMask's rail is running at roughly 36x that volume. The three signer EOAs have lifetime Base nonces of 235,571, 235,566 and 235,838 — about 707,000 settlement transactions all told, though that lifetime figure covers all three transfer methods the facilitator settles, not just erc7710. The rest of the redeemer table is small: Multicall3 accounts for 1,225 redemptions, and no other single redeemer breaks 41.
The caveats that make a budget — and the default that skips one
How does a payment become a budget? Through the delegation chain. In the direct flow, an agent's smart account signs one delegation per payment. In the recurring flow — the interesting one — a human grants an ERC-7715 permission of type erc20-token-periodic to a session key; the session key then re-delegates to the facilitator per payment, carrying the parent's caveats with it. The period enforcer accumulates spending onchain across every payment redeemed under that permission. This is precisely the composition that did not exist anywhere in the stack we audited on August 24: the per-payment protocol cap and the cumulative onchain budget, enforced in the same transaction.
We also read what the SDK actually puts in a per-payment delegation, in createx402DelegationProvider (exported from a path literally named experimental). Every delegation gets a transfer-amount scope capped at the offer's exact amount, plus a caveat pinning the ERC-20 transfer recipient to the seller's payTo via calldata matching. But the delegation is an open delegation — redeemable by anyone — and the redeemer restriction is only added when the server's requirements echo the facilitator's addresses. The expiry caveat is only added if the integrator explicitly configures expirySeconds; nothing defaults it and the offer's maxTimeoutSeconds is not used. So the default artifact is a payment authorization for an exact amount to a fixed payee that never expires — a sharp contrast with EIP-3009's mandatory validBefore. Funds can only ever reach the intended seller, but a stale delegation can be redeemed at any point in the future by anyone who kept the bytes, charging the buyer when they no longer expect it.
The drive: defaults that reject MetaMask's own rail
Finally, we did what this series always does: installed the published packages — @x402/core 2.23.0 and @metamask/x402 1.0.0, both from npm — and drove them. MetaMask's documentation, and the agent-facing SKILL.md the repo ships for AI coding assistants, both give the same setup: new x402Client().register('eip155:*', erc7710Client).
That setup cannot pay anything today. A $0.50 offer in USDC on Base — a default asset, well under the $1 default cap we audited two days ago — is rejected with All payment requirements were rejected by spendControls. The reason is structural: since 2.23.0, the core client decides whether an asset is a recognized stablecoin by asking the scheme client's findDefaultAsset, a method the interface marks optional and x402Erc7710Client does not implement. No method, no recognized assets, no payable offers — the exact failure mode for third-party scheme clients that our spend-controls audit flagged in the abstract. Here it is in the concrete, against the highest-volume external adopter x402 has.
The timeline is unforgiving. @metamask/x402 1.0.0 shipped August 13 with a peer dependency of @x402/core ^2.12.0. Core 2.23.0, the release that turned spend controls on by default, shipped August 18 — five days later, inside that caret range. The string spendControls appears zero times in the smart-accounts-kit repo, code or docs. Anyone following MetaMask's current quickstart against MetaMask's own live facilitator gets a client that rejects every offer the facilitator makes.
The escape hatches work, with one trap. setSpendControls(false) unblocks payment, and so does building the client with x402Client.fromConfig({...}) — through which we also confirmed the other edge: with spendControls: false, our test client signed a delegation payload authorizing 1,000,000 USDC without complaint. But passing that same config object to the constructor — new x402Client({ spendControls: false }) — is silently accepted and silently ignored, because the constructor takes a selector function, not a config. A wrong-typed argument that changes nothing and throws nothing is exactly the kind of edge an LLM-written integration will hit.
What it means for LLM4Agents
This is the first production rail where the answer to "what has this agent already spent?" lives onchain and inside the x402 payment path. That matters to us three ways. First, as a buyer-side option: LLM4Agents wallets settle via EIP-3009 today, which caps each payment but accumulates nothing. An erc7710 flow with a periodic caveat gives a fleet operator a per-agent budget that a compromised agent cannot exceed no matter how many under-cap payments it signs — the failure mode we have now documented three times. Second, as a seller-side obligation: offers carrying assetTransferMethod: "erc7710" already exist in the wild, and a gateway that publishes or proxies offers needs to either pass the field through cleanly or strip it deliberately, not mangle it. Third, as a warning we already knew in miniature: every layer we add above the protocol — spend controls, budgets, delegation scopes — ships with defaults, and defaults from different vendors now demonstrably break each other five days apart. The 87.6% number cuts both ways, too: the rail is real, but it is one vendor's facilitator redeeming one vendor's delegations through one vendor's SDK, end to end.
Staying on the frontier
Concrete steps, in order. One: add erc7710 offer handling to our client stack behind an explicit opt-in — implement findDefaultAsset in any scheme client we ship, because we now have empirical proof of what its absence does under 2.23.0 defaults. Two: pin @x402/core exactly, not with a caret; the last two behavioral breaks both arrived inside minor versions, and this audit caught a major wallet vendor absorbing one. Three: prototype a periodic-budget flow on Base Sepolia against tx-sentinel — one session key, one erc20-token-periodic permission, N payments — and measure where the onchain accounting diverges from our gateway's metered billing, the seam our cost-observability audit showed nobody standardizes. Four: always set expirySeconds when creating delegations, and treat any delegation without a timestamp caveat as a standing liability in wallet hygiene reviews. Five: watch PR #1837 and the facilitator field — the day a second, non-MetaMask facilitator advertises erc7710, delegation-based payment stops being a vertical stack and becomes an interoperable rail worth first-class support.
Give your agents budgets, not just balances
LLM4Agents runs stablecoin-funded wallets with per-call caps on an OpenAI-compatible gateway — and we audit the budget layers before they reach your fleet.
Register your agent