feat(x402): first-class Solana (SVM) support — partial-signed tx, fee payer, verify/settle#30
Merged
Conversation
… payer, verify/settle Adds Solana to the existing x402 rail without a new rail type. EVM and Solana differ only in client authorization (no EIP-3009 — the client builds and partially signs a real SPL transfer, leaving the fee-payer slot for the facilitator) and a couple of requirement fields. Rail (src/rail-adapters/x402-rail.ts): - X402RailConfig.feePayer + extra.feePayer injected for Solana challenges - x402Version auto-defaults to 2 for SVM (the "exact" scheme is a v2 scheme) - discoverFeePayer(): pulls the fee payer from the facilitator's /supported Client signer (examples/x402-solana-recovery/sign-payload.mjs): - buildSolanaPaymentPayload(): ComputeBudget(limit) + ComputeBudget(price<=5) + TransferChecked + Memo(nonce), partial-signed, base64 -> x402 v2 payload - @solana/web3.js + @solana/spl-token dynamically imported (dev-only) Tests (verify and settle exercised separately, success AND failure each): - x402-solana-rail.test.mjs: challenge shape, fee-payer discovery, verify/settle success + failure paths (facilitator stubbed via fake fetch) - x402-solana-sign.test.mjs: offline signer — payload is v2, fee-payer slot empty (partial sign), tx carries the 4 expected instructions Full suite: 228 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…evnet runner Ran the full rail end-to-end against PayAI + Solana devnet and corrected the client signer to match the live SVM "exact" v2 wire format. The rail's own verify/settle bodies were already correct — fixes are client-side only. Confirmed devnet settle (err: None), fee paid by the facilitator's fee payer (not the client): tx 5JeSK1je6xrt3HouPUSKheawqiwJhVPtSufqyzNgyqCLBKSZ11Krvty... Signer (examples/x402-solana-recovery/sign-payload.mjs): - PaymentPayload now embeds the agreed requirement under `accepted`, with the amount as an atomic STRING field `amount` (facilitator rejected the old shape) - compute-unit limit default 120k -> 30k (facilitators reject too-high limits; too-low fails simulation), price still clamped to <= 5 Add examples/x402-solana-recovery/devnet-settle.mjs — self-transfer devnet smoke test (discover feePayer -> challenge -> partial-sign -> /verify -> /settle), prints the explorer link. Local payer keypair path is gitignored. Tests updated for the new payload envelope. Full suite: 228 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beyond the self-transfer smoke test, ran a real payer -> recipient transfer on devnet via PAY_TO: moved exactly 0.001 USDC (payer 20 -> 19.999, recipient 20 -> 20.001), gas paid by the facilitator, err: None (tx de6S852jpFTJ1hHLNMBPAaWqrkkMXzjq8XqPbbCf3LD4s1Mm6ZDAU3ah6dxUZueyN19U...). Also documents that the SVM "exact" fixed instruction layout means the recipient's token account must exist before settle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…m-rail # Conflicts: # .gitignore # src/rail-adapters/x402-rail.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Solana (SVM) support to the existing x402 rail — no new rail type. x402 is EVM-rooted (EIP-3009 + EIP-712), but the SVM "exact" scheme is fully supported by facilitators; the rail was already chain-agnostic on verify/settle, so the real work was the client authorization and a couple of requirement fields.
EVM vs Solana, what actually differs:
transferWithAuthorization, EIP-712 sig/settleChanges
Rail —
src/rail-adapters/x402-rail.tsX402RailConfig.feePayer; injected asextra.feePayerfor Solana challenges (the client needs it to leave the fee-payer slot empty)x402Versionauto-defaults to 2 for SVM networksdiscoverFeePayer()— pulls the fee payer from the facilitator'sGET /supportedand caches itClient signer —
examples/x402-solana-recovery/sign-payload.mjsbuildSolanaPaymentPayload():ComputeBudget(limit) + ComputeBudget(price ≤ 5) + TransferChecked + Memo(nonce), partial-signed, base64 → x402 v2 payload@solana/web3.js+@solana/spl-tokenare dynamically imported (dev-only) so the core SDK install stays lightDocs —
examples/x402-solana-recovery/NOTES.md(end-to-end flow, facilitator options: PayAI / Coinbase CDP / self-hosted Kora) + README status row.Tests (verify & settle separate; success AND failure each)
src/__tests__/x402-solana-rail.test.mjs— challenge shape (mint/payTo/v2/feePayer), fee-payer discovery, and verify success / verify reject / verify non-200 / settle success / settle success:false / settle network-error — facilitator stubbed via a fakefetch, no network.src/__tests__/x402-solana-sign.test.mjs— offline signer: payload is x402 v2, fee-payer signature slot is empty (partial sign), serialized tx carries the 4 expected instructions.Full suite: 228 passing, typecheck clean.
Notes / follow-ups
pay.shintentionally not integrated here; it sits on top of x402 and can be a later layer.🤖 Generated with Claude Code