Skip to content

feat(x402-solana): package the signer + full MCP verify/settle e2e (Beta on devnet)#32

Merged
tkorkmazeth merged 3 commits into
niceberginc:mainfrom
boymak:feat/solana-beta-signer-export-e2e
Jun 25, 2026
Merged

feat(x402-solana): package the signer + full MCP verify/settle e2e (Beta on devnet)#32
tkorkmazeth merged 3 commits into
niceberginc:mainfrom
boymak:feat/solana-beta-signer-export-e2e

Conversation

@boymak

@boymak boymak commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase A of hardening Solana: move it from experimental rail + example to a packaged, end-to-end Beta.

Signer ships in the package

  • src/rail-adapters/x402-solana-signer.ts exports buildSolanaPaymentPayload + extractSolanaRequirement, re-exported from the root:
    import { buildSolanaPaymentPayload } from "@niceberglabs/tollgate";
  • @solana/web3.js and @solana/spl-token are now optional peer dependencies, dynamically imported — installs stay light for callers that never sign on Solana. The example sign-payload.mjs re-exports from the package (one implementation).

Full MCP verify→settle e2e (CI, no validator)

  • src/__tests__/x402-solana-e2e.test.mjs drives the MCP adapter's built-in verify → credit → execute → settle path on the Solana rail against a fake in-process facilitator (localhost /supported, /verify, /settle).
  • Asserts: 402 Solana challenge in _meta (network, mint, discovered feePayer), signed retry, premium execution, and a trace carrying rail_payment_verified + rail_payment_settled with the on-chain tx signature.

Status

  • README + landing: x402 — Solana / SVM bumped Experimental → Beta on devnet. Mainnet still untested.

Full suite: 230 passing, typecheck clean.

Follow-ups (next phases)

  • Phase B: settlement-uncertainty recovery loop (retry/queue failed settles).
  • Mainnet smoke test with a tiny amount.

🤖 Generated with Claude Code

boymak and others added 3 commits June 25, 2026 15:08
…eta on devnet)

Moves Solana from "experimental rail + example" to a packaged, end-to-end Beta.

- Ship the client signer in the package: src/rail-adapters/x402-solana-signer.ts
  exports buildSolanaPaymentPayload + extractSolanaRequirement (also re-exported
  from the root). @solana/web3.js and @solana/spl-token become OPTIONAL peer
  dependencies, dynamically imported — install stays light for non-Solana callers.
  The example sign-payload.mjs now re-exports from the package.

- Add src/__tests__/x402-solana-e2e.test.mjs: drives the MCP adapter's built-in
  verify -> credit -> execute -> settle path on the Solana rail against a fake
  in-process facilitator (localhost; no validator). Asserts the 402 Solana
  challenge, the signed retry, and a trace carrying rail_payment_verified +
  rail_payment_settled with the on-chain tx signature.

- Status bumped Experimental -> Beta on devnet (README + landing). Mainnet still
  untested.

Full suite: 230 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mainnet)

devnet-settle.mjs now honors SOLANA_NETWORK=devnet|mainnet (plus NETWORK_CAIP2 /
USDC_MINT / SOLANA_RPC_URL overrides), resolving the CAIP-2 id, USDC mint,
default RPC, and explorer cluster per network. Mainnet uses a real-USDC funding
prompt instead of the faucet. Self-transfer default keeps the smoke test
zero-net-cost. Gitignore the mainnet payer keypair too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the same flow on Solana mainnet-beta with real USDC via PayAI: self-transfer,
err: None, fee paid by the facilitator
(tx 3d9k5PACqnSqYk42xMjyvkdzZZNfDPjysRyHGVzpxxCYu1womD6eMAGQx2neZcNCerLNkbjDoy15Y31...).

Status (README + landing): Solana x402 now "Beta; verified on devnet and mainnet".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tkorkmazeth tkorkmazeth merged commit 2a89df0 into niceberginc:main Jun 25, 2026
1 check passed
tkorkmazeth pushed a commit that referenced this pull request Jun 25, 2026
When a payment is verified + executed but settlement fails after the fact
(facilitator/RPC blip), it was a dead-end trace (settlement_uncertain) with the
provider unpaid. Phase B turns that into a recovery loop:

- src/settlement-recovery.ts:
  - settleWithRetry() — bounded exponential backoff over settlePayment.
  - PendingSettlementStore + InMemoryPendingSettlementStore — durable queue of
    verified-but-unsettled payments.
  - SettlementReconciler — drains the queue through each rail adapter; removes
    what settles, keeps the rest with attempts/lastError for the next pass.
- TollGate: pendingSettlementStore config (default in-memory), `pendingSettlements`
  getter, `enqueueSettlement()`, and `reconcileSettlements(opts)`.
- MCP adapter: settle now retries (configurable via settleRetry); anything still
  unconfirmed is queued and the trace records attempts + queued:true.

Tests (src/__tests__/settlement-recovery.test.mjs): retry success/exhaustion,
store ops, reconciler drain/keep/no-adapter, and an end-to-end loop through
TollGate + the MCP adapter (settle fails -> queued -> reconciled).

Built on the merged Solana Beta (#32) and EVM EIP-712 (#33) work. Full suite: 242 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants