Skip to content

chore(cleanup): Codex-gated cleanup (Tier A) + B1 codehash enforcement#22

Merged
cipherwebllc merged 7 commits into
mainfrom
chore/cleanup-tier-a
May 31, 2026
Merged

chore(cleanup): Codex-gated cleanup (Tier A) + B1 codehash enforcement#22
cipherwebllc merged 7 commits into
mainfrom
chore/cleanup-tier-a

Conversation

@cipherwebllc
Copy link
Copy Markdown
Owner

Codex-gated cleanup + trust-boundary hardening. Plan reviewed (approve), code reviewed (approve). Each unit verified (tsc / eslint / madge 0 cycles / full suite) and the production build + bundle budget.

What's in (7 commits)

Tier A — behavior-preserving cleanup (audit-verified, in-code-confirmed):

  • Dead zero-consumer re-export aliases removed (enumeratorDomainForChainId, url.ts PayMode).
  • isAggregable → type guard + 3 redundant casts dropped; redundant as SignedCircleUserOp dropped (FSM as Hex untouched).
  • Type-only consolidation: AttestationResponse, history/paymentLog provider unions (SoT = paymentLog).
  • DRY: pad() → viem-free lib/pad.ts; chainObjectForId() in gas-quote hooks; requireAdminAuth() shared by export+stats routes (+ new export-route auth tests).

B1 — Circle Paymaster codehash enforcement (trust boundary):

  • New scripts/verify-circle-codehash.mjs — read-only cross-chain gate (eth_getCode on all 14 Circle chains, per-class non-empty+uniform, cross-class equality, fail-closed non-zero exit). Ran 2026-05-31: all 14 identical = 0x6ed62b6e…68f78a6a.
  • CIRCLE_PAYMASTER_CODEHASH populated (explicit verified chainIds) → assertCirclePaymasterDeployed now enforces keccak256(code) match; throws on a missing entry (fail-loud); completeness test fences every allowlisted chain.

Deferred by design (not in this PR)

  • A3.2 BatchCall relocation (would add a module edge for a trivial type).
  • appendFromCtx (payment-history call sites are non-uniform).
  • Tier C — the 6 OOM'd payment-form unit tests stay e2e-fenced/allowlisted (standard fixes exhausted; render-phase OOM; behavior covered by Playwright). Diagnosis recorded in run-tests.mjs.

Verification

  • tsc --noEmit ✅ · eslint ✅ · madge --circular ✅ 0 cycles · full suite 2542 passed / 0 failed · codehash gate exit 0.
  • Production build ✅; bundle budget: /pay sits at the 420 kB edge on main too (pre-existing platform delta) — this branch adds 0 kB to /pay.
  • Codex plan-review: APPROVE · Codex code-review: APPROVE.

🤖 Generated with Claude Code

cipherwebllc and others added 7 commits May 31, 2026 07:54
…consolidation

Codex-plan-reviewed, behavior-preserving. Each claim verified in-code first.

- A1 dead code: delete zero-consumer re-export aliases pathEnumerator.ts
  enumeratorDomainForChainId (+ its orphaned comment) and url.ts PayMode re-export
  (all consumers import PayMode from lib/fee; url.ts keeps its internal import).
- A2 weak types: stats/route.ts isAggregable -> type guard, drop 3 now-redundant
  casts (chainId/tokenAddress/result narrowed); circleSend.ts drop redundant
  alias-to-alias `as SignedCircleUserOp` (FSM `as Hex` assertions untouched).
- A3.1 types: execute.ts inline {attestation,signature} x3 -> AttestationResponse
  from ./types (already imported; IRIS {message,attestation} shapes left alone).
- A3.3 types: history HistoryProvider/CircleVerification now alias the paymentLog
  SoT (PaymentProvider/CircleVerificationStatus); fixed the inaccurate "circular
  import" comment (history->paymentLog is one-directional, verified acyclic).
- A3.2 (BatchCall relocation) deferred: audit premise was wrong (simpleAccount has
  no BatchCall; mav2 imports neither) -> would add a new module edge for a trivial
  1-line type; not worth it on payment-adjacent modules.

