Skip to content

fix(x402-evm): inject EIP-712 USDC domain (verified on Base mainnet)#33

Merged
tkorkmazeth merged 1 commit into
niceberginc:mainfrom
boymak:fix/x402-evm-eip712-domain
Jun 25, 2026
Merged

fix(x402-evm): inject EIP-712 USDC domain (verified on Base mainnet)#33
tkorkmazeth merged 1 commit into
niceberginc:mainfrom
boymak:fix/x402-evm-eip712-domain

Conversation

@boymak

@boymak boymak commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Mainnet-testing Talha's original EVM x402 path surfaced a real bug: the EVM "exact" scheme (EIP-3009) requires the token's EIP-712 domain (name + version) in PaymentRequirements.extra, or the facilitator rejects verify with invalid_exact_evm_missing_eip712_domain. The rail never added it, so EVM verify failed unless the caller hand-built the requirements. (This is the EVM analogue of the Solana feePayer gap.)

Fix

  • EVM_USDC_EIP712_DOMAINS table (Base, Base Sepolia, Ethereum, Polygon, Arbitrum, Optimism) + X402RailConfig.eip712Domain override for custom tokens.
  • createChallenge auto-injects extra.name / extra.version for EVM networks (mirrors the Solana feePayer injection); forwarded to the facilitator on verify/settle.

Tests

src/__tests__/x402-evm-rail.test.mjs (offline): auto-injection for Base mainnet, the Base Sepolia variant, explicit override, no feePayer for EVM, and that verify forwards the domain in paymentRequirements. Full suite: 233 passing.

Verified live (Base mainnet)

Before the fix: invalid_exact_evm_missing_eip712_domain. With the domain: a 0.001 USDC self-transfer settled on Base mainnet via PayAI, gasless (signer held 0 ETH):
0xc18b14ef…2a42f9 — status success, gas paid by the facilitator.

Status: x402 EVM Experimental → Beta (verified on Base mainnet); x402 mainnet row notes EVM + Solana live smoke settles.

🤖 Generated with Claude Code

… on Base mainnet)

Found while mainnet-testing Talha's original EVM x402 path: the EVM "exact"
scheme (EIP-3009) needs the token's EIP-712 domain (name + version) in
PaymentRequirements.extra, or the facilitator rejects verify with
`invalid_exact_evm_missing_eip712_domain`. The rail wasn't adding it, so EVM
verify failed unless the caller hand-built the requirements.

- Add EVM_USDC_EIP712_DOMAINS (Base, Base Sepolia, Ethereum, Polygon, Arbitrum,
  Optimism) and X402RailConfig.eip712Domain override.
- createChallenge auto-injects extra.name/version for EVM (mirrors the Solana
  feePayer injection); forwarded to the facilitator on verify/settle.
- src/__tests__/x402-evm-rail.test.mjs: domain auto-injection, testnet variant,
  explicit override, no feePayer for EVM, and that verify forwards the domain.

Verified live: with the domain, a 0.001 USDC self-transfer settled on Base
mainnet via PayAI, gasless (tx 0xc18b14ef94c7ebd5589bf0816f6c8c8820d87e77c4ace5f118486a3f3c2a42f9).

Status: x402 EVM -> Beta (verified on Base mainnet). Full suite: 233 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
@tkorkmazeth tkorkmazeth merged commit e1a1266 into niceberginc:main Jun 25, 2026
1 check passed
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