Skip to content

Batch-settlement scheme specification for EVM#2051

Merged
CarsonRoscoe merged 7 commits intox402-foundation:mainfrom
phdargen:batch-settlement-specs
May 5, 2026
Merged

Batch-settlement scheme specification for EVM#2051
CarsonRoscoe merged 7 commits intox402-foundation:mainfrom
phdargen:batch-settlement-specs

Conversation

@phdargen
Copy link
Copy Markdown
Collaborator

@phdargen phdargen commented Apr 16, 2026

Summary

Introduces the batch-settlement payment scheme specification for EVM chains, following the network agnostic specification introduced in #1145 by @CameronWhiteside.

This scheme enables capital-backed, high-throughput, low-cost payments using stateless unidirectional payment channels. Clients deposit funds into onchain escrow once and sign off-chain cumulative vouchers per request. Servers batch-claim vouchers across many channels in a single transaction and settle earned funds with a separate sweep transfer, reducing per-request latency and gas costs.

Key Features

  • Fully gasless for client and server: All onchain transactions (deposits, claims, settlements, refunds, withdrawals) are relayed by the facilitator or any third party via EIP-712 signatures
  • Stateless unidirectional channels: Channel identity is deterministically derived from an immutable ChannelConfig struct (channelId = keccak256(abi.encode(config))), fully reconstructible from the 402 response for recovery after client state loss
  • Cumulative vouchers: Each voucher carries a monotonically increasing maxClaimableAmount; old vouchers are naturally superseded, eliminating the need for nonces
  • Dynamic pricing: Clients authorize a maximum per-request, and servers charge the actual cost (actualPrice <= amount) within that ceiling
  • Batched claims + sweep settlement: claimWithSignature aggregates claims across many channels (only onchain accounting, no transfers); settle sweeps claimed-but-unsettled funds to the receiver in one transfer
  • Pluggable deposit collectors: Deposit via eip3009 (e.g. USDC receiveWithAuthorization) or permit2 as a universal ERC-20 fallback
  • Flexible authorizer roles: payerAuthorizer supports fast ECDSA recovery (EOA) or EIP-1271 smart-wallet verification (zero address). receiverAuthorizer authorizes claims and refunds via EIP-712 and can be a server-owned address or a facilitator-provided delegate
  • Cooperative refunds + timed-withdrawal escape hatch: Instant refundWithSignature when the server cooperates; initiateWithdraw / finalizeWithdraw with a 15 min – 30 day configurable delay as unilateral fallback
  • Long-lived channel reuse: Channels can be topped up and reused after refund; config changes require a new channel.

Use Cases

  • High-frequency pay-as-you-go API requests
  • Streaming / metered API access (charge per token, data chuncks)

Links

x402BatchSettlement contract: #1950

Related work

Deferred scheme: #426

Session scheme: #2080

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 16, 2026

@phdargen is attempting to deploy a commit to the Coinbase Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the specs Spec changes or additions label Apr 16, 2026
@pr0toshi
Copy link
Copy Markdown

pr0toshi commented Apr 18, 2026

Why use this for state channels not #1907?

Hub route = one channel, N payees. This is the biggest structural advantage. An agent opens one channel and pays any hub-connected API. Batch-settlement is strictly unidirectional/pairwise, a new channel (and deposit) per payer↔payee pair. For an agent calling 20 services, that's 20 escrows vs 1.

Bidirectional Bidirectional channels let funds be used for agent↔agent, user↔agent, user↔user payments. claimWithSignature makes funds not usable for state channel payments.

Doing 1 channel payer>payee shows yall are missing the whole reason for using state channels for APIs, if an API only costs $0.001 then this fucks them in gas. Hub route lets 1 channel do any payee so 1 wei payments are fine.

@pr0toshi
Copy link
Copy Markdown

If receiver are the server to then do one channel, n payees theres no flow in the spec.

@pr0toshi
Copy link
Copy Markdown

pr0toshi commented Apr 18, 2026

Also

{
  "scheme": "batch-settlement",
  "network": "eip155:8453",
  "amount": "100000",
  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "payTo": "0xServerReceiverAddress",
  "maxTimeoutSeconds": 3600,
  "extra": {
    "receiverAuthorizer": "0xReceiverAuthorizerAddress",
    "withdrawDelay": 900,
    "name": "USDC",
    "version": "2"
  }
}

shows this is closed vs open source where anyone can have their own servers since there's no url. how does an API say where to sign with? the APIs could be server but then thats a new infra every API needs

@jithinraj
Copy link
Copy Markdown
Contributor

Separate from the hub-routing question, I think one thing needs to be explicit in the spec is signer discovery.

The voucher model is clear. The “where does the client learn the signer or authorizer it should bind to?” path feels implicit.

Even one simple rule would help: if the authorizer is not the default receiver path, surface it in the requirement payload in a way the client can treat as authoritative for that accept entry.

