← Blog
August 4, 2026 · 13 min

One 402, fifteen chains: inside x402's network bindings

The x402 spec that defines what "pay exactly this amount" means is 519 words long. The fifteen documents that explain how to encode it on a specific chain run past 31,000.

That ratio is the most honest description of x402 available. The protocol markets itself as HTTP-native and chain-agnostic, and at the semantic layer that is true. At the layer where money actually moves, every blockchain forces a different answer to the same four questions, and the answers do not compose.

We cloned x402-foundation/x402 on 2026-08-04 (HEAD db9dabd0) and read every network binding in specs/schemes/exact/. This is what is in there, what it costs to support, and where the repository contradicts itself.

The shared spec is half a page

The root exact document says that the scheme "transfers a specific amount of funds from a client to a resource server" and that the server must know the amount in advance. Its example use cases are paying to view an article, purchasing digital credits, and "an LLM paying to use a tool." That is the entire semantic content.

Everything else is delegated. And the delegation is where the first inconsistency shows up: the root document closes by pointing at six per-network files — SVM, Stellar, EVM, Sui, TON, Starknet — while the folder next to it holds fifteen. Its "Critical Validation Requirements" appendix, the closest thing to a normative summary of what a facilitator must enforce, covers four of them.

The other eleven bindings carry their rules in their own files, unsummarized. A facilitator author reading top-down gets a map of roughly a quarter of the territory.

The four invariants every binding has to satisfy

Strip the chain-specific vocabulary and each of the fifteen documents is solving the same problem set.

First, the payer decides where the money goes. The facilitator broadcasts the transaction and must be structurally incapable of redirecting it. Second, the facilitator pays gas and must not be drainable by a hostile client. Third, exactness: the transferred amount must equal requirements.amount, not approximate it. Fourth, replay and expiry protection that does not require the facilitator to keep state — because the facilitator is supposed to be a stateless verify/settle service, as we covered in the facilitator API deep dive.

On EVM there is a token-level primitive that solves all four at once: EIP-3009 transferWithAuthorization. The payer signs a typed message naming recipient, amount, validity window and nonce; the token contract verifies it and moves the funds; anyone can submit it and pay gas.

Off EVM, that primitive almost never exists. So each chain reaches for whatever its virtual machine already gives it, and the results sort into five families.

Family one: the token contract checks a signature

The closest analogues to EIP-3009 are literal ports of it. Casper's binding is built on CEP-3009, described in the spec as "Casper's adaptation of EIP-3009 for CEP-18 tokens": an EIP-712 typed message carrying recipient, amount, validity window and a 32-byte nonce, with the token contract recomputing the digest and enforcing nonce uniqueness.

Starknet gets there through account abstraction instead of through the token. The client signs a SNIP-9 outside execution authorizing exactly one transfer call, encoded as SNIP-12 typed data, and the facilitator triggers it via execute_from_outside_v2 on the client's own account contract. Signature validity is delegated to the account's SNIP-6 is_valid_signature, so multisig and hardware-backed accounts work unmodified. SNIP-9 nonces are single-use and enforced on-chain, which means the facilitator needs no persistent replay store at all.

The tell — Starknet's binding is the longest of the fifteen at 4,442 words, and it is also the newest, created 2026-07-24. Account-abstraction chains produce more spec, not less, because the authorization surface is programmable.

Family two: a transaction with an empty sponsor slot

The most common pattern is a partially signed transaction. The client builds and signs the whole thing, leaving a designated slot for whoever pays the fee, and the facilitator fills it at settlement.

Solana does this with extra.feePayer, which we took apart in the SVM exact deep dive. Aptos uses its native fee payer transactions, and the spec is explicit about why this is safe: "the client's signature covers the transaction payload but not the fee payer address," so the sponsor can only append. Hedera has the client set transactionId.accountId to the facilitator's account and sign a partially signed TransferTransaction. Concordium does the same with a sponsor signature slot left empty, then waits for ConcordiumBFT finalization, which the spec puts at roughly ten seconds of deterministic finality.

The security burden of this family lands on gas pricing. Aptos states it as a MUST: verify that gas_unit_price is under a configured bound, "to prevent a malicious client from setting an arbitrarily high gas price that drains the fee payer's account." A sponsor slot is an open invitation unless someone bounds the cost.

Family three: meta-transactions and relays

NEAR's binding is the most rigorous document in the folder. The client signs a NEP-366 SignedDelegateAction containing exactly one action, which must be a FunctionCall to ft_transfer. The relayer account is chosen from facilitator-local configuration and must never come from client input.

The detail worth stealing is the timeout mapping. Rather than leave maxTimeoutSeconds to interpretation, the spec pins estimatedBlockSeconds = 1 and requires the facilitator to reject both an expired window and a window wider than the advertised budget. It also documents that NEAR's public RPC has no equivalent to eth_call or simulateTransaction for the delegate path, so it replaces simulation with a list of targeted chain-state preflight checks — account exists, access key exists, nonce not consumed, ft_balance_of sufficient, NEP-145 storage registered for the recipient — and requires all of them to fail closed.

