Skip to content

fix: block unsupported EURC to USDC swaps#4

Open
westkite1201 wants to merge 1 commit into
circlefin:masterfrom
westkite1201:fix/unsupported-eurc-usdc-route
Open

fix: block unsupported EURC to USDC swaps#4
westkite1201 wants to merge 1 commit into
circlefin:masterfrom
westkite1201:fix/unsupported-eurc-usdc-route

Conversation

@westkite1201

Copy link
Copy Markdown

Summary

  • add a shared Arc Testnet swap route guard that currently allows USDC -> EURC only
  • return a clear unsupported-route message for EURC -> USDC before auth, database writes, or Circle AppKit calls
  • disable quote/execution flow for unavailable routes in the swap panel
  • add regression coverage for supported and unsupported swap directions

Fixes #1.

Test plan

  • npm test
  • npm run lint
  • NEXT_PUBLIC_SUPABASE_URL=https://example.supabase.co NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=dummy-publishable-key NEXT_PUBLIC_ARC_CHAIN=Arc_Testnet CIRCLE_API_KEY=dummy-circle-key CIRCLE_ENTITY_SECRET=0000000000000000000000000000000000000000000000000000000000000000 KIT_KEY=dummy-kit-key APP_FEE_RECIPIENT=0x0000000000000000000000000000000000000000 SUPABASE_SECRET_KEY=dummy-supabase-secret npm run build
  • git diff --check HEAD~1..HEAD

@osr21

osr21 commented Jun 18, 2026

Copy link
Copy Markdown

Alternative: support both directions without blocking either

This PR correctly guards the route that Circle Kit can't handle. For teams who need both EURC→USDC and USDC→EURC working on Arc Testnet today, we found a complete workaround using direct ERC-20 peer-to-peer transfers with a relayer wallet — no Circle Kit swap router involved.

Quick summary of the approach:

  1. User signs an EIP-2612 permit off-chain (zero gas, MetaMask typed-data popup)
  2. Relayer submits three sequential transactions: permit()transferFrom(tokenIn, user→relayer)transfer(tokenOut, relayer→recipient)
  3. Rate is sourced from real market APIs (Frankfurter for USD/EUR, CoinGecko for BTC/USD) with a 5-min cache

Both USDC→EURC and EURC→USDC work in this model because the relayer holds both tokens and the swap is a peer-to-peer bookkeeping operation — there's no on-chain AMM or Circle Kit router in the path.

Important Arc Testnet note for the permit step:

The EIP-2612 domain for both tokens uses version: "2" and chainId: 5042002n. Using version: "1" will cause permit verification to fail silently (the permit() tx succeeds but the allowance is not set because the recovered signer doesn't match).

domain: {
  name: "EURC",          // or "USDC"
  version: "2",          // required — both tokens use version 2 on Arc Testnet
  chainId: 5042002n,
  verifyingContract: EURC_ADDRESS,
}

Also critical: inside a Multicall3From subcall, msg.sender is the Multicall3From contract address, not the relayer EOA. If you batch permit + transferFrom into Multicall3From, the transferFrom will see allowance[user][Multicall3From] = 0 and revert — even though the permit set the allowance for the relayer. Use sequential direct transactions instead. Full write-up: circlefin/arc-node#164.

Full working implementation (both directions, all token pairs including cirBTC): osr21/arc-swapartifacts/api-server/src/routes/pay.ts.

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.

bug: EURC → USDC has no swap route on Arc Testnet (error 331001) — only USDC → EURC direction works

2 participants