Skip to content

feat(circle): OP/Polygon/Avalanche smoke + effective-fee measurement (gate-pending)#20

Merged
cipherwebllc merged 3 commits into
mainfrom
feat/circle-opt-poly-avax
May 30, 2026
Merged

feat(circle): OP/Polygon/Avalanche smoke + effective-fee measurement (gate-pending)#20
cipherwebllc merged 3 commits into
mainfrom
feat/circle-opt-poly-avax

Conversation

@cipherwebllc
Copy link
Copy Markdown
Owner

What

Scaffolding to extend Circle Paymaster to Optimism / Polygon / Avalanche.

Unlike Base/Arbitrum (documented flat 10% surcharge), these 3 chains have no documented 10% surcharge but an unquantified USDC→native slippage spread per Circle dev docs. So the per-chain CIRCLE_GAS_SURCHARGE_BPS must be measured on-chain, not assumed.

This PR adds only the measurement scaffolding. It registers no surcharge, so merging it activates nothing — resolveUsdcGaslessProvider keeps falling back to Pimlico erc20 for these chains until a value is registered post-gate.

Changes

  • scripts/smoke-circle-crossswitch.mjs
    • Add SMOKE_CHAIN=optimism|polygon|avalanche configs (SoT-matched native USDC addresses, v0.8 paymaster 0x0578…700Ec).
    • Add effective-fee measurement: fetch USDC/native exchangeRate (Pimlico getTokenQuotes — the same rate useGasQuoteCircle displays with), and per circle leg compute markup = pulledUSDC / (actualGasCost × rate) − 1.
    • Output circleEffectiveMarkupBps + recommendedSurchargeBps (= measured + 300bps margin, rounded up to 100) in the result JSON.
  • lib/circlePaymaster.ts — comment noting these 3 are smoke-ready but unregistered until their gate runs.
  • docs/runbooks/circle-paymaster-release-gate.md — 3-chain invocation + the measure → register → enable flow.

Gate flow (after merge)

  1. User runs SMOKE_CHAIN=<chain> SMOKE_MAINNET_OK=1 … on each chain with a disposable wallet (~2 real USDC each).
  2. Gate prints recommendedSurchargeBps per chain.
  3. Register the measured value in CIRCLE_GAS_SURCHARGE_BPS → activates Circle for that chain on next deploy.

Test

  • npm run typecheck
  • eslint (smoke + circlePaymaster) ✅
  • tests/lib/circlePaymaster.test.ts ✅ (OP/Polygon/Avalanche still assert unregistered)

🤖 Generated with Claude Code

…urement (gate-pending)

These 3 chains have no documented 10% surcharge but an unquantified USDC->native
slippage spread, so the surcharge must be measured, not assumed. This adds the
scaffolding to measure it; it registers no surcharge so merging activates nothing.

- smoke-circle-crossswitch.mjs: add SMOKE_CHAIN=optimism|polygon|avalanche configs
  (SoT USDC native addrs, v0.8 paymaster 0x0578..700Ec). Add effective-fee
  measurement: fetch USDC/native exchangeRate (Pimlico getTokenQuotes, same rate
  useGasQuoteCircle uses) and compute per-circle-leg markup = pulled USDC /
  (actualGasCost * rate) - 1. Output circleEffectiveMarkupBps + recommendedSurchargeBps
  (measured + 300bps margin, round up to 100).
- circlePaymaster.ts: note OP/Polygon/Avalanche smoke-ready but unregistered until
  their gate runs (resolveUsdcGaslessProvider falls back to pimlico while unregistered).
- runbook: 3-chain invocation + fee-measurement -> register -> enable flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

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

Project Deployment Actions Updated (UTC)
openpay Ready Ready Preview, Comment May 30, 2026 8:40pm

…ax gate findings

The first measurement compared Circle's pull against raw actualGasCost, which is
the wrong basis (excludes L2 L1-data-fee; the product never displays against actual
gas). It produced absurd recommendations (103% OP / 197% Polygon).