TON reaches the same place through a wallet contract. The client signs a W5 wallet message with the internal_signed opcode; the facilitator wraps it in an internal message from its own funded wallet. The spec quantifies the subsidy at roughly 0.013 TON per transaction and states there is no relay commission: "the facilitator absorbs gas costs as the cost of operating the payment network, analogous to how EVM facilitators pay gas for transferWithAuthorization."

Family four: sign the authorization, not the transaction

Stellar splits the difference. The client signs Soroban authorization entries rather than a transaction, and the facilitator rebuilds and submits the transaction around them. The scope is narrow by design: SEP-41 Soroban tokens only, classic Stellar assets explicitly excluded.

The invariants show up as structural constraints. Exactly one invokeHostFunction operation. Function name transfer with exactly three arguments, where argument one must equal payTo and argument two must equal requirements.amount as an i128. Authorization entries must use sorobanCredentialsAddress only and must contain no sub-invocations. And expiry is a formula, not a guess: entries must not exceed currentLedger + ceil(maxTimeoutSeconds / estimatedLedgerSeconds), with a fallback of five seconds.

Family five: atomic groups and signed blocks

Algorand does not send a transaction. It sends a paymentGroup — an array of base64 transactions executed atomically — plus a paymentIndex pointing at the one that actually pays the resource server. Groups cap at 16 top-level transactions and fees can be pooled, which is how the facilitator's fee transaction rides along in the same group. The other slots can carry swaps or asset movements; the facilitator only has to prove that the indexed transaction matches the requirements.

Keeta sends a base64 ASN.1 DER signed block containing a SEND operation. The facilitator computes and signs a separate fee block, collects votes from the network's representatives, and publishes the combined vote staple.

The three chains that break the promise

Two bindings do not deliver the thing agents actually want, which is a payment that requires no native gas token.

Sui requires the client to form and sign a complete transaction. The spec is blunt about the consequence: the facilitator has "no ability to adjust the transaction." Gasless payment is possible only through an interactive gas station handshake advertised at extra.gasStation, where the client sends a partial transaction, gets gas fields filled in, then signs. The appendix pins hope on an in-development "Address Balances" feature that would remove the coin-object storage cost and might eventually enable EIP-3009-style authorizations.

XRPL is the hard case. The ledger charges the fee to the transaction Account, full stop. The spec states that this scheme "does not support facilitator-sponsored network fees" and that supporting them "would require a different payment model, not only a facilitator implementation change." It then makes the limitation machine-readable: extra.areFeesSponsored MUST be present and MUST be false.

XRPL also inherits a sequencing problem. The default sequence method serializes the payer account between verify and settle — any other transaction consuming the same sequence number permanently invalidates the payment with tefPAST_SEQ, after the resource handler has already run. The alternative, ticketSequence, allows concurrent pending payments but needs a ticket inventory and a reserve. The spec calls the mitigation cooperative, "not a protocol-level reservation."

Cardano is the third outlier, in a different direction: its binding defines three asset transfer methods, one of which routes payments through the Masumi protocol for refund mechanics and decision logging, and another of which pays to scripts with parameters applied at transaction build time. It is the only binding where "pay the merchant" is not the only shape.

Where the abstraction lives in code

The repository resolves fifteen chains into three TypeScript interfaces. A network binding is a package that implements them.

// typescript/packages/core/src/types/mechanisms.ts

export interface SchemeNetworkFacilitator {
  readonly scheme: string;

  // "eip155:*" | "solana:*" — groups signers by chain family
  readonly caipFamily: string;

  // SVM returns { feePayer }, EVM returns undefined
  getExtra(network: Network): Record<string, unknown> | undefined;

  // array, for load balancing and key rotation
  getSigners(network: string): string[];

  verify(payload, requirements, context?): Promise<VerifyResponse>;
  settle(payload, requirements, context?): Promise<SettleResponse>;
}

The client side is a single method, createPaymentPayload. The server side converts a price string into the chain's asset and amount. That is the whole plug-in contract, and it is why getExtra matters more than it looks: it is the channel through which a chain tells the buyer that a fee payer exists at all.

What the audit turned up

Reading the folder against the code produced five discrepancies, all checked on 2026-08-04.

// Finding 1

Fifteen specs, eleven SDKs, two in Go

The TypeScript tree ships eleven mechanism packages at version 2.20.0: aptos, avm (Algorand), concordium, evm, hedera, keeta, near, stellar, svm, tvm (TON) and xrpl. Python ships three — evm, svm, tvm. Go ships two: evm and svm.

Cardano, Casper, Starknet and Sui have specifications and no mechanism package in the repository. The public schemes overview lists exactly the eleven that shipped, which means the docs describe the SDK, not the spec folder. If you write a facilitator in Go today, x402 is a two-chain protocol.

Finding 2. The file a new chain copies to start its binding, specs/scheme_impl_template.md, still documents the X-Payment header. That is v1. Every binding written in 2026 is v2-only and uses PAYMENT-SIGNATURE. The on-ramp for new contributors points at the deprecated transport.