Verified: tsc 0, madge 0 cycles, eslint clean, full suite 2532 passed/0 failed
(6 OOM files unchanged in allowlist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rId in gas-quote hooks

Behavior-equivalent DRY, verified in-code (Codex-gated).

- pad(): hoist the 3 byte-identical `(n)=>n.toString().padStart(2,'0')` copies
  (history formatHistoryTimestamp / historyCsv filename / SuccessOverlay) into
  lib/format.ts. gateway.ts toString(16) hex formatting left untouched (different).
  Added pad equivalence tests (zero / 2-digit / 3+-digit boundary).
- chainObjectForId(): replace the inline `supportedChains.find(c=>c.id===id)` in
  useGasQuoteUsdc/useGasQuoteCircle with the existing helper — byte-identical
  expression incl. unsupported-chainId -> undefined (verified by reading the impl).

Verified: tsc 0, eslint clean, madge 0 cycles, full suite 2535 passed/0 failed
(+3 pad tests; 6 OOM files unchanged in allowlist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xport+stats routes

The two admin log endpoints had byte-identical Bearer-auth + KV-config guards (3
branches). Extracted into app/api/log/payment/_auth.ts (underscore = not a Next
route) so the security-relevant auth has one tested source and cannot drift.

Per Codex's "tests before extraction": added tests/app/api/log-payment-export.test.ts
(export route previously had NO tests) asserting byte-identical status + error body
for all branches; verified it passes against the pre-extraction route first, then
extracted. Stats route already had auth tests (47) — unchanged after wiring.

- _auth.ts: requireAdminAuth(req): NextResponse|null (503 admin_token_not_configured
  / 401 unauthorized / 503 kv_not_configured / null). Imports isKvConfigured from
  @/lib/kv (test mocks still apply module-wide).
- export+stats routes: replace inline auth with `const e=requireAdminAuth(req); if(e)return e;`
  and drop the now-unused isKvConfigured import.

Verified: tsc 0, eslint clean, route auth tests 53 pass, full suite 2541 passed/0 failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ified) + gate script

Codex-gated: do not hardcode an unverified value; verify every Circle chain, compare
by deployment class, enforce only on full match (wrong value = fail-closed outage).

- scripts/verify-circle-codehash.mjs: read-only gate (no keys/funds). eth_getCode on
  the v0.8 paymaster across all 14 Circle chains (7 mainnet + 7 testnet), asserts
  non-empty before hashing, compares keccak256 per deployment class. 2026-05-31 run:
  ALL 14 identical = 0x6ed62b6e...68f78a6a (mainnet == testnet; deterministic CREATE2).
- circlePermit.ts: populate CIRCLE_PAYMASTER_CODEHASH for the explicitly-verified
  chainIds -> assertCirclePaymasterDeployed now enforces keccak256(code) match (C3).
  Explicit list (not derived from the address map) so a future chain isn't auto-enforced
  unverified; re-run the gate to add chains.
- circleAccount.test.ts: the C3 "non-empty code passes" test now asserts codehash
  MISMATCH throws (421614 is registered); match path is the on-chain-verified prod path.
- run-tests.mjs: record the refined Tier C OOM diagnosis (render-phase, not module-eval;
  boundaries already mocked) so the e2e-fenced known-limitation isn't re-chased.

Verified: tsc 0, eslint clean, circleAccount 9 pass, full suite 2541 passed/0 failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ess fence, pad module

Phase-2 review fixes (Codex REVISE):
- BLOCKER scripts/verify-circle-codehash.mjs: `uniform` ignored `missing`, so 1-success/
  6-fail would falsely report "all match" and never fail. Now require full coverage
  (missing.length===0 && single hash) per deployment class, add fallback RPCs (Polygon),
  and exit non-zero on any miss/divergence. Re-ran: all 14 chains reached, GATE PASS.
- WARNING completeness: circlePaymaster.test.ts now asserts every CIRCLE_PAYMASTER_ADDRESSES
  chain has a CIRCLE_PAYMASTER_CODEHASH entry, so no allowlisted Circle chain can silently
  skip hash validation.
- WARNING proxy caveat: circlePermit.ts comment clarifies codehash pins runtime bytecode
  (auxiliary; address allowlist is primary; proxy shells aren't impl-pinned).
- WARNING recovery: runbook documents the codehash-mismatch fail-closed recovery (flag off
  -> Pimlico -> re-run gate -> update codehash).
- NIT bundle: moved pad() from lib/format.ts (imports viem) to viem-free lib/pad.ts so the
  /pay-baseline consumers (SuccessOverlay/history) don't drag viem in. (Note: /pay is at the
  420kB edge on main too — pre-existing platform delta, not a regression from this branch.)

Verified: tsc 0, eslint clean, full suite 2542 passed/0 failed, codehash gate exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…loud on missing codehash

- verify-circle-codehash.mjs: production stores ONE universal codehash for all chains,
  so the gate now requires mainnet.hash === testnet.hash (cross-class) in the pass
  condition, not just per-class uniformity. Re-ran: GATE PASS (all 14 + cross-class same).
- circlePermit.ts: assertCirclePaymasterDeployed now THROWS when a chain has no codehash
  entry (was silent non-empty-only skip). The completeness test guarantees all allowlisted
  Circle chains are pinned, so absence = misconfiguration -> fail-loud.
- smoke-circle-crossswitch.mjs: update the stale "enforcement はまだ" comment (codehash
  enforcement is active since 2026-05-31).

Verified: gate exit 0, tsc 0, eslint clean, full suite 2542 passed/0 failed.

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

- Gasless now: Pimlico (JPYC sponsorship / USDC erc20) OR Circle Paymaster v0.8 for
  USDC on Base/Arbitrum/Optimism (USDC-native gas, OpenPay 0 fee).
- Avalanche C-Chain: standard-mode only until EIP-7702 (ACP-209) activates on mainnet;
  gasless gracefully falls back (merchant pay) / fans guided to another chain (tips).
  Corrects the prior "gasless on all 6 merchant chains" claim.

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

vercel Bot commented May 31, 2026

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

Project Deployment Actions Updated (UTC)
openpay Ready Ready Preview, Comment May 31, 2026 7:25am

@cipherwebllc cipherwebllc merged commit 781d414 into main May 31, 2026
6 checks passed
@cipherwebllc cipherwebllc deleted the chore/cleanup-tier-a branch May 31, 2026 07:46
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