- smoke: compute displayBaseUsdc exactly as useGasQuoteCircle/useGasQuoteUsdc do
  (500k + postOp) x standard maxFeePerGas x exchangeRate. recommendedSurchargeBps now
  = max(0, pull/displayBase - 1) + 300bps margin (the value that keeps the customer-
  facing "max gas" >= Circle's actual pull). Add circleVsPimlicoRatio (Circle gas /
  Pimlico gas, same EOA same gate) as the enable/skip decision signal. Demote the
  actual-gas markup to a labeled diagnostic. gasCharge = usdcOut.total - merchant transfer.
- 2026-05-31 gate results (real USDC, disposable wallet):
  * Optimism: PASS, Circle ~2.5x Pimlico (on par with Base/Arb 3-4x) -> enable candidate.
  * Polygon:  PASS, Circle ~5.8x Pimlico -> cost-heavy, Pimlico likely better.
  * Avalanche: FAIL, all 3 legs AA23, delegation never applied. Avalanche C-Chain uses
    ACP-209 "EIP-7702 style" AA (non-canonical nonce/balance) so the standard
    viem/permissionless/Pimlico 7702 stack can't delegate there -- breaks the Pimlico
    7702 leg too, not just Circle. Not enableable with current stack.
- runbook + circlePaymaster.ts: record findings; no surcharge registered (nothing activates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…=1000 (gate passed)

2026-05-31 re-gate with corrected measurement (PASS): displayBaseUsdc 0.001336 >=
Circle pull 0.001156 -> required display surcharge 0%; circleVsPimlicoRatio 2.31x.
Registering 1000bps (10%) to match Base/Arbitrum policy and cushion OP-stack L1
data-fee volatility (displayBase tracks L2 gas price only; Circle's pull already
consumes 87% of displayBase, ~15% raw headroom). Permit (10x multiplier) covers
actual pulls regardless, so no revert/fund-loss risk; surcharge only pads the
displayed max. Activates on next deploy via the global flag (already on).

- circlePaymaster.ts: add [optimism.id]: 1000 + rationale; note Polygon (5.8x, held)
  and Avalanche (ACP-209 7702 incompat) stay unregistered.
- circlePaymaster.test.ts: Optimism (10) now asserts 1000; unregistered set drops 10.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cipherwebllc
Copy link
Copy Markdown
Owner Author

3-chain gate results (2026-05-31, real USDC, disposable wallet)

Chain Gate Circle gas Pimlico gas Circle÷Pimlico Outcome
Optimism (10) ✅ PASS ~0.0012 USDC 0.0005 USDC 2.31× EnabledCIRCLE_GAS_SURCHARGE_BPS[10]=1000
Polygon (137) ✅ PASS ~0.038 USDC 0.0065 USDC ~5.8× Held (cost-heavy) → keep Pimlico
Avalanche (43114) ❌ FAIL 7702 incompatible (ACP-209) → not enableable

Optimism surcharge basis

Re-gated with corrected measurement: displayBaseUsdc 0.001336 ≥ Circle pull 0.001156 → required display surcharge 0%. Registered 1000bps (10%) to match Base/Arbitrum policy and cushion OP-stack L1-data-fee volatility (displayBase tracks L2 gas price only; Circle's pull already consumes 87% of displayBase). Permit (10× multiplier) covers actual pulls regardless — surcharge only pads the displayed max, no revert/fund-loss risk.

Avalanche root cause

Avalanche C-Chain implements 7702 via ACP-209 "EIP-7702-style" AA (non-canonical nonce/balance handling). The standard viem/permissionless/Pimlico 7702 stack can't apply the delegation → all 3 legs AA23 (validateUserOp revert), delegateAfter: null. This breaks the Pimlico 7702 leg too, not just Circle. Documented in the runbook + code; not fixable in OpenPay.

Scope

Polygon/Avalanche/Ethereum/Unichain remain unregistered → resolveUsdcGaslessProvider falls back to Pimlico erc20 (safe). Only Optimism activates (on next deploy; global flag already on).

@cipherwebllc cipherwebllc merged commit d547260 into main May 30, 2026
6 checks passed
@cipherwebllc cipherwebllc deleted the feat/circle-opt-poly-avax branch May 30, 2026 21:00
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