Finding 3. The bindings claim CAIP-2 network identifiers, and most of them earn it. Three do not: near:mainnet, keeta:* and cardano:mainnet use namespaces with no directory in the ChainAgnostic namespaces registry, while algorand, aptos, casper, ccd, hedera, solana, starknet, stellar, sui, tvm, xrpl and eip155 all resolve. Router code that treats the network string as a registered identifier will be wrong for three chains.

Finding 4. The duplicate-settlement race is documented in exactly two places — the SVM spec and the NEAR spec, which cross-references it explicitly and adapts the mitigation to max_block_height. The same race exists on every chain where /settle can be called twice before the first submission lands. Thirteen bindings do not mention it.

Finding 5. The expansion is recent and accelerating. EVM's binding dates to 2025-02-21, in a commit titled "Start refactor," roughly three months before x402's public launch. Sui followed in June 2025, Solana in August 2025. The other twelve all landed in 2026 — Aptos, Algorand and Stellar in January, Hedera in February, Keeta and Cardano in April, TON and NEAR in May, Concordium in late May, then XRPL, Casper and Starknet within three weeks of each other in July.

What it means for LLM4Agents

We sell inference. Buyers arrive holding whatever wallet they already have, and the network binding decides what that costs us to accept.

The first consequence is that "gasless" is a per-chain property, not a protocol property. On EVM, Solana, Stellar, TON, NEAR, Aptos, Hedera, Concordium, Algorand, Keeta, Casper and Starknet, the binding provides for the facilitator to pay the fee, so an agent can pay with a stablecoin balance and no native token. On XRPL it cannot, by construction. On Sui it can only through an extra round trip to a gas station. Advertising a single "pay per call, no gas needed" promise across all of them would be false, and the field that tells you is extra.areFeesSponsored or the presence of extra.feePayer — both of which arrive from the facilitator's /supported response before any buyer shows up.

The second consequence lands on billing. Our reserve-then-settle path, described in the billing internals post, assumes that verification gives a reliable read on whether settlement will succeed. That assumption holds unevenly. NEAR requires waiting for the inner ft_transfer receipt because the outer relayer transaction can succeed while the transfer has not executed. XRPL can invalidate a verified payment between verify and settle through nothing more than the payer sending another transaction. Concordium takes about ten seconds to finalize. A gateway that releases tokens on verification alone is exposed differently on each rail.

The third is narrower and more actionable: the duplicate-settlement race is real and mostly undocumented, so the defense has to be ours. Keying an idempotency record on the hash of the payload bytes — the pattern the SVM and NEAR specs both recommend — is chain-agnostic and cheap. It belongs in the gateway, not in a per-chain adapter.

And the fourth is a language problem before it is a chain problem. Our stack is not TypeScript everywhere. Any binding outside EVM and SVM currently means either running the TypeScript mechanism packages or writing the verification path ourselves against the spec, which is exactly the work the upto scheme already taught us not to underestimate.

Staying on the frontier

Concrete steps, in the order they pay off.

Publish a support matrix, not a support list. The unit is (scheme, network, fee-payer model), and buyers need to see the third element. A page that says "x402 accepted on Base, Polygon, Solana and Stellar; gas sponsored on all four" is a better product surface than a chain logo strip, and it forces us to be honest when a rail like XRPL requires the agent to hold a native token.

Sequence the rails by sponsorship maturity, not by market cap. EVM and SVM are already load-bearing. Stellar is next: the binding is tight, the facilitator ecosystem exists, and the auth-entry model gives the strongest structural guarantee that a sponsor cannot redirect funds. TON and NEAR follow, because both have native meta-transaction paths and specs that already state the facilitator's obligations as MUSTs. Sui and XRPL should be explicitly deferred with a documented reason rather than left ambiguous.

Make the gateway idempotent at the payload level. One record keyed by the hash of the received payload bytes, inserted before settlement is attempted, evicted on the chain's own expiry primitive — blockhash lifetime on Solana, max_block_height on NEAR, ledger window on Stellar. This closes the race on all fifteen rails with one piece of code.

Read /supported at boot and refuse to advertise what we cannot settle. The facilitator already returns kinds and signer addresses per network. Treating that response as the source of truth for our own pricing page removes an entire class of buyer-facing lies.

Contribute the cheap fixes upstream. The template that points at X-Payment and the root exact document that lists six of fifteen bindings are both one-paragraph pull requests. Standing in a foundation repository is earned before it is needed, and we will need it when we propose gateway-side extensions — the same argument we made about publishing to the MCP Registry.

Watch two specific things. Sui's Address Balances feature, which its own binding says could enable EIP-3009-style authorizations and remove the interactive gas station. And whether Starknet's SNIP-9 model — authorization validated by the buyer's own account contract, single-use nonces enforced on-chain, no facilitator state — becomes the template other account-abstraction chains copy. If it does, the facilitator's job gets smaller and the trust surface gets better, which is the direction this whole protocol should be moving.

One gateway, whichever rail your agent holds

OpenAI-compatible inference, paid per call in stablecoins.

Register your agent