Skip to content

x402: serve before settling, so a stock client gets low latency #1457

Description

@asim

The problem

Every x402 request does verify then settle against the facilitator, and settle is an on-chain transaction. So every single request carries a settlement round trip. An agent making fifty calls pays fifty settlement latencies, which makes sustained use of the tools impractical however cheap each call is.

What x402 actually offers

Checked against the specs rather than assumed — both of my first guesses were wrong.

upto — one authorization per request, single use. From the spec: "Each authorization MUST be settled at most once. After settlement (regardless of amount), the authorization is consumed and cannot be reused." What it gives you is that the settled amount may be less than the authorized ceiling: at verification the amount field is the maximum, at settlement it is "the actual amount to settle".

So upto is the primitive for metered pricing, not for batching. It belongs with the pricing-units work, not here.

batch-settlement — many separate per-request commitments settled together. The client still signs every request. What changes is that the server validates the commitment, stores it, serves immediately, and value moves later through "the network binding's redemption process". That genuinely removes the per-request settlement latency.

But the spec is a framework, not a mechanism: the redemption step is explicitly "network-defined", and the only concrete binding published is Cloudflare's. CDP advertises the scheme on Base; what binding it means there is an open question and should be asked before building anything.

The spec names two backing models — capital-backed (on-chain escrow, a payment channel, delegated spending authority) and credit-backed (a trusted intermediary holds a billing account and settles off-chain).

Rejected: a deposit endpoint

The first design was a deposit endpoint — the agent funds x402:<its address> once, then authenticates per request with Authorization: Wallet and spends credits with no on-chain work. Most of it already exists: the credit ledger is keyed by an arbitrary account id so x402:0xabc… can hold a balance, walletauth proves control of an address without payment, and callerIdentity already collapses signer and payer to the same id.

Rejected because standard clients cannot use it. A stock x402 client knows one thing: hit a URL, get a 402, sign, retry with X-PAYMENT. It will never know to call a deposit endpoint, and it will never send Authorization: Wallet instead of paying. That design makes our own agent fast and leaves everyone else exactly where they were — a Mu feature wearing x402's clothes.

It also makes us custodial, and we are not doing withdrawals, so the balance would be one-way.

Proposed: batch on the server, client unchanged

Keep the stock flow exactly as it is — 402, sign, X-PAYMENT. Change only what happens on receipt:

  1. Verify the signature locally. wallet/recover.go has secp256k1 recovery and SignX402Payment already builds the EIP-712 digest, so the authorization can be checked without a facilitator round trip.
  2. Check the payer can cover it — one RPC balance read, cacheable per address.
  3. Serve immediately.
  4. Settle asynchronously, in batches.

Request-path latency drops to a local signature check. A stock client gets that for free, having changed nothing.

This is batch-settlement's accumulate-then-redeem pattern with the redemption implemented here rather than waiting for a binding.

The cost, stated plainly

We serve before we are paid. A signature can fail to settle afterwards — insufficient balance by redemption time, or a reused nonce.

Mitigations, all ordinary:

  • cache payer balances and refuse when the balance will not cover outstanding commitments
  • cap total unsettled value per payer
  • stop serving an address whose settlements start failing
  • bound how long a commitment may sit unsettled, since authorizations carry a deadline

The real decision is accepting a bounded amount of credit risk in exchange for stock clients getting low latency with no deposit, no custody, and no bespoke protocol.

Before building

  • Ask CDP what batch-settlement on Base actually binds to. If they have defined the redemption path, use theirs rather than inventing one.
  • Decide the outstanding-value cap per payer, and what happens when it is hit.
  • Confirm local verification matches what the facilitator's /verify accepts, so we never serve something it would have rejected.

Not in scope

Metered pricing and the upto scheme. Same conversation originally, genuinely separate problem — tracked separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions