x402: Sepolia E2E harness + client↔server contract test + LOW review nits - #48
Merged
Merged
Conversation
…nits Follow-up to PR #47. Adds the live-testnet verification the fail-closed payment gate needs (a green real payment is the only proof the EIP-712 USDC domain is right), plus the three LOW cosmetic items from the third review. E2E tooling (proofs from the round-3 review's "what Sepolia must prove"): - scripts/x402-client.mjs: minimal buyer-side EIP-3009 signer + X-PAYMENT header builder, matching the server's parser/verifier exactly. Refuses to sign for a `from` it doesn't hold the key to. - scripts/sepolia-e2e.mjs (`npm run e2e:sepolia`): env-gated live harness — proof 1 (genuine payment SUCCEEDS end-to-end, catches the LOW-1 domain footgun), proof 2 (delivery gated on >= confirmationDepth), proof 4 (forged-`from` rejected against a real facilitator). Missing config exits 2 with instructions; never invents keys or funds anything. - test/e2e-client-contract.test.ts: CI-side de-risking of proof 1 with ZERO infra — a buyer-signed payment round-trips through the server's own parsePaymentAuthorization + verifyTransferAuthorization, and a wrong-domain signature is rejected (fail-closed). Catches client/server crypto drift before it becomes silent live-payment rejection. - E2E-SEPOLIA.md: full runbook incl. proof 3 (cross-payment txHash replay + durable-ledger restart persistence, facilitator/restart-orchestrated) and the pre-mainnet sign-off checklist. LOW nits from the third adversarial review: - LOW-1: env.ts now prints a loud [x402] WARNING when a live non-base network runs the on-chain confirm with X402_USDC_NAME/_VERSION left at Base defaults — a domain mismatch silently rejects every payment (fail-closed footgun). - LOW-2: removed the dead confirmPaymentOnChain poll (no callers); fixed the stale checkOnce doc comment. - LOW-3: simplified the eip3009 signature-length guard and documented that it rejects EIP-2098 compact sigs (known limitation, safe direction). 92/92 tests pass (89 + 3 new contract tests); tsc + prod audit clean; biome check clean. README "Remaining gate" points at the new tooling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gitleaks' generic-api-key rule fires on an assignment whose identifier contains "key" plus a high-entropy value. The const held the well-known public Anvil account-0 key (same one eip3009.test.ts uses as `PAYER`, which passed because that identifier has no secret keyword). Renaming the const clears the false positive without changing the (public, non-sensitive) test key. Verified locally: gitleaks detect --no-git → "no leaks found". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Follow-up to #47. Delivers the live-testnet E2E verification the fail-closed payment gate needs before any mainnet flip, and cleans up the three LOW cosmetic items from the third adversarial review.
Why
Every payment check in the service is fail-closed, so the 92 unit tests prove the rejections work but not that a real payment can succeed. Only a live testnet run proves the EIP-712 USDC domain is configured right (the LOW-1 footgun: a
X402_USDC_NAME/_VERSIONmismatch silently rejects every payment). This PR makes that run one command, and de-risks its most fragile assumption in CI with zero infra.E2E tooling
scripts/x402-client.mjs— minimal buyer-side EIP-3009 signer +X-PAYMENTheader builder, matching the server's parser/verifier exactly. Refuses to sign for afromit doesn't hold the key to.scripts/sepolia-e2e.mjs(npm run e2e:sepolia) — env-gated live harness against a running live-mode service + self-hosted facilitator on Ethereum Sepolia:X402_CONFIRMATION_DEPTHfromrejected against a real facilitator2with instructions; never invents keys or funds anything.test/e2e-client-contract.test.ts— CI-side de-risking of proof 1 with no infra: a buyer-signed payment round-trips through the server's ownparsePaymentAuthorization+verifyTransferAuthorization, and a wrong-domain signature is rejected. Catches client/server crypto drift before it becomes silent live-payment rejection.E2E-SEPOLIA.md— full runbook incl. Proof 3 (cross-paymenttxHashreplay + durable-ledger restart persistence, facilitator/restart-orchestrated) and the pre-mainnet sign-off checklist.LOW nits (third adversarial review)
env.tsprints a loud[x402] WARNINGwhen a live non-base network runs the on-chain confirm withX402_USDC_NAME/_VERSIONleft at Base defaults.confirmPaymentOnChainpoll (no callers); fixed the stalecheckOncedoc comment.eip3009signature-length guard; documented that it rejects EIP-2098 compact sigs (known limitation, safe direction).Verification
npx tsc --noEmit✓ ·npx vitest run→ 92/92 (89 + 3 new contract tests) ·npm audit --omit=dev→ 0 vulns ·biome check --changedcleanI could not run the live Sepolia E2E from here — it needs operator infra (a funded Sepolia payer key, a deployed facilitator, an RPC) and I won't handle private keys or fund wallets. This PR makes it runnable in one command; the contract test verifies the crypto contract in CI in the meantime.
🤖 Generated with Claude Code