Skip to content

fix: error handling for txn signing#1583

Open
kushagrasarathe wants to merge 1 commit intomainfrom
hot-fix/windows-passkey-issue
Open

fix: error handling for txn signing#1583
kushagrasarathe wants to merge 1 commit intomainfrom
hot-fix/windows-passkey-issue

Conversation

@kushagrasarathe
Copy link
Contributor

  • contributes to TASK-17526

@vercel
Copy link

vercel bot commented Dec 23, 2025

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

Project Deployment Review Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Dec 23, 2025 11:02am

@notion-workspace
Copy link

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Walkthrough

Updates error handling in two distinct areas: introduces targeted balance-error detection and selective clearing in the initial link-send view component, while adding NotAllowedError capture and logging with device debug info during transaction signing in the ZeroDev hook.

Changes

Cohort / File(s) Summary
Balance Error Handling in Link Send Component
src/components/Send/link/views/Initial.link.send.view.tsx
Implements selective balance-error management: defines isBalanceError predicate to detect insufficient balance, clears balance errors only when no balance/token value exists (preserving non-balance errors), sets insufficient balance error when balance is insufficient, and clears only balance errors when non-balance errors coexist. Extends effect dependency array to include errorState and adds inline documentation.
NotAllowedError Handling in Transaction Signing
src/hooks/useZeroDev.ts
Adds NotAllowedError capture during handleSendUserOpEncoded with device debug info collection via capturePasskeyDebugInfo, structured exception logging, sending state cleanup, and throws user-facing PasskeyError with code 'SIGNING_CANCELED'. Introduces new imports (capturePasskeyDebugInfo, WebAuthnErrorName) and reorders catch block logic to prioritize NotAllowedError before stale key detection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: improved error handling for transaction signing, which is the focus of both modified files.
Description check ✅ Passed The description references a specific task (TASK-17526) related to the changeset, indicating it addresses a documented issue or feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hot-fix/windows-passkey-issue

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aeca903 and fbffc0c.

📒 Files selected for processing (2)
  • src/components/Send/link/views/Initial.link.send.view.tsx
  • src/hooks/useZeroDev.ts
🧰 Additional context used
🧠 Learnings (17)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:279-279
Timestamp: 2025-08-14T08:02:26.705Z
Learning: For hotfixes in the peanut-ui codebase, prefer generic error messages over specific validation error details until the copy can be reviewed with the team, even when the validation functions return detailed error messages.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2025-06-18T19:56:55.443Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 919
File: src/components/Withdraw/views/Initial.withdraw.view.tsx:87-87
Timestamp: 2025-06-18T19:56:55.443Z
Learning: In withdraw flows for Peanut Wallet, the PeanutActionDetailsCard should always display "USDC" as the token symbol because it shows the amount being withdrawn from the Peanut Wallet (which holds USDC), regardless of the destination token/chain selected by the user. The TokenSelector is used for choosing the withdrawal destination, not the source display.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.

Applied to files:

  • src/components/Send/link/views/Initial.link.send.view.tsx
📚 Learning: 2025-01-13T17:45:04.539Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 623
File: src/context/walletContext/zeroDevContext.context.tsx:106-123
Timestamp: 2025-01-13T17:45:04.539Z
Learning: The promise chain for creating passkey validator and kernel client from webAuthnKey in ZeroDevContext is designed to be reliable and should not error, as the webAuthnKey is already validated before being stored in localStorage.

Applied to files:

  • src/hooks/useZeroDev.ts
📚 Learning: 2025-01-13T17:36:31.764Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 623
File: src/context/walletContext/zeroDevContext.context.tsx:91-93
Timestamp: 2025-01-13T17:36:31.764Z
Learning: In the peanut-ui project, the webAuthnKey stored in localStorage only contains public data and is safe to store there. This is used in the ZeroDevContext for passkey validation.

Applied to files:

  • src/hooks/useZeroDev.ts