@Bortlesboat
Copy link
Copy Markdown
Contributor

Agree with @jithinraj's signer-discovery point. From the perspective of low-value paid API calls, I think this needs to be normative before SDKs copy the scheme.

A client should be able to answer, from the accept entry alone: "which authorizer am I binding this voucher to, and where did that authority come from?" A concrete rule could be:

  • extra.receiverAuthorizer is authoritative for this accept entry when present.
  • If omitted, the spec states the default unambiguously, e.g. receiver authorizer is payTo only for the default receiver-authorized path.
  • The requirement payload includes the EIP-712 domain fields the client must bind to (chainId, verifyingContract, name, version) and the configured withdraw delay.
  • If a facilitator-provided authorizer is allowed, mark the source explicitly (server, payTo, facilitator) so clients do not silently bind to the wrong party.

I would also love the spec to call out minimum receipt/observability fields for operators: channelId, previous/new cumulative amount, actual charged amount, recovery/correction reason when applicable, and claim/settle tx hashes when an onchain action happens. For tiny API payments, those fields are what let a merchant distinguish "client stopped after 402" from "voucher verified but settlement/claim failed".

@phdargen
Copy link
Copy Markdown
Collaborator Author

Hi @jithinraj @Bortlesboat, receiverAuthorizer is required and that field is all the client needs to know. there is no fallback to payto. receiverAuthorizer may be identical to payto but needs to be explicitly set

CarsonRoscoe
CarsonRoscoe previously approved these changes May 1, 2026
Copy link
Copy Markdown
Contributor

@CarsonRoscoe CarsonRoscoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree on making the authorizer explicit rather than falling back to payTo. Servers should return everything a client needs to fully rediscover a channel independently.

On the broader direction question: we looked at three prior state-channel proposals before landing here (including the cited model), so this wasn't a quick decision. The short answer is that we think bidirectional channels are a valid and interesting design, but they're solving a somewhat different problem, and we didn't want to conflate the two in a single scheme.

The specific use case we're targeting is: people who already use exact or upto and want a more economical version where they can defer and batch payments. That framing drove a few concrete choices:

  1. Unidirectional channels fit the buyer/seller flow naturally. In x402 the happy path is funds flowing one direction, client -> server. Unidirectionality means the server never needs to lock capital, which in turn means it never needs to host sensitive signing material for the channel itself. The receiverAuthorizer design is intentional here: every x402 scheme so far avoids requiring servers to keep signing keys on the hot path, and this one only slightly compromises by allowing you to EITHER bring singing material for a hot wallet (no funds required) OR use your facilitator's signer as your authorizer. Bidirectionality would break that property, requiring the keys with funds to be required by the server.

  2. Simpler off-chain state means simpler recovery. The only invariant the server tracks is chargedCumulativeAmount; the only invariant the client tracks is chargedCumulativeAmount + amount for the next voucher. State loss is well-defined in both directions, fall back to onchain totalClaimed. Bidirectional balance conservation (balA + balB == totalBalance) adds a harder-to-recover invariant and a dispute path that we don't need for this use case.

  3. No intermediary in the funds flow. The hub model is powerful for multi-payee fan-out, but it introduces a fee layer and a trust surface between the client and server. x402's design principle has been that the disclosed amount is exactly what flows from client to server, any income splitting happens in the periphery via payTo routing contracts. We wanted to preserve that here.

  4. Dynamic pricing is native. Since this is essentially "batched upto", the server charges actual cost up to a ceiling per request. With a hub/ticket model, amounts tend to be fixed at quote time, which doesn't map as cleanly to metered workloads.

None of this is a knock on the bidirectional approach, hub-routed bidirectional channels are genuinely useful for different scenarios (multi-payee fan-out, bidirectional flows, etc.) and could absolutely be their own scheme. We just didn't want to stretch batch-settlement to cover both use cases at the cost of the simplicity that makes it a practical upgrade path from exact/upto.

@pr0toshi
Copy link
Copy Markdown

pr0toshi commented May 2, 2026

Ah are we still able to get #1907 pulled then as it's the only reason havent deployed yet since need to know if its in the standard before use it.

Unidirectionality means the server never needs to lock capital, which in turn means it never needs to host sensitive signing material for the channel itself.
Bidirectionality would break that property, requiring the keys with funds to be required by the server.

This isnt the case, you might be thinking about LN. hub just credits into a channel and can move funds in if need, direct is pretty much just a 1 way until a pays, theres no reason b needs to lock funds or hub.

No intermediary in the funds flow. The hub model is powerful for multi-payee fan-out, but it introduces a fee layer and a trust surface between the client and server. x402's design principle has been that the disclosed amount is exactly what flows from client to server, any income splitting happens in the periphery via payTo routing contracts. We wanted to preserve that here.

x402s lets you do direct.

@CarsonRoscoe CarsonRoscoe merged commit c763d05 into x402-foundation:main May 5, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

specs Spec changes or additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants