Skip to content

feat(e2e): add multi-mechanism /protected-multi endpoint to Express server#1450

Open
ryanRfox wants to merge 1 commit intocoinbase:mainfrom
ryanRfox:feature/e2e-multi-mechanism-express
Open

feat(e2e): add multi-mechanism /protected-multi endpoint to Express server#1450
ryanRfox wants to merge 1 commit intocoinbase:mainfrom
ryanRfox:feature/e2e-multi-mechanism-express

Conversation

@ryanRfox
Copy link
Contributor

@ryanRfox ryanRfox commented Mar 4, 2026

Summary

Adds a /protected-multi endpoint to the Express e2e server — the first reference implementation of a multi-element accepts array. The endpoint offers four payment options spanning two chains, three transfer mechanisms, and both gas sponsoring flavors.

Closes #1449

Design

The accepts array has four options covering the full range of x402 payment mechanisms:

# Asset Mechanism Network Gas Sponsoring Selection
1 USDC EIP-3009 Base Sepolia N/A (gasless natively) Default for USDC holders
2 WETH Permit2 Base Sepolia erc20ApprovalGasSponsoring Default for WETH holders
3 USDC Native transfer Solana Devnet N/A (SVM) Default for SOL-USDC holders
4 USDC Permit2 Base Sepolia eip2612GasSponsoring Requires custom PaymentPolicy

Why WETH for option 2? The default client selector picks the first compatible option. If options 1 and 2 both used USDC on the same network, a client with USDC would always match option 1 — making option 2 unreachable without a custom PaymentPolicy. WETH (0x4200000000000000000000000000000000000006, OP Stack predeploy) is a distinct asset, so each option is independently exercisable based on what the client holds. WETH is a plain ERC-20 with no EIP-3009 or EIP-2612, making Permit2 the only valid transfer method.

Why option 4 shares USDC with option 1? Option 4 demonstrates the atomic settleWithPermit() flow — the facilitator calls permit() + permitWitnessTransferFrom() in a single transaction. USDC is the only widely available token on Base Sepolia with EIP-2612 permit() support. Since option 1 also uses USDC (via EIP-3009), option 4 is not auto-reachable by default client behavior — it requires a custom PaymentPolicy to exercise, which is expected to be covered in a follow-up PR focused on this aspect, so we consider it out of scope in this PR.

Gas sponsoring: Both flavors are declared on this route:

  • erc20ApprovalGasSponsoring — for WETH (option 2). Facilitator broadcasts the client's signed approve() tx, paying gas. Two on-chain transactions.
  • eip2612GasSponsoring — for USDC-via-Permit2 (option 4). Facilitator calls permit() + transfer atomically via settleWithPermit(). Single transaction.

Extensions are per-route (not per-option) in the current spec. The client SDK handles this correctly — options that don't need gas sponsoring (EIP-3009, SVM) simply ignore the extension data. The client also checks existing allowance(owner, Permit2) before signing any gas sponsoring artifact, skipping it if already approved.

Changes

  • /protected-multi endpoint configuration with four-option accepts array
  • Both gas sponsoring extensions (erc20ApprovalGasSponsoring + eip2612GasSponsoring)
  • Route handler and server banner updates
  • Updated test.config.json for e2e discovery
  • 6 new unit tests:
    • 4 Express middleware tests (multi-element accepts creation, 402 response, PAYMENT-REQUIRED header, settlement)
    • 2 core initialize tests (multi-chain + multi-mechanism validation, unsupported network rejection)

Test plan

  • Express middleware tests pass (41/41)
  • Core initialize tests pass (10/10)
  • Manual verification: GET /protected-multi returns 402 with four accepts entries
  • Manual verification: both gas sponsoring extensions appear in PAYMENT-REQUIRED response

Future work (separate PR)

Client-side PaymentPolicy / registerPolicy() tests:

  • Client overrides server preference (selects WETH over USDC)
  • Client selects option 4 (USDC-via-Permit2) over option 1 (USDC-via-EIP-3009)
  • Client prefers cross-chain SVM
  • Balance-aware fallback selection

@cb-heimdall
Copy link

cb-heimdall commented Mar 4, 2026

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@vercel
Copy link

vercel bot commented Mar 4, 2026

@ryanRfox is attempting to deploy a commit to the Coinbase Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added typescript sdk Changes to core v2 packages labels Mar 4, 2026
@ryanRfox ryanRfox force-pushed the feature/e2e-multi-mechanism-express branch from fb18877 to 431e509 Compare March 4, 2026 19:44
…erver

Add the first reference implementation of a multi-element accepts array
to the e2e test suite. The endpoint offers four payment options spanning
two chains (Base Sepolia + Solana Devnet), three transfer mechanisms
(EIP-3009, Permit2, SVM native), and both gas sponsoring flavors
(erc20ApprovalGasSponsoring + eip2612GasSponsoring).

Options:
1. USDC via EIP-3009 (Base Sepolia) — default for USDC holders
2. WETH via Permit2 (Base Sepolia) — default for WETH holders
3. USDC native transfer (Solana Devnet) — cross-chain alternative
4. USDC via Permit2 (Base Sepolia) — EIP-2612 gas sponsoring demo

Includes 7 unit tests: 5 Express middleware tests (multi-element accepts
creation, 402 response with length assertion, PAYMENT-REQUIRED header,
gas sponsoring extension keys, settlement) and 2 core initialize tests
(multi-chain + multi-mechanism validation, unsupported network rejection).

Closes coinbase#1449
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdk Changes to core v2 packages typescript

Development

Successfully merging this pull request may close these issues.

[e2e] Add multi-mechanism accepts array reference implementation

2 participants