feat(card): /fix-card-signature — guided repair for enable-bricked kernel accounts#2412
Conversation
…rnels Three accounts carry validNonceFrom > currentNonce (left by the 2025-09-18 root-validator migration wave); Kernel v3.1 rejects every enable-mode card approval below that floor with InvalidNonce, so auto-balance fails hourly forever and no re-grant can help — the account state itself is the poison. A fourth account is undeployed with a v0.0.3-initCode approval (AA14). Hidden support page (not linked anywhere): diagnoses the account on-chain, sends the matching repair userOp (invalidateNonce(floor+1), or the migration no-op deploy), confirms against re-read chain state, then re-grants — the existing session-approve route stores the fresh approval and kicks off a funding run immediately.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds wallet diagnosis and repair logic for nonce-bricked or undeployed accounts, confirms repairs on-chain, rebuilds the client, and provides a dedicated repair and funding page with route registration and hook tests. ChangesCard signature repair
Estimated code review effort: 4 (Complex) | ~45 minutes Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 6093.56 → 6109.87 (+16.31) 🆕 New findings (10)
✅ Resolved (1)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/hooks/wallet/useCardSignatureRepair.ts (1)
46-48: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConfirm-poll budget is tight (~6s total).
If the RPC lags the bundler beyond ~6s, a successfully-landed repair will surface as "did not confirm" even though it worked, forcing a manual retry. Given this is a support-guided hotfix flow, consider a slightly larger budget or backoff (e.g., 8–10 retries) to reduce false negatives.
[reliability_and_availability]
Also applies to: 115-131
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/wallet/useCardSignatureRepair.ts` around lines 46 - 48, Increase the confirmation polling budget used by the card signature repair flow from the current five attempts to roughly 8–10 attempts, or otherwise extend the total wait to about 8–10 seconds. Update the retry logic associated with CONFIRM_RETRIES while preserving the existing confirmation interval and successful-repair handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/`(mobile-ui)/fix-card-signature/page.tsx:
- Line 23: Update the fix-card-signature page’s useRainCardOverview
destructuring to include isLoading, then gate the disabled={!card} fallback
behavior and “No active card found...” message on the overview query being
settled. Preserve the existing card flow once loading completes.
- Around line 31-34: Update the useEffect that invokes diagnose so it re-runs
when the useZeroDev().address value changes, allowing diagnosis after the
initially undefined address is populated. Add address to the effect dependency
list while preserving the existing diagnose invocation.
---
Nitpick comments:
In `@src/hooks/wallet/useCardSignatureRepair.ts`:
- Around line 46-48: Increase the confirmation polling budget used by the card
signature repair flow from the current five attempts to roughly 8–10 attempts,
or otherwise extend the total wait to about 8–10 seconds. Update the retry logic
associated with CONFIRM_RETRIES while preserving the existing confirmation
interval and successful-repair handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fa14351f-75d9-4450-b6f2-cf318390c253
📒 Files selected for processing (4)
src/app/(mobile-ui)/fix-card-signature/page.tsxsrc/constants/routes.tssrc/hooks/wallet/__tests__/useCardSignatureRepair.test.tssrc/hooks/wallet/useCardSignatureRepair.ts
- diagnose keyed on the async zerodev address (mount-only effect left the page dead on a cold load from a support DM — the exact target flow) - repair() re-diagnoses live state before sending, so a retry after a confirm-poll timeout can't re-send an already-consumed invalidateNonce - guard the Kernel v3.1 invalidation cap (floor > currentNonce+10 has no valid target — fail with clear copy instead of burning doomed passkey taps) - step 2 respects the card-overview loading state instead of flashing 'no active card — contact support' at users on the success path - 'Check again' affordance after a failed diagnosis; dismissed passkey sheets are quiet no-ops, matching the grant path; drop the dead lastError render condition and the type export from the hook file
…lse negatives on laggy RPCs
Summary
Three users' card auto-balance fails hourly forever with
AA23 reverted 0x756688fe(InvalidNonce): their kernel accounts havevalidNonceFrom (3) > currentNonce (2)— state left behind by the 2025-09-18 root-validator migration wave. Kernel v3.1 rejects every enable-mode validation installed below thevalidNonceFromfloor, so no approval — however correctly signed (one victim re-granted post-#2313 and failed 3 seconds later) — can ever validate. Root-passkey ops are exempt, which is why everything else works for them. A fourth user's account is undeployed and pre-cutoff, so his stored approval's v0.0.3 initCode derives the wrong CREATE2 address (AA14).This adds a hidden, auth-gated page
/fix-card-signature(registered inDEDICATED_ROUTES, not in any public regex, linked from nowhere — support DMs the URL):getCode/currentNonce/validNonceFromviapeanutPublicClient.invalidateNonce(validNonceFrom + 1)on the user's own kernel (the deployed v3.1 impl syncscurrentNonceup); undeployed: the existing migration no-op (buildMigrationNoopCall) which deploys + migrates. Confirmation is by re-reading chain state, never the bundle receipt (same rationale asensureRootValidatorMigrated), thenrebuildClientForChainso the re-grant signs fresh.useGrantSessionKey().grant(); the backendsession-approveroute stores the approval andscheduleRebalances immediately, so the user leaves with a funded card.Fleet scan of all 227 active-card wallets: exactly 3 bricked + 1 undeployed-AA14; the state can't recur (current migration path leaves nonces untouched — verified on-chain against healthy migrated accounts), hence a hidden page over grant-preflight plumbing.
Risks / breaking changes
DEDICATED_ROUTESentry).invalidateNonce(floor+1)also revokes any not-yet-installed enable signature below the new floor — for these accounts that's exactly the already-dead approval being replaced; healthy accounts never see the button (diagnose gates it).QA
useCardSignatureRepairunit tests: diagnosis classification (undeployed / bricked / healthy), repair call selection (invalidateNonce encoded to self vs migration no-op), on-chain confirmation polling incl. never-confirms path (7 tests).71d2d847…(papilomal),1e432995…(freddy),ead23114…(arianotfound) — cn=2/vnf=3;c7d77186…(lotito) — undeployed/AA14.Hotfix to
main(prod incident remediation) — back-merge todevneeded after merge.Design notes / accepted trade-offs (from review passes)
currentNonceread hits a lagging fallback RPC that hasn't seen the repair op the confirm poll observed. Accepted: no client-side fix is airtight (provider stickiness isn't controllable here); the confirm poll requires a healthy read first, and we'll verify all 4 affected users' next sweep in prod DB after they run the flow.repair()re-diagnoses live state before sending — a retry after a confirm-poll timeout converges instead of re-sending an already-consumedinvalidateNonce.MAX_NONCE_INCREMENT_SIZE (10)beyondcurrentNoncehas no valid invalidation target (noncemust be> validNonceFromand≤ currentNonce+10), so the page fails with clear copy instead of sending a deterministically-reverting op. No account in the fleet scan is in that state.delay/poll helper duplicated fromkernelMigration.utils(declined dedup): it isn't exported there, and widening a shared util's API inside a hotfix isn't worth two lines.session-approvetime (readvalidNonceFrom/currentNonceand reject with a typed error pointing at this page).