Skip to content

fix(gasless): guard delegated EOAs on non-canonical-7702 chains (Avalanche) + Pimlico-only smoke#21

Merged
cipherwebllc merged 3 commits into
mainfrom
fix/avalanche-non-canonical-7702-guard
May 30, 2026
Merged

fix(gasless): guard delegated EOAs on non-canonical-7702 chains (Avalanche) + Pimlico-only smoke#21
cipherwebllc merged 3 commits into
mainfrom
fix/avalanche-non-canonical-7702-guard

Conversation

@cipherwebllc
Copy link
Copy Markdown
Owner

Problem

Avalanche C-Chain implements EIP-7702 via ACP-209 ("7702 style" AA) with non-canonical nonce/balance handling. The standard viem/permissionless/Pimlico 7702 stack can't delegate there — UserOps revert AA23 (proven by the 2026-05-31 gate: all 3 legs, including the Pimlico leg, failed; delegateAfter: null).

useSmartAccount routed purely on detection.kind with no per-chain check. So a delegated EOA (pimlico-simple-7702 / alchemy-mav2-7702 / metamask-7702) on Avalanche would enter a 7702 builder and fail at send time with a cryptic AA23 and no graceful fallback. (Pristine EOAs were already safe: errorPristineNoBootstrap → standard mode.)

Fix

  • lib/chains.ts: chainSupportsCanonical7702(chainId) — blocklist (avalanche + avalancheFuji). New chains default true; verify with SMOKE_LEGS=pimlico before relying.
  • hooks/useSmartAccount.ts: before the kind branches, if a known 7702-delegated kind is on a non-canonical chain → IncompatibleSmartAccountError('errorChainNo7702'). The existing saFallback renders standard mode (which always works on Avalanche).
  • i18n: errorChainNo7702 added to PaymentForm/TipForm/CheckoutForm (ja+en). Pay/Checkout → "pay in standard mode"; Tip → "use another chain" (tips require gasless).
  • smoke SMOKE_LEGS=pimlico: Pimlico-only mode (no Circle) to verify the Pimlico erc20 gasless path on any chain with a disposable EOA — reproduces the Avalanche AA23 and is reusable for future chains.

Scope / risk

  • Strictly an improvement: delegated users on Avalanche go from cryptic AA23 → graceful standard-mode fallback. Reversible.
  • MAv2 is behind NEXT_PUBLIC_ENABLE_MAV2 (off by default), so gating it on Avalanche regresses nothing live.
  • Open product question (not in this PR): since no gasless path works on Avalanche, should the QR/UI advertise gasless on Avalanche at all? Left for a separate decision.

Tests

  • chainSupportsCanonical7702 unit test (Avalanche/Fuji false; Base/Arb/OP/Polygon/Ethereum true; unknown→true).
  • errorChainNo7702 i18n parity across 3 namespaces × ja/en.
  • typecheck + eslint clean. tests/lib/{chains,i18nKeys,accountDetection} 503 pass.

🤖 Generated with Claude Code

…anche) to standard mode

Avalanche C-Chain implements EIP-7702 via ACP-209 ("7702 style" AA) with
non-canonical nonce/balance handling. The standard viem/permissionless/Pimlico
7702 stack can't delegate there: UserOps revert AA23 (proven 2026-05-31 gate,
all 3 legs incl. the Pimlico leg). useSmartAccount routed purely on detection.kind
with no per-chain check, so a delegated EOA (pimlico-simple / mav2 / metamask) on
Avalanche would enter a 7702 builder and fail at send time with a cryptic AA23 and
no fallback. Pristine EOAs were already safe (errorPristineNoBootstrap -> standard).

- lib/chains.ts: add chainSupportsCanonical7702(chainId) (blocklist: avalanche +
  avalancheFuji). New chains default true; verify with SMOKE_LEGS=pimlico before relying.
- useSmartAccount.ts: before kind branches, if a known 7702-delegated kind is on a
  non-canonical chain, throw IncompatibleSmartAccountError(errorChainNo7702) -> the
  existing saFallback shows standard mode. Standard mode always works on Avalanche.
- accountDetection.ts: add errorChainNo7702 to the i18n key union.
- messages ja/en: errorChainNo7702 in PaymentForm/TipForm/CheckoutForm (Pay/Checkout
  guide to standard mode; Tip guides to another chain since tips require gasless).
- smoke: add SMOKE_LEGS=pimlico (Pimlico-only, no Circle) to verify the Pimlico erc20
  gasless path on any chain with a disposable EOA (reproduces Avalanche AA23; circle
  paymaster optional in config). runbook documents the guard + the pimlico-only mode.
- tests: chainSupportsCanonical7702 (chains.test) + errorChainNo7702 i18n parity.

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 10:05pm

…ed -> formatUnits)

Surfaced by SMOKE_LEGS=pimlico on Avalanche: both legs correctly fail AA23, but the
summary then crashed with "Cannot read properties of undefined (reading 'toString')".
Failed legs (catch block) pushed a result without gasChargeUsdc, so the JSON map's
`r.gasChargeUsdc !== null ? formatUnits(...)` passed (undefined !== null) and called
formatUnits(undefined). Regression from the displayBase measurement commit; affects
both modes on any leg failure.

- catch block sets gasChargeUsdc: null explicitly.
- summary JSON map + circle/pimlico gas filters guard with typeof === 'bigint'.
- verified: all-failed-legs summary now renders a clean FAIL (no crash).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion

Lets us verify the existing Pimlico erc20 gasless path on Ethereum mainnet with a
disposable EOA (same tool that caught Avalanche's ACP-209 incompatibility). Circle
on Ethereum stays deferred, so circlePaymaster is omitted -> pimlico-only by design
('all' mode throws a clear "use SMOKE_LEGS=pimlico" error). Polygon already has a
config, so SMOKE_CHAIN=polygon SMOKE_LEGS=pimlico works as-is.

WARNING in funding text: L1 gas is dollar-scale (vs cents on L2), budget ~20-30 USDC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cipherwebllc cipherwebllc merged commit c4f44c8 into main May 30, 2026
6 checks passed
@cipherwebllc cipherwebllc deleted the fix/avalanche-non-canonical-7702-guard branch May 30, 2026 22:23
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