feat(circle): OP/Polygon/Avalanche smoke + effective-fee measurement (gate-pending)#20
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…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>
3-chain gate results (2026-05-31, real USDC, disposable wallet)
Optimism surcharge basisRe-gated with corrected measurement: Avalanche root causeAvalanche 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 ScopePolygon/Avalanche/Ethereum/Unichain remain unregistered → |
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_BPSmust be measured on-chain, not assumed.This PR adds only the measurement scaffolding. It registers no surcharge, so merging it activates nothing —
resolveUsdcGaslessProviderkeeps falling back to Pimlico erc20 for these chains until a value is registered post-gate.Changes
scripts/smoke-circle-crossswitch.mjsSMOKE_CHAIN=optimism|polygon|avalancheconfigs (SoT-matched native USDC addresses, v0.8 paymaster0x0578…700Ec).exchangeRate(PimlicogetTokenQuotes— the same rateuseGasQuoteCircledisplays with), and per circle leg computemarkup = pulledUSDC / (actualGasCost × rate) − 1.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)
SMOKE_CHAIN=<chain> SMOKE_MAINNET_OK=1 …on each chain with a disposable wallet (~2 real USDC each).recommendedSurchargeBpsper chain.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