feat(card): repair enable-bricked kernels inline in the grant preflight#2414
Merged
Conversation
A subset of accounts from the 2025-09-18 root-validator migration wave carry validNonceFrom > currentNonce, so every card session-key enable they ever grant reverts InvalidNonce on backend replay — dead on arrival, invisible to the user until the card silently never funds. A separate cohort of undeployed pre-cutoff accounts mints approvals whose initCode derives the wrong address (AA14). Both states are detectable from pure on-chain reads of the connected wallet (nothing account-specific ships in this repo). The grant now reads validNonceFrom alongside currentNonce and, when repair is needed, sends the fixing root userOp first (invalidateNonce(floor+1), or the migration no-op deploy) — one extra passkey tap during card setup instead of a support ticket. Confirmation is by re-read chain state, never the bundle receipt, mirroring ensureRootValidatorMigrated.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Contributor
Code-analysis diffPainscore total: 6179.6 → 6186.26 (+6.66) 🆕 New findings (8)
✅ Resolved (7)
📈 Painscore deltas (top movers)
|
Contributor
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
- deploy path now goes through ensureRootValidatorMigrated: the hand-rolled confirm only checked 'deployed', which a reverted migration inside a successful bundle satisfies while leaving the account on v0.0.2 — the approval signed next would be silently dead (the exact class this PR repairs); the gate verifies the swap against on-chain ground truth and rebuilds the client - a flaky validNonceFrom read no longer aborts healthy grants — proceed on pre-floor-check behavior and flag it (floored stragglers are still caught by the sweep + /fix-card-signature) - repairEnableNonce: confirm-poll survives flaky reads (tap and gas are already spent); a lagging 'undeployed' fresh read is retryable, never 'contact support'; cap check runs against fresh state only - preflight-repair analytics captured after success, not before the tap
Contributor
Author
|
@coderabbitai review |
Contributor
✅ Action performedReview finished.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to peanut-ui#2412 / peanut-api-ts#1179. An on-chain sweep of the 2025-09-18 root-validator migration wave found 91 kernel accounts with
validNonceFrom > currentNonce— a revocation floor above the counter that makes every enable-mode validation revertInvalidNonceforever (Kernel.sol's floor check; root-passkey ops are exempt, so the wallets look healthy). 3 of them already surfaced as card auto-balance victims; the rest are latent and would hit this the day they activate a card. A smaller cohort of undeployed pre-cutoff accounts mints approvals whose baked v0.0.3 initCode CREATE2-derives the wrong address (AA14 initCode must return sender).This folds the repair into
useGrantSessionKeyso affected users never see a failure:validNonceFromalongside the existinggetCode/currentNonce/accountMetadatareads (same parallel batch — no extra latency for healthy accounts).invalidateNonce(floor + 1)(kernel syncs the counter up), then the grant binds the fresh nonce. Undeployed migration-wrapper account → the migration no-op deploys it (the SDK wrapper swaps the root validator in the same op), then the grant proceeds. Either way it's one extra passkey tap during card setup instead of a support ticket +/fix-card-signatureround-trip.ensureRootValidatorMigrated. A retry after a confirm timeout re-diagnoses live state first, so it can never re-send an already-consumed invalidation.> currentNonce + 10; no valid target exists) fails loudly with contact-support copy instead of sending a deterministically-reverting op. No known account is in that state.card_session_key_preflight_repair(mode: invalidate | deploy) to watch the latent cohort drain.Privacy note: detection is purely on-chain state of the connected wallet — no account list, no addresses, nothing user-identifying ships in this open-source repo.
Risks / breaking changes
eth_callin the grant preflight; behavior otherwise unchanged (all existing nonce-binding tests pass unmodified).grant()— consumers (EnableAutoBalanceBanner,ReEnableModal,spendPreflight) already treatgrant()as an opaque async step with a spinner, so no consumer changes needed.QA
kernelNonceRepair.utilsunit tests: repair-call selection per mode, retry-skips-consumed-invalidation, cap guard, confirm-by-state incl. never-confirms (7 tests).useGrantSessionKeysuites: all existing nonce-binding/validator-binding tests pass unchanged; 2 new tests cover floored → repair → bind-fresh-nonce and undeployed-wrapper → deploy-first (15 total across the three suites).Design notes / accepted trade-offs (from review passes)
ensureRootValidatorMigrated(review finding): a hand-rolled "is it deployed" confirm is satisfiable by a reverted migration inside a successful bundle (account lands on v0.0.2, approval silently dead) — the gate verifies the root swap against on-chain ground truth and rebuilds the client.validNonceFromread does not block healthy grants: falls back to pre-floor-check behavior +card_session_key_preflight_repair{mode:floor-read-failed}analytics. A floored account slipping through is still caught by the sweep's permanent-failure path and/fix-card-signature.handleSendUserOpEncoded, which force-logs-out on the stale-webAuthnKey failure. Identical exposure already exists in prod for the migration no-op inrunCollateralSpendPreflight; the logout is deliberate account-safety behavior, not new risk surface.setSecurityOverlay). Wiring the overlay through grant consumers is scope creep for a one-time tap affecting a bounded legacy cohort — follow-up if support signals confusion.