Squads and Swig can pay x402 now: auditing Solana's smart-wallet path
Since May, the x402 exact scheme on Solana has a second way to verify a payment. It no longer insists that the token transfer is a top-level instruction, so a Squads or Swig smart wallet can pay through a CPI. That moves the agent's spending policy into the payment itself. It also moves new risk onto whoever sponsors the fee.
In July we described the exact SVM scheme as a fixed layout: two compute-budget instructions, one TransferChecked, a few optional extras, and nothing else. That was accurate for the fast path, and it is still how most payments look. But it was only half of the picture. Since @x402/svm 2.14.0, published on 2026-05-29, the reference facilitator ships an opt-in second path for smart wallets. The spec was rewritten around it on 2026-06-09.
This matters more for agents than it looks. On EVM, as we showed in the Smart Sessions audit, an x402 payment is an EIP-3009 signature that the facilitator submits, so the smart account's session policies never run. On Solana, a smart-wallet payment is an instruction to the wallet program. The wallet's own limits execute inside the payment transaction. So we read the spec and the TypeScript and Go code. Then we sent crafted /verify requests to live facilitators and sampled mainnet settlements. The design is sound. The details around it are not uniform.
From instruction layout to payment outcome
The current scheme_exact_svm.md now opens with a different premise. It defines "outcome-based payment semantics": the transaction must produce a transfer of at least the required amount of the right mint to the ATA derived from payTo. It does not care which instruction produces it. The transfer "MAY appear either as a top-level instruction or as an inner instruction (CPI) emitted by another program." The spec says outright that this is "what allows smart wallets to satisfy the scheme."
Everything about fee sponsorship moved into a separate section called the Sponsor Acceptance Policy. The sponsor is whoever signs as feePayer, either the merchant or a third-party facilitator. The spec keeps five MUSTs for any sponsor: fee payer isolation, address lookup table resolution, fee payer fund safety, signer set integrity, and the exact payment outcome. Compute caps, a program allowlist and simulation are SHOULDs. A sponsor "MAY reject transactions that satisfy the exact scheme but violate its local policy," and clients "SHOULD NOT assume universal sponsorship."
The history is short and public. The idea started as RFC #646, opened in November 2025, which proposed counting TransferChecked across top-level and inner instructions instead of checking positions. The RFC is still open, because its title also asks for deadline validation. The TypeScript implementation landed as PR #1527, merged on 2026-05-28. Its author's GitHub profile lists Dexter Intelligence DAO LLC, and the PR states the approach "has been running in production at Dexter." The spec text followed in PR #829. Go parity arrived in PR #3263 on 2026-08-26. The Python facilitator has no smart-wallet path. Neither does x402-rs, whose Solana verifier simulates with inner_instructions: false.
How Path 2 verifies a payment it cannot parse
The reference ExactSvmScheme keeps two paths. Path 1 is the static layout check we covered in July, now allowing three to seven instructions because Phantom injects up to three Lighthouse assertions. Path 2 runs only when an operator sets enableSmartWalletVerification, and only when Path 1 failed for a layout reason. Semantic failures such as a wrong amount, mint, recipient or memo never fall through. That keeps a real rejection from being hidden behind a smart_wallet_* error code.
const scheme = new ExactSvmScheme(signer, undefined, {
enableSmartWalletVerification: true,
smartWalletMaxComputeUnits: 400_000, // default
smartWalletMaxPriorityFeeMicroLamports: 50_000, // default, per compute unit
smartWalletAllowedPrograms: [/* defaults to 7 programs */],
});
Path 2 then runs six steps. First, fee payer isolation: the sponsor's address must not appear in any instruction's accounts or as a program, after lookup tables are resolved. The spec's reasoning is that a key the runtime never sees in an instruction cannot be debited beyond the network fee. Second, compute caps: only SetComputeUnitLimit and SetComputeUnitPrice are accepted, within the configured limits. Third, every top-level program other than ComputeBudget and Memo must be on the allowlist. Fourth, a seller-set extra.memo must match exactly one top-level Memo instruction, so a smart wallet cannot be used to drop it.
Fifth, the facilitator calls simulateTransaction with inner instructions enabled and reads the full CPI trace. Sixth, across top-level and inner instructions, exactly one TransferChecked must match the mint, one of the two possible destination ATAs (SPL Token or Token-2022), and an amount of at least the requirement. Overpayment is tolerated on purpose, for wallets that round fees internally. Zero matches or two matches is a rejection. If any observed transfer names a facilitator key as authority, the payment is rejected as self-spend.
Settlement adds one more step that Path 1 does not need. Simulation shows a transaction would succeed, not that it did. So after a Path 2 settlement confirms, the facilitator fetches the confirmed transaction's inner instructions and looks for the matching transfer again. If the RPC has not indexed it after three tries, it falls back to the destination balance: after minus before must be at least the amount. The spec lists the resulting invariants as I1 to I7. The two that carry security are I1, no token loss, enforced before signing, and I3, merchant truth, enforced after settlement. Simulation is explicitly "non-critical."
What a smart-wallet payment looks like on-chain
The x402 end-to-end suite settles a Swig payment on devnet. We pulled one of those transactions, from 2026-05-28, straight from the devnet RPC:
// devnet tx 2yvRfgR4...xFGVbG (x402 e2e suite, Swig client)
top[0] ComputeBudget SetComputeUnitLimit
top[1] ComputeBudget SetComputeUnitPrice
top[2] swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB // Swig SignV2
inner Tokenkeg... transferChecked 1000 units of devnet USDC
authority = BEU34VsxHNECbtwqMq7F51EYFXirX2teM3rCCb4SV1gD // Swig wallet PDA
top[3] Memo
signers: fee payer + one client key fee: 12,000 lamports CU used: 17,054
Two details matter for a merchant. The payer that verification reports is the transfer's authority, which here is the Swig wallet address, not the agent's key. And the whole thing used 17,054 compute units, far below the 400,000 units the current e2e client requests. We return to that gap below.
The part that matters for agents: policy runs inside the payment
This is where Solana and EVM diverge. In a Path 2 payment, the facilitator simulates and then submits a transaction whose payment instruction is the wallet program. The program authenticates the agent's role, applies its limits, and only then performs the CPI transfer. If a limit is exceeded, the program errors and the payment never happens. The facilitator sees it first as a failed simulation.
We read both main wallet programs. Swig roles can carry TokenLimit, TokenRecurringLimit, TokenDestinationLimit and TokenRecurringDestinationLimit. The last one is keyed on mint plus destination token account, with a reset window measured in slots. In SignV2, a token spend from a Swig-owned account is denied unless a matching limit exists, unless the role holds the unrestricted All or AllButManageAuthority permission. Role authorities include Ed25519, secp256k1 and secp256r1 keys, each with a session variant.
The Squads Smart Account program has use_spending_limit. A spending limit lists the signers allowed to use it, an optional list of destination owner addresses, a period of one-time, day, week or 30-day month, and an expiration. The instruction checks all of that, decrements the remaining amount, and calls transfer_checked through CPI. The destination list holds owner addresses, the same thing x402 calls payTo.
One caveat keeps this from being proven end to end in public. The x402 e2e fixture creates its Swig wallet with Actions.set().all(), an unrestricted root role. So the smart-wallet path that CI exercises never touches a spending limit. The limit behavior above is read from the programs' source. We could not get devnet SOL to build our own limited wallet, because both the devnet and testnet faucets returned rate-limit errors.
Five findings from the spec, the code and the live probes
We built one transaction shape and varied its top-level program: compute budget, one instruction to the program under test with a fresh signer, and a random-nonce Memo. We sent it to /verify only, never /settle, on the morning of 2026-09-29, UTC. Because the instruction data is junk, a facilitator that runs Path 2 fails at simulation, and the rejection reason tells you how far the transaction got.
The allowlist contains a Swig address that does not exist
The default allowlist in the TypeScript and Go facilitators, and in the spec's table, labels SWiGmQedKzMz1tiTqoJCWeGDnGXfNBp2PkXLkpCAtQo as "Swig (legacy)". We queried it on mainnet, devnet and testnet. There is no account at that address on any of them. It appears nowhere in the 1,176-commit history of Swig's own repository. Swig's real legacy program, swigDk8JezhiAVde8k6NMwxpZfgGm2NNuMe1KYCmUjP, used by @swig-wallet/classic 0.2.0-beta.1 in May 2025, is deployed on mainnet and devnet and is not on the list.
The history explains it. PR #2509 removed the bad address on 2026-06-02 at 16:31 UTC, noting it "returns AccountNotFound on Solana mainnet and devnet." PR #2504, merged two hours later, put it back as "legacy" next to the real program. The third-party Rust crate qntx/r402 copied the same list.
The live probes confirm it. On the x402.org devnet facilitator and on PayAI mainnet, the phantom address passed the allowlist and died in simulation with ProgramAccountNotFound, while the real legacy Swig program was stopped with smart_wallet_program_not_allowed. The impact is bounded: I1 and I3 do not depend on the allowlist. But I7, "known programs only," is violated. We found no record of where the address came from. If anyone holds its private key, they could deploy a program there and inherit allowlisted status on every facilitator that uses the defaults. Leaving out the real legacy program costs little today, since it showed no mainnet transactions in the window we measured below. The phantom entry is the actual problem.
The smart-wallet path has the tighter fee ceiling, by 350x
Solana charges the priority fee on the requested compute limit: ceil(price * limit / 1,000,000) lamports, on top of 5,000 lamports per signature. Path 2 defaults to 400,000 compute units at 50,000 microlamports per unit. That is at most 20,000 lamports of priority fee.
Path 1 defaults to a price cap of 5,000,000 microlamports per unit, which is 5 lamports. Its compute-limit cap, maxComputeUnits, is unset by default "preserving existing behavior." Against Solana's 1,400,000-unit transaction maximum, that allows 7,000,000 lamports of priority fee on a payment that might be worth $0.001. The spec calls the static path's 5 lamports per unit a "tighter cap." Per unit, it is 100 times looser than Path 2's. Operators should set both static-path limits.
Signer set integrity is a MUST that defaults to off
Section 2.1.4 says the transaction "MUST NOT require any signatures beyond the client and the sponsor." Each extra required signature costs the sponsor another 5,000 lamports. In both reference implementations the count is only enforced when maxRequiredSignatures is set, and it is unset by default. We sent a smart-wallet shape with three client signers. The x402.org facilitator and Dexter let it through to simulation. PayAI rejected it with invalid_exact_svm_payload_excessive_signers. This is also where multisig smart wallets get awkward. The Squads Smart Account's synchronous execution needs as many signers in the transaction as the threshold. At a threshold of two, that is exactly the extra signer the MUST forbids.
The capability flag does not tell you who runs Path 2
When Path 2 is on, the reference /supported response adds features.smartWalletSupported: true to the Solana kind. We read /supported from ten public facilitators that answered. The flag appeared on x402.org (devnet only) and on Dexter (mainnet and devnet). PayAI does not advertise it, yet the probe shows PayAI runs Path 2 on mainnet with the upstream allowlist. PayAI also rejected compute limits of 75,000 units and above before simulation, while 60,000 went through. The upstream e2e client asks for 400,000, and PayAI refused that value in our probe. A smart-wallet client cannot rely on the flag. It has to try, and it has to size its compute request.
Sponsor policies are already diverging
Dexter, where the reference code came from, runs its own policy layer. It rejected a $0.001 payment as "below dynamic floor": 1,309 atomic units against a settlement cost it put at $0.001190. The floor did not move when we raised the transaction's priority fee by a factor of 1,000. So it is an economic minimum, not a fee-exposure check. Dexter separately enforced a priority-fee cap (60,000 microlamports per unit was refused) and a compute-limit cap (1,400,000 refused). But it applied no program allowlist that we could detect. The real legacy Swig program, Jupiter's aggregator and the System Program all reached its simulation step. The reference allowlist would have stopped all three. None of this breaks I1 or I3. It does mean "x402 exact on Solana" now names a family of sponsor policies, not one.
// /verify probe matrix, 2026-09-29 (smart-wallet shape, junk instruction data)
facilitator net flag Path2 phantom SWiGmQed real swigDk8 3 client signers
x402.org devnet yes yes -> simulation not_allowed -> simulation
Dexter mainnet yes yes -> simulation -> simulation -> simulation
PayAI mainnet no yes -> simulation not_allowed excessive_signers
// not assessed: x402.rs (devnet only, no flag), Daydreams (/verify needs a bearer token),
// OpenX402 (payTo must be registered), UltravioletaDAO (rejected our v2 body shape)
One smaller issue sits in the post-settlement fallback. The balance-delta check passes if the destination ATA grew by at least the amount between two reads. For a busy merchant, a different buyer's payment landing in that window satisfies it just as well. The primary check, which reads the confirmed inner instructions, does not have this problem. The fallback only runs when the RPC has not indexed the transaction after three attempts. But busy merchants are exactly the ones that will hit indexing lag.
How often mainnet actually uses it
Support is one thing and use is another. On 2026-09-29 we measured both mainnet facilitators that run Path 2, from two angles.
First, we decoded the last 1,000 transactions signed by each fee payer. For Dexter's DeXterR2k..., that covered 2026-09-28 03:11 to 2026-09-29 09:22 UTC. 998 were standard payments with a top-level TransferChecked. The other two called a program we did not identify and moved no tokens. For PayAI's CjNFTjvB..., the window ran from 2026-09-27 16:15 to 2026-09-29 09:35 UTC. 996 were standard payments, seven of them carrying Lighthouse assertions and one on Token-2022. The other four called a program we did not identify. All 2,000 transactions succeeded, and none went through Squads or Swig.
Second, we intersected signature lists. That catches a wallet program even when it is only reached through a CPI. In the window the public RPC still indexes, the Swig program appeared in 6,406 transactions, Squads Smart Account in 17,790 and Squads v4 in 24,034. Over roughly the same window, Dexter's fee payer signed 1,420 transactions and PayAI's two listed signers 1,748. The overlap was zero.
// mainnet, windows ending 2026-09-29 ~09:45 UTC
Dexter fee payer 1,420 txs (from 2026-09-28 00:30) with Swig/Squads: 0
PayAI signers (2) 1,748 txs (from 2026-09-27 15:31) with Swig/Squads: 0
Swig program 6,406 txs
Squads Smart Account 17,790 txs
Squads v4 24,034 txs
Swig legacy (real) 0 txs
So smart-wallet x402 on Solana is a supported path with no measurable mainnet traffic yet. Squads and Swig carried tens of thousands of transactions in that window, and none of them was an x402 payment sponsored by these facilitators. That is normal for infrastructure a few months old. It is also the cheapest moment to fix the defaults, before anything depends on them.
What it means for LLM4Agents
We advertise Solana as a first-class billing chain. Until now, an agent paying us over x402 on Solana had to sign the transfer directly with a keypair: the token account's owner or an SPL delegate. Its budget lived wherever its operator put it: in the agent's code, or in the x402 SDKs' per-payment default cap. Path 2 changes the buyer side. An operator can keep the treasury in a Squads smart account or a Swig wallet. The agent gets a role or a spending limit scoped to our payTo, capped per day, and revocable on-chain. Every payment to us then runs that policy before a single unit moves.
It changes our seller side too, in three ways. First, the payer we record is the wallet PDA, not the agent key. Our per-agent accounting has to map wallet addresses to agents, not assume one key per agent. Second, a smart-wallet payment can overpay. The spec tolerates at least the amount, so the reserve, proxy, settle pipeline must credit what actually arrived, not what was requested. Third, whether a smart-wallet agent can pay us at all depends on the facilitator we point our 402 at. Today that is a per-facilitator question with no reliable flag, as Finding 4 shows.
There is a threat side as well. If we ever sponsor fees ourselves, the spec allows a merchant to be its own sponsor, and every finding above becomes our problem. The static path's default fee ceiling alone could make a $0.001 call cost us more in SOL than it earns.
Staying on the frontier
First, test the path we would expose. Before advertising Solana smart-wallet support, run the probe above against whichever facilitator our Solana 402 names, and record its answers on allowlist, signer count and compute limits. Re-run it when that facilitator's version changes. The same due diligence we recommended for verify and settle generally applies here with more cases.
Second, make our Solana accounting wallet-aware. Record the transfer authority as the payer, credit the settled amount rather than the quoted one, and put the reservation ID in extra.memo. Path 2 enforces the memo just like Path 1, so a smart wallet cannot strip it. That gives us a chain-native join key for every smart-wallet payment.
Third, if we run our own sponsor, start from stricter settings than the defaults. Set maxComputeUnits and maxPriorityFeeMicroLamports on the static path, and set maxRequiredSignatures to 2. Replace the phantom Swig entry in the allowlist with the program our users actually run. Prefer the inner-instruction check and log every balance-delta fallback for review.
Fourth, publish a buyer-side recipe. A short guide for operators: create a Squads spending limit with our payTo as the only destination and a daily period, or a Swig role with a TokenRecurringDestinationLimit. Then point the agent's x402 client at it. It is the cleanest agent budget available on any chain we settle on, and our census found nobody using it through x402 yet.
Fifth, report upstream. The phantom allowlist entry, the "tighter cap" wording and the default-off signer count are all small, verifiable fixes to the spec and the reference facilitators. They are the kind of fix we should be sending, not just writing about.
Pay for inference from the wallet that holds the budget
One OpenAI-compatible gateway, settled in USDC over x402 on Solana and EVM.
Register your agent