Skip to content

fix: decouple EIP-712 domain name from display symbol (mainnet USDC = "USD Coin") - #7

Open
Robin Schulte (relativvv) wants to merge 2 commits into
mainfrom
fix/x402-mainnet-eip712-domain-name
Open

fix: decouple EIP-712 domain name from display symbol (mainnet USDC = "USD Coin")#7
Robin Schulte (relativvv) wants to merge 2 commits into
mainfrom
fix/x402-mainnet-eip712-domain-name

Conversation

@relativvv

Copy link
Copy Markdown

Draft — split out of #6 as the standalone mainnet fix.

Problem

On Base mainnet, every x402 payment was rejected by the CDP facilitator with SWAG_X402__VERIFICATION_FAILED / invalid_payload — no funds moved. The exact same code settled fine on Base Sepolia.

Root cause

The 402 challenge advertises the ERC-3009 EIP-712 domain in extra.name / extra.version — the fields the buyer signs over and the facilitator uses to recover the signature. X402RequirementBuilder set extra.name to assetSymbol (default "USDC"). But the EIP-712 domain name is not the display symbol: Base mainnet USDC (0x8335…2913) has on-chain name() = "USD Coin" (version "2"), while Base Sepolia test USDC (0x036C…CF7e) is really named "USDC". So the shop advertised "USDC" on mainnet, the facilitator rebuilt the domain with the wrong name, signature recovery failed → invalid_payload. Testnet passed only because the test token's real name happens to be "USDC". (Both name()s verified on-chain.)

What changed

  • src/Core/X402/Config/X402Config.php + X402ConfigService.php + Resources/config/config.xml — new optional config assetEip712Name; assetSymbol's label clarified to "display only".
  • src/Core/X402/X402RequirementBuilder.phpextra.name is now assetEip712Name !== '' ? assetEip712Name : assetSymbol (empty ⇒ falls back to the symbol, so testnet is unchanged).
  • docs/spec.md — note documenting the EIP-712 domain-name requirement.

Testing

  • tests/Unit/Core/X402/X402RequirementBuilderTest.php + tests/Unit/Support/X402Fixtures.php — fallback (empty ⇒ assetSymbol) and configured-name ("USD Coin") cases.
  • Full local quality gate green: phpunit (87 tests), mago fmt --check, mago lint, mago analyze, file-length, jscpd, dependency-analyser, composer audit.

Notes

Robin Schulte (relativvv) added a commit that referenced this pull request Jul 22, 2026
The mainnet EIP-712 domain-name decoupling (assetEip712Name) is split out
into its own standalone PR (#7) as the isolated mainnet blocker. This branch
now carries only the base-URI resolver (§1), verify-failure logging + error
meta (§1d), and deepLinkCode-in-pay_url (§1e), plus the test bootstrap /
quality-gate machinery.

Removed from here (now in #7):
- X402RequirementBuilder: extra.name back to $config->assetSymbol
- X402Config / X402ConfigService / config.xml: dropped assetEip712Name field
- X402Fixtures / X402RequirementBuilderTest: dropped the assetEip712Name
  fixture param and the fallback/configured-name cases
- docs/spec.md: dropped the EIP-712 domain-name note

§1d coupling: X402SettlementServiceTest advertises the domain via the
requirements' extra.name. With §1c gone, extra.name is assetSymbol again, so
the test's fixture value and expected domain.name are set to 'USDC' (the
fixture assetSymbol) instead of 'USD Coin'.
@relativvv
Robin Schulte (relativvv) marked this pull request as ready for review July 23, 2026 06:48
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