Skip to content

fix(paymaster): CORS headers for ERC-7677 endpoint#9

Closed
PierreBesson wants to merge 5 commits into
mainfrom
fix-paymaster-cors
Closed

fix(paymaster): CORS headers for ERC-7677 endpoint#9
PierreBesson wants to merge 5 commits into
mainfrom
fix-paymaster-cors

Conversation

@PierreBesson

Copy link
Copy Markdown
Member

Summary

Wallets that broker EIP-5792 paymasterService (e.g. Reown / WalletConnect via secure.walletconnect.org) call our paymaster JSON-RPC endpoint cross-origin. Without CORS the browser blocked the preflight, the wallet then broadcast UserOps with paymasterAndData = 0x, and the bundler rejected them with AA21 didn't pay prefund because the smart account had no ETH for gas.

This adds:

  • OPTIONS handler returning 204 with CORS headers (clears preflight).
  • Access-Control-Allow-Origin: *, Allow-Methods: POST, OPTIONS, Allow-Headers: Content-Type, Max-Age: 86400 on every JSON response (success + error).

* is correct here: the route is a public ERC-7677 JSON-RPC endpoint with no cookies/auth state — context.orgAddress is the only client input and CGPaymaster enforces its own per-org budget on-chain.

Test plan

  • After deploy, preflight clears:
    curl -i -X OPTIONS https://app.chain.giving/api/paymaster \
      -H 'Origin: https://secure.walletconnect.org' \
      -H 'Access-Control-Request-Method: POST' \
      -H 'Access-Control-Request-Headers: content-type'
    
    Expect 204 with Access-Control-Allow-* headers.
  • Connect a smart-account wallet via Reown on Base Sepolia, fund the org's paymaster balance, then click Create New Program — UserOp should ship with non-empty paymasterAndData and succeed (no more AA21 didn't pay prefund).
  • Sanity-check the same flow for fallback paths (Add/Remove/Replace beneficiaries on /program/[address]) since they share useSponsoredWrite.

🤖 Generated with Claude Code

… data

Wallets that broker EIP-5792 paymasterService (e.g. Reown / WalletConnect via
secure.walletconnect.org) call the paymaster JSON-RPC endpoint cross-origin.
Without CORS, preflight failed and the wallet broadcast UserOps with
paymasterAndData = 0x, causing the bundler to reject them with AA21
("didn't pay prefund") because the smart account had no ETH to cover gas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chain-giving-nextjs Ready Ready Preview, Comment May 9, 2026 10:54pm

…text

Reown's embedded smart wallets drop the `capabilities.paymasterService.context`
field when bridging to ERC-7677, so the paymaster route was rejecting all
sponsored UserOps with `Missing or invalid context.orgAddress`. Encode the org
address in the paymaster URL itself so it survives regardless of the wallet's
context handling; keep the context fallback for spec-compliant wallets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… flow

Reown's smart-wallet stack reports "Sponsorship Available" but ships UserOps
with paymasterAndData=0x. Add server-side logging so Vercel function logs show
exactly which method is called, the URL/query, the entryPoint, and the
response we returned. We can then reason about why the wallet isn't attaching
the data instead of guessing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vercel logs showed Reown's smart-wallet stack calls pm_getPaymasterStubData
twice (once for the modal estimate, once on confirm) and never calls
pm_getPaymasterData. Without `isFinal: true` on the stub response, the wallet
treats the data as non-final, expects a follow-up call that never happens,
and submits the UserOp with paymasterAndData=0x — triggering AA21.

Also branch the response shape on EntryPoint version: v0.7 wallets need
{paymaster, paymasterData, paymasterVerificationGasLimit,
paymasterPostOpGasLimit}; v0.6 keeps the existing {paymasterAndData}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Server logs prove Reown calls our paymaster with valid responses (twice for
stub, never for data, isFinal: true). Yet UserOps still ship with
paymasterAndData=0x. Add browser-side logging of:
- the capabilities Reown advertises (specifically paymasterService.supported)
- the exact wallet_sendCalls payload we send

This will tell us whether the wallet receives the URL we think it does and
whether the capability claim matches the actual broadcast behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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