Skip to content

feat(solana): honor server-provided blockhash from 402 extra (x402#2693) - #7

Merged
VickyXAI merged 5 commits into
mainfrom
feat/server-provided-blockhash
Aug 4, 2026
Merged

feat(solana): honor server-provided blockhash from 402 extra (x402#2693)#7
VickyXAI merged 5 commits into
mainfrom
feat/server-provided-blockhash

Conversation

@VickyXAI

@VickyXAI VickyXAI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What

When the 402 payment requirement carries extra.recentBlockhash, the SVM exact-scheme signer uses it directly and skips the client-side getLatestBlockhash RPC — zero RPC calls on the payment path. Absent or malformed values fall back to the existing cached RPC fetch.

Why

Implements the client half of x402-foundation/x402#2693 (spec fix by ludo/Solana team): the server-provided blockhash is fresher than anything the client can cache (our 10s TTL meant up to ~12s-old blockhashes) AND is pinned to an RPC view the settling side has observed — both shrink the blockhash-expiry settle retry tail behind sol.blockrun.ai's p95.

Testing

  • TestCreateSolanaPaymentPayloadUsesServerBlockhash: zero RPC calls + tx carries the server hash (TDD, watched fail first)
  • TestCreateSolanaPaymentPayloadFallsBackWithoutServerBlockhash: absent/malformed extra falls back to RPC
  • Full suite green
  • E2E validated against a local gateway with real PayAI verify + settle: payments settled on-chain with 0 client RPC calls (server half: BlockRunAI/blockrun-sol#98-pending)

When the 402 requirement carries extra.recentBlockhash, sign with it and
skip the client-side getLatestBlockhash RPC entirely — zero RPC calls on
the payment path, and the tx is pinned to a blockhash the settling RPC
has observed (fresher than the 10s client cache, so less blockhash-expiry
settle retry tail). Absent or malformed values fall back to the RPC fetch.
1bcMax added 4 commits August 3, 2026 22:46
The server-provided blockhash fast path leaked into the async poll re-sign
path. pollPaymentPayload exists precisely to re-sign long image/video jobs
with a FRESH blockhash every 30s, but it passes the same PaymentOption every
time — so once the signer preferred extra.recentBlockhash, every re-sign
silently reused the original, increasingly expired hash. Signing still
succeeds locally, so nothing surfaces until settlement fails at the gateway,
which is the exact failure the re-sign machinery exists to prevent.

Gate the fast path behind allowServerBlockhash: CreateSolanaPaymentPayload
passes true and keeps the zero-RPC win on the submit path, pollPaymentPayload
passes false and fetches fresh. option.Extra is left untouched so the envelope
still echoes "accepted" verbatim.

Also reject the all-zero hash: base58 decodes "111...1" successfully, so a
server sending it would have had the client sign an unsettleable transaction.

Tests:
- TestPollPaymentPayloadForcesFreshBlockhash pins the regression (watched it
  fail against the unfixed re-sign path)
- the fallback test now asserts the RPC-fetched hash actually reaches the
  signed transaction, and covers the zero hash and non-string types
- TestCreateSolanaPaymentPayloadBlockhashRPCFailure covers the re-wired
  no-server-hash + RPC-down error path

Found by /review: the poll regression was flagged independently by the
performance specialist, the Claude adversarial pass, and Codex.
@VickyXAI
VickyXAI merged commit 6f49044 into main Aug 4, 2026
1 check passed
@VickyXAI
VickyXAI deleted the feat/server-provided-blockhash branch August 4, 2026 04:33
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.

1 participant