← Blog
October 20, 2026 · 14 min

x402 on Solana: one payment, nine payees, and who funds the rent

An open pull request would let one x402 payment on Solana pay up to nine recipients atomically, and let the facilitator fund token accounts that do not exist yet. Both ideas are useful. Both move risk that the current spec keeps out of the fee payer's hands.

Today an exact payment on Solana is one transfer to one payTo. A platform that wants a fee, a reseller that wants a margin, or a facilitator that wants to recover its costs has to settle that money some other way.

PR #3221, by Ludo Galabru, proposes a fix. It was opened on 2026-08-21, was still open on 2026-10-20, and last moved on 2026-10-15. It is spec-only. We read the full diff and the review thread, modeled the transaction it describes, and checked what the shipped client does with it. Numbers below come from those checks, dated.

What the PR specifies

The seller adds extra.splits to the payment requirements. Each entry carries a payTo, an absolute amount in base units, an optional memo and an optional ataCreationRequired flag.

// PaymentRequirements.extra (from the PR description)
{
  "feePayer": "EwWqGE4ZFKLofuestmU4LDdK7XM1N4ALgdZccwYugwGd",
  "memo": "pi_3abc123def456",
  "splits": [
    { "payTo": "3pF8…", "amount": "50000", "memo": "platform fee", "ataCreationRequired": false }
  ]
}

The rules are strict and short:

Without splits (and without ataCreationRequired), nothing changes. That backward compatibility is the PR's main selling point.

The PR adapts the splits model from the Solana charge intent draft at paymentauth.org. We opened that draft: it has the same splits array and a sum rule that leaves a positive remainder for the primary recipient.

Capability is advertised, not assumed

The first review round, on 2026-09-15, changed the design most. phdargen (who proposed a similar wire type for Sui in #2615) objected to split support being an unadvertised local policy. The revision of 2026-10-15 moves it into /supported:

// /supported, kinds[].extra
{ "feePayer": "EwWq…", "maxSplits": 8, "ataCreationSponsored": true }

Absent maxSplits means zero. Absent ataCreationSponsored means false. A resource server must not exceed what its facilitator advertises, and the facilitator rejects requirements that do, with split_count_exceeded or split_ata_creation_not_authorized.

This is the right pattern. Our facilitator endpoints post covers how /supported works; a buyer cannot read a facilitator's private policy, so a policy that matters has to be in that document.

Fitting nine legs into 1,232 bytes

The PR says the practical ceiling "may be lower than eight" because of Solana's 1,232-byte transaction limit, and leaves it there. We did the arithmetic.

The model follows the shipped client. We unpacked @x402/svm 2.28.0 and read its construction code: a version 0 message, a compute-unit limit, a compute-unit price, one TransferChecked, and a Memo instruction. With no seller memo it writes a 16-byte random nonce as 32 hex characters. We added one TransferChecked per split and, for the second case, one CreateIdempotent per leg. The fee payer and the client are the two signers.

# serialized size in bytes, our model, 32-byte memo, no address lookup tables
splits   no ATA creation   ATA creation on every leg
0        496               602
2        594               784
4        692               966
6        790               1148
7        839               1239   # over 1,232
8        888               1330   # over 1,232

Eight splits fit with room to spare when the recipients already hold their token accounts: 888 bytes. Create an account for every leg and the ceiling is six splits. With a 256-byte seller memo, the maximum for the creation case is four splits (1,191 bytes). Without creation and with that memo it is ten, so the cap of eight still holds there.

Each creation instruction costs 91 bytes in our model: six account references, a program index and a one-byte payload, plus the recipient's owner key added to the account table (the ATA program and system program keys are paid once). Each extra transfer costs 49 bytes, since its destination token account is the only new key.

Two caveats. This is our model, not a captured transaction, and the PR also permits wallet-injected Lighthouse and Memo instructions that would add bytes. And the PR does not say who decides, per request, between "eight splits" and "six splits with creation". Today the answer is: nobody checks until the transaction is built, and the client finds out when it cannot fit the instructions. Address lookup tables could raise the ceiling, but the PR notes they remain subject to the base spec's rules on them.

A seller should compute len(splits) + count(ataCreationRequired) against a budget before issuing the 402. The spec could say so.

The rent exception, and who can drain it

The second feature drew the longest thread. A transfer to a missing associated token account (ATA) fails. Issue #1020 (open since 2026-01-23) asks that x402 create the seller's account automatically. The PR answers with a bounded exception to fee-payer isolation: the fee payer may appear solely as the funding payer of a CreateIdempotent instruction for a recipient the seller marked ataCreationRequired, and only if the facilitator advertises sponsorship. The client consents by signing.

phdargen first objected that allowing it for splits but not for payTo was inconsistent, and that the griefing vector was worse for splits, so sponsorship should be dropped. Another reviewer, notorious-d-e-v, argued the opposite: sponsoring creation is "one of the biggest wins", because the sponsor can recoup the rent through a split to itself. The author made it symmetric and opt-in on every side.

The PR bounds the exposure at "about 0.002 SOL" per recipient per mint. We measured it. On 2026-10-20, getMinimumBalanceForRentExemption(165) on mainnet returned 1,488,440 lamports (0.00148844 SOL). A 170-byte account returned 1,513,840, which is 5,080 lamports per extra byte. So the figure is about a quarter lower than the PR states for a plain token account. Token-2022 mints with extensions need larger accounts, and the cost scales linearly with size.

The bound the PR gives is "at most once per recipient per mint". That is true per recipient. It is not a bound per sponsor, because recipients are chosen by the seller, and any seller is permissionless in x402. This is our analysis, not a claim in the spec:

  1. An attacker runs a resource server and a client.
  2. Each request names a fresh payTo with ataCreationRequired: true.
  3. The attacker's client pays the minimum amount to that fresh key. The sponsor funds one new account, 1,488,440 lamports.
  4. The key's owner transfers the tokens back out and closes the account. Rent returns to a destination the owner picks.

The attacker's net cost is the transaction fees, because the payment amount round-trips to themselves. A thousand requests would pull about 1.49 SOL out of the sponsor. The PR's mitigations are the opt-in flag, the idempotent instruction, and a pointer to the base spec's section 2.2 griefing controls for payTo rotation. The cost-recovery split helps only if the sponsor can price a SOL cost in a stablecoin leg and refuse requests that do not pay it. The spec gives no formula, and a split to the sponsor in the stablecoin has to track the SOL price.

The facilitator already has the information to refuse: the payTo is new, the account does not exist, and the seller is unknown. A sponsor that wants this feature needs an allowlist or a per-seller rent budget, and the spec should say a rent budget is expected.

The shipped client ignores splits

We read the client code in @x402/svm 2.28.0 (the current release, last modified 2026-09-29). It builds the destination from paymentRequirements.payTo and the amount from paymentRequirements.amount, and reads only extra.feePayer, extra.memo and extra.recentBlockhash. The words splits and ataCreationRequired do not appear in its exact-scheme client.

// @x402/svm 2.28.0, exact client
const transferIx = getTransferCheckedInstruction({
  source: sourceATA, mint: paymentRequirements.asset,
  destination: destinationATA,                 // ATA of payTo
  amount: BigInt(paymentRequirements.amount),  // the full amount
  ...
});

A seller that issues splits to this client gets a transaction paying the full amount to payTo. What happens next depends on the facilitator:

The PR says networks that do not implement the extension "MUST reject requirements that carry a non-empty splits array rather than silently ignoring it." That is the right rule, and it works only if every facilitator and client implements the rejection. The second failure is the one that matters. It is silent, and it fails in the merchant's favor, so nobody complains.

A seller can protect itself today by reading the facilitator's /supported and not issuing splits unless maxSplits is present. The PR requires that. The client side needs an explicit rule: refuse a requirement whose extra carries splits unless it understands them, and never pay the full amount blind. This is also the client's defense against a malicious server injecting a split to divert funds, which section 5.6 of the PR requires the client to check.

Open questions in the thread

The author left two. Should the primary leg keep its overpayment tolerance, or should all legs be exact? And should splits live inline in the SVM document, or as a standalone extension? A third surfaced in review: whether a per-split memo that is never verified belongs in the spec at all. The author kept it because a partner needs to identify a recipient in the consent screen.

We would answer the first as "exact everywhere" for the splits case. The tolerance exists so a smart wallet can round fees. With splits, an overpaying smart wallet pays the merchant the difference, which is harmless but means the merchant's remainder is not a function of the requirement. For reconciliation, exact is easier to reason about. On placement, a network-agnostic extension is cleaner: our Sui audit covers another open binding, and phdargen's comment said the wire type resembled his Sui proposal, which suggests the shape is portable.

The PR has no reference implementation. It says one follows in a separate PR. Until then the spec is untested against real facilitators, and numbers like the 1,232-byte ceiling are unconfirmed.

What it means for LLM4Agents

We are a seller of inference, paid in stablecoin per call. Splits matter to us on three sides.

First, agents that resell. An agent that fronts our API to its own users wants a margin on each call. Today it must run its own settlement. With splits, the user's single payment could pay us and the reseller in one atomic transaction, with no custody gap. That fits the agent-to-agent economy better than a pooled balance.

Second, platform fees. A marketplace of agents can take its fee in the same transaction without ever holding the buyer's funds.

Third, the threat. Everything above says that a split-blind client or facilitator drops legs silently. Our buyers are autonomous agents running whatever SDK version they shipped with. We cannot assume they understand a field that is not in their release. The failure mode is quiet: the agent pays, the resource is served, a counterparty is shorted. We would also not offer ATA sponsorship as a facilitator without a per-seller rent budget, for the reason in the drain analysis above.

The payment path we run today is unchanged by this PR. We do not issue splits.

Staying on the frontier

Five steps, in order.

  1. Add a guard to our own client code: if extra.splits is present and the installed SDK does not know the field, refuse to sign. This costs a few lines and closes the silent-drop case for any agent we ship.
  2. Build a transaction-size check into our 402 issuance. Count legs and creation instructions against the 1,232-byte budget using the model above, and cap at six splits when creation is in play.
  3. Watch #3221 and the follow-up reference implementation for @x402/svm. When it lands, run it on a Solana fork against a split-aware facilitator and capture real transaction sizes to replace our model.
  4. If we ever sponsor token-account creation, price it. Set a per-seller rent budget in lamports, require a cost-recovery split, and allowlist sellers. Re-measure rent at issuance time instead of hardcoding 0.002 SOL.
  5. Decide the reseller story. If agents reselling our inference is a goal, splits are the clean path, and it is worth designing the reseller registration flow now so it can use them when facilitators advertise maxSplits. Our smart-wallet audit covers the other half: how a Squads or Swig wallet would pay these multi-leg transactions through CPI.

For the broader chain picture, see the fifteen network bindings census. Of the open scheme PRs, this one changes the money flow most, and it does so without a single line of shipped code.

Pay per call, in stablecoins, over an OpenAI-compatible API

One 402, one signature, 345+ models. No subscription, no minimum.

Register your agent