📚 Learning: 2025-06-22T16:10:53.167Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 915
File: src/hooks/useKycFlow.ts:96-124
Timestamp: 2025-06-22T16:10:53.167Z
Learning: The `initiateKyc` function in `src/app/actions/users.ts` already includes comprehensive error handling with try-catch blocks and returns structured responses with either `{ data }` or `{ error }` fields, so additional try-catch blocks around its usage are not needed.

Applied to files:

  • src/hooks/useZeroDev.ts
📚 Learning: 2025-07-07T20:22:11.092Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 958
File: src/app/actions/tokens.ts:266-266
Timestamp: 2025-07-07T20:22:11.092Z
Learning: In `src/app/actions/tokens.ts`, within the `fetchWalletBalances` function, using the non-null assertion operator `!` on `process.env.MOBULA_API_KEY!` is intentional and correct, and should not be flagged for replacement with explicit validation.

Applied to files:

  • src/hooks/useZeroDev.ts
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.

Applied to files:

  • src/hooks/useZeroDev.ts
🧬 Code graph analysis (2)
src/components/Send/link/views/Initial.link.send.view.tsx (2)
src/redux/slices/send-flow-slice.ts (1)
  • sendFlowActions (101-101)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN_DECIMALS (19-19)
src/hooks/useZeroDev.ts (1)
src/utils/passkeyDebug.ts (1)
  • capturePasskeyDebugInfo (7-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (2)
src/hooks/useZeroDev.ts (1)

9-16: LGTM: Import additions support new error handling.

The new imports for passkey debug capture and WebAuthn error constants are appropriate for the enhanced error handling.

src/components/Send/link/views/Initial.link.send.view.tsx (1)

147-147: The errorState dependency is correct and necessary since the effect reads errorState?.errorMessage to check for balance errors. While non-balance error state changes will trigger effect runs, the early return guards prevent any adverse effects. This is acceptable behavior.

Comment on lines +108 to +110
// only manage balance related errors here
// dont clear non-balance errors (like PasskeyError from transaction signing like biometric timeout errors)
const isBalanceError = errorState?.errorMessage === 'Insufficient balance'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

String literal comparison for error detection is fragile.

Using the hardcoded string 'Insufficient balance' to distinguish balance errors from other errors (like PasskeyError) creates a tight coupling that breaks if the error message changes elsewhere. This is a maintainability risk.

🔎 Recommended solution: Use error types or codes instead

Define error types/codes in a shared constant:

// In a shared constants file or at the top of the file
export const ERROR_TYPES = {
  INSUFFICIENT_BALANCE: 'INSUFFICIENT_BALANCE',
  PASSKEY_ERROR: 'PASSKEY_ERROR',
  // ... other error types
} as const

Update the error state interface to include an error type/code field:

// In Redux slice or wherever errorState is defined
interface ErrorState {
  showError: boolean
  errorMessage: string
  errorType?: string  // Add this
}

Then use error types for comparison:

-        const isBalanceError = errorState?.errorMessage === 'Insufficient balance'
+        const isBalanceError = errorState?.errorType === ERROR_TYPES.INSUFFICIENT_BALANCE

And when setting errors:

            dispatch(
                sendFlowActions.setErrorState({
                    showError: true,
                    errorMessage: 'Insufficient balance',
+                   errorType: ERROR_TYPES.INSUFFICIENT_BALANCE,
                })
            )

This would decouple error detection logic from user-facing messages and make the code more maintainable.

Based on learnings: prefer generic error messages for hotfixes until copy can be reviewed, but use typed error codes internally for logic.

Also applies to: 134-134

🤖 Prompt for AI Agents
In src/components/Send/link/views/Initial.link.send.view.tsx around lines
108-110 (and also apply to line 134), the code uses a hardcoded string
comparison ('Insufficient balance') to detect balance errors which is fragile;
replace this by adding and using an errorType/errorCode on the shared error
state (e.g., ERROR_TYPES.INSUFFICIENT_BALANCE) instead of comparing user-facing
messages: update the error state/interface where errors are set to include
errorType, introduce a shared constant for error codes, change the check here to
compare errorState.errorType to that constant, and update all places that set
this error to set the new errorType; keep user-facing messages separate and
unchanged.

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