Skip to content

fix(payments): request pot balance validation and navigation issues#1373

Merged
Hugo0 merged 2 commits intopeanut-wallet-devfrom
fix/request-pots-fixes
Oct 29, 2025
Merged

fix(payments): request pot balance validation and navigation issues#1373
Hugo0 merged 2 commits intopeanut-wallet-devfrom
fix/request-pots-fixes

Conversation

@Hugo0
Copy link
Contributor

@Hugo0 Hugo0 commented Oct 29, 2025

Fix Request Pot Balance Check & Navigation

Issues Fixed

  1. Balance validation skipped
  2. Payment methods reshuffle
  3. Broken back navigation

Changes

  • PaymentForm: Remove balance check guards
  • ActionList: Stabilize method sorting
  • add-money: Fix back button

✅ No breaking changes

- Always check balance for Peanut wallet payments (including request pots)
- Stabilize payment method sorting with useCallback
- Fix add-money back button to respect browser history
@vercel
Copy link

vercel bot commented Oct 29, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Oct 29, 2025 10:19pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Unconditionally enable Peanut Wallet balance checks (including request-pot initial view), stabilize payment-method availability callbacks, add an onBeforeShow interception to Daimo pay flows, change add-money back navigation to prefer router.back() with a /home fallback, and refine early-return/charge handling in payment initiation.

Changes

Cohort / File(s) Summary
Payment Balance Validation & Initiation
src/components/Payment/PaymentForm/index.tsx, src/hooks/usePaymentInitiator.ts
Removed request-pot guards so Peanut Wallet balance checks run unconditionally; reorganized insufficient-balance redirect, button label/icon logic, and prioritized balance-first render paths. usePaymentInitiator adds a shouldReturnAfterCharge guard and uses react-query client to avoid premature payment execution when charges are being reconciled.
Payment Method Selection & Daimo Integration
src/components/Common/ActionList.tsx, src/components/Common/ActionListDaimoPayButton.tsx
ActionList stabilizes isMethodUnavailable via useCallback, adds selectedPaymentMethod state and peanut-balance modal gating with continuation logic. ActionListDaimoPayButton adds an optional onBeforeShow prop to intercept/show confirm flows (invite modal handling) before initiating Daimo payments.
Navigation: Add Money Back Handler
src/app/(mobile-ui)/add-money/page.tsx
Replaces unconditional navigation to /home with a handleBack that calls router.back() when internal history exists, falling back to router.push('/home') otherwise.
Docs
docs/fixes/request-pot-balance-and-navigation-fixes.md
Documentation/summary of the above fixes and behavior changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas needing extra attention:
    • src/components/Payment/PaymentForm/index.tsx — verify all payment-view permutations (request pot, confirm, external wallet) still produce correct balance checks, redirects, and button semantics.
    • src/components/Common/ActionList.tsx — ensure selectedPaymentMethod modal gating and state resets are correct and race-free when continuing after the peanut modal.
    • src/components/Common/ActionListDaimoPayButton.tsx — confirm onBeforeShow interception ordering and that confirm-invite flows trigger the stored Daimo click consistently.
    • src/hooks/usePaymentInitiator.ts — validate shouldReturnAfterCharge conditions against existing charge/creation flows to avoid suppressing legitimate initiations.

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • jjramirezn
  • kushagrasarathe
  • Zishan-7

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "fix(payments): request pot balance validation and navigation issues" directly addresses the primary changes in the changeset. The title covers the two main areas of fixes: balance validation improvements in PaymentForm and navigation issues in add-money, which align with the substantial modifications documented in the raw_summary. The title is concise, specific, and clearly communicates the intent of the changes without vague terminology.
Description Check ✅ Passed The pull request description is related to the changeset and provides meaningful context about the issues being addressed. It identifies three specific problems (balance validation skipped, payment methods reshuffle, broken back navigation) and maps them to corresponding file changes (PaymentForm, ActionList, add-money), which align with the actual modifications described in the raw_summary. While concise, the description avoids generic or vague language and conveys the purpose of the changes clearly.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2771ea and 0e3fbb6.

📒 Files selected for processing (1)
  • src/app/(mobile-ui)/add-money/page.tsx (2 hunks)

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

@Hugo0
Copy link
Contributor Author

Hugo0 commented Oct 29, 2025

@coderabbitai review in depth

@coderabbitai coderabbitai bot added the enhancement New feature or request label Oct 29, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Get the list of files changed in this PR
echo "=== Files changed in PR #1373 ==="
gh pr diff 1373 --name-only

echo -e "\n=== Full diff for PR #1373 ==="
gh pr diff 1373

Length of output: 30718


@Hugo0, I'll conduct an in-depth review of this PR. Let me analyze each change carefully.

📊 Overall Assessment

This PR addresses three critical UX bugs with well-documented fixes. The changes are focused and surgical, but I found several areas that need attention.


🔍 Detailed File-by-File Analysis

1. src/components/Payment/PaymentForm/index.tsx ⚠️

✅ Strengths:

  • Correctly removes the !showRequestPotInitialView guard that was skipping balance validation
  • Balance check now runs consistently for all Peanut wallet payments
  • Button text priority properly reordered (balance check before "Pay" text)

⚠️ Critical Issues:

Issue 1: Inconsistent guard logic in getButtonIcon()

// Line 549: Missing showRequestPotInitialView check could show wrong icon
if (!isProcessing && isActivePeanutWallet && !isExternalWalletFlow && !showRequestPotInitialView)
    return 'arrow-up-right'

This is checking !showRequestPotInitialView at the END, but the insufficient balance check above it (line 548) doesn't have this guard. This could cause the button to show "arrow-up-right" when it should show "arrow-down" for insufficient balance on initial view.

Recommendation: The logic seems correct, but verify the icon priority is intentional.

Issue 2: Missing validation in balance check

// Line 238-244
const walletNumeric = parseFloat(String(peanutWalletBalance).replace(/,/g, ''))
if (walletNumeric < parsedInputAmount) {
    dispatch(paymentActions.setError('Insufficient balance'))
}

Missing checks:

  • No validation that peanutWalletBalance is defined before parsing
  • No validation that parsedInputAmount is a valid number
  • No handling of edge case where parsedInputAmount is 0 or negative

Recommended fix:

if (isActivePeanutWallet && areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN)) {
    const walletNumeric = parseFloat(String(peanutWalletBalance ?? '0').replace(/,/g, ''))
    if (!isNaN(walletNumeric) && !isNaN(parsedInputAmount) && parsedInputAmount > 0) {
        if (walletNumeric < parsedInputAmount) {
            dispatch(paymentActions.setError('Insufficient balance'))
        }
    }
}

2. src/components/Common/ActionList.tsx ⚠️

✅ Strengths:

  • useCallback correctly stabilizes the isMethodUnavailable function reference
  • Dependencies array [requiresVerification] is correct
  • Improved balance modal UX with "Continue" button option

⚠️ Issues:

Issue 1: Race condition in handleMethodClick

// Line 118: selectedPaymentMethod state could be stale
const handleMethodClick = async (method: PaymentMethod, bypassBalanceModal = false) => {
    if (flow === 'request' && requestLinkData && !bypassBalanceModal) {
        if (!isUsePeanutBalanceModalShown && hasSufficientPeanutBalance) {
            setSelectedPaymentMethod(method)
            setShowUsePeanutBalanceModal(true)
            return
        }
    }
    // ... rest of logic
}

The issue: If a user rapidly clicks multiple payment methods before the modal appears, selectedPaymentMethod could be overwritten. When they click "Continue" in the modal, they might proceed with a different method than what the modal showed.

Recommended fix: Use functional state update or add method info to modal.

Issue 2: Daimo button onBeforeShow complexity

// Line 254: onBeforeShow callback needs error handling
onBeforeShow={() => {
    if (!isUsePeanutBalanceModalShown && hasSufficientPeanutBalance) {
        setSelectedPaymentMethod(method)
        setShowUsePeanutBalanceModal(true)
        return false
    }
    return true
}}

This is synchronous but should match the async pattern in ActionListDaimoPayButton. Also, there's no error handling if state updates fail.

Issue 3: Modal button actions clear state inconsistently

// Line 339-348: First button clears selectedPaymentMethod
onClick: () => {
    setShowUsePeanutBalanceModal(false)
    setIsUsePeanutBalanceModalShown(true)
    setSelectedPaymentMethod(null) // ✅ Good
    setTriggerPayWithPeanut(true)
}

// Line 351-360: Second button also clears, but AFTER recursive call
onClick: () => {
    setShowUsePeanutBalanceModal(false)
    setIsUsePeanutBalanceModalShown(true)
    if (selectedPaymentMethod) {
        handleMethodClick(selectedPaymentMethod, true) // ⚠️ Async call
    }
    setSelectedPaymentMethod(null) // Clears before handleMethodClick completes
}

The second button calls setSelectedPaymentMethod(null) immediately after the recursive call, but handleMethodClick is async. If the recursive call needs to access selectedPaymentMethod state again, it might be null.

Recommendation: Clear state in a useEffect or after await, or pass method directly instead of relying on state.


3. src/components/Common/ActionListDaimoPayButton.tsx

✅ Strengths:

  • Clean implementation of onBeforeShow prop
  • Proper async handling with early returns
  • Good separation of concerns

Minor observation:

  • Line 138: Added peanutWalletAddress to dependency array—verify this doesn't cause unnecessary re-renders when address changes

4. src/app/(mobile-ui)/add-money/page.tsx ⚠️

⚠️ Critical Issue: window.history.length is unreliable

// Line 17: This check is problematic
if (window.history.length > 1) {
    router.back()
} else {
    router.push('/home')
}

Why this is broken:

  1. window.history.length includes ALL history entries (even from previous sites/tabs)
  2. In many browsers, this value is always > 1 even for the first page
  3. It doesn't tell you if there's a page in YOUR app's navigation stack
  4. On iOS Safari, this value can be incorrect due to privacy features

Better approach:

const [hasHistory, setHasHistory] = useState(false)

useEffect(() => {
    // Track if we arrived here via in-app navigation
    const referrer = document.referrer
    const isInternalNavigation = referrer && new URL(referrer).origin === window.location.origin
    setHasHistory(isInternalNavigation)
}, [])

const handleBack = () => {
    if (hasHistory) {
        router.back()
    } else {
        router.push('/home')
    }
}

Alternative (simpler but still better):

const handleBack = () => {
    // Always try router.back() - Next.js router handles fallback gracefully
    router.back()
    
    // Set a timeout fallback in case back() fails
    const timeoutId = setTimeout(() => {
        router.push('/home')
    }, 100)
    
    // Cleanup on unmount
    return () => clearTimeout(timeoutId)
}

Or use Next.js router history detection:

const handleBack = () => {
    // Check if there's a previous route in Next.js router
    if (router && typeof window !== 'undefined' && window.history.state?.as) {
        router.back()
    } else {
        router.push('/home')
    }
}

5. src/hooks/usePaymentInitiator.ts ⚠️

New guard condition (Line 648-657):

const shouldReturnAfterCharge =
    payload.returnAfterChargeCreation ||
    (chargeCreated && ...) ||
    (!chargeCreated && payload.chargeId && !payload.skipChargeCreation) // ← NEW

⚠️ Issue: Logic complexity and unclear semantics

The new condition (!chargeCreated && payload.chargeId && !payload.skipChargeCreation) is trying to detect "fetching existing charge in prepare mode," but:

  1. Triple negative logic is hard to reason about
  2. The comment says "NEW: If charge exists (not created)" but !chargeCreated means charge was NOT created in THIS call, not that it doesn't exist
  3. The semantics of skipChargeCreation are overloaded—it means both "skip creating a NEW charge" AND "we're in CONFIRM mode"

Potential bug: If chargeCreated is false because charge creation failed (not because we're fetching an existing one), this guard would still trigger.

Recommended refactor:

// Make intent explicit with better variable names
const isPreparingExistingCharge = payload.chargeId && !payload.skipChargeCreation && !chargeCreated
const isExternalWalletOrCrossChain = chargeCreated && (
    payload.isExternalWalletFlow ||
    !isPeanutWallet ||
    (isPeanutWallet && (
        !areEvmAddressesEqual(determinedChargeDetails.tokenAddress, PEANUT_WALLET_TOKEN) ||
        determinedChargeDetails.chainId !== PEANUT_WALLET_CHAIN.id.toString()
    ))
)

const shouldReturnAfterCharge =
    payload.returnAfterChargeCreation ||
    isExternalWalletOrCrossChain ||
    isPreparingExistingCharge

if (shouldReturnAfterCharge) {
    const reason = isPreparingExistingCharge 
        ? 'Fetching existing charge (prepare mode)'
        : isExternalWalletOrCrossChain
        ? 'External wallet or cross-chain flow'
        : 'Return requested by caller'
    console.log(`Charge ready. Returning without payment execution. Reason: ${reason}`)
    // ...
}

🧪 Testing Concerns

The PR lacks test coverage for:

  1. Balance check edge cases (null balance, NaN values, 0 amount)
  2. Race conditions in ActionList payment method selection
  3. Router.back() fallback behavior
  4. Payment initiator guard logic with various charge states

Recommendation: Add unit tests for these critical flows.


📝 Documentation

The docs/fixes/request-pot-balance-and-navigation-fixes.md file is excellent—detailed, well-structured, with root cause analysis. This is a great practice! 🎉

Small suggestion: Add a "Known Limitations" section mentioning the window.history.length caveat.


🎯 Summary of Recommendations

Priority File Issue Recommendation
🔴 HIGH add-money/page.tsx window.history.length unreliable Use referrer check or Next.js router state
🔴 HIGH ActionList.tsx Race condition in payment method selection Use functional state update or pass method to modal
🟡 MEDIUM PaymentForm/index.tsx Missing null checks in balance validation Add null/NaN guards before comparison
🟡 MEDIUM usePaymentInitiator.ts Complex triple-negative logic Refactor with explicit variable names
🟢 LOW ActionList.tsx State clearing timing in modal Clear state after async operation completes

Would you like me to generate code snippets for any of these fixes? Also, should I create follow-up issues for the test coverage gaps?


🏁 Script executed:

#!/bin/bash

# Check if there are existing tests for the modified files
echo "=== Checking for test files ==="
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts | grep -E "(PaymentForm|ActionList|add-money|usePaymentInitiator)"

echo -e "\n=== Checking for router usage patterns in codebase ==="
rg -n "router\.back\(\)" --type=tsx --type=ts -C2

echo -e "\n=== Checking for window.history usage patterns ==="
rg -n "window\.history" --type=tsx --type=ts -C2

Length of output: 195


🏁 Script executed:

#!/bin/bash

# Check if there are existing tests for the modified files
echo "=== Checking for test files ==="
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts | grep -E "(PaymentForm|ActionList|add-money|usePaymentInitiator)" || echo "No test files found for modified components"

echo -e "\n=== Checking for router usage patterns in codebase ==="
rg -n "router\.back\(\)" -g "*.tsx" -g "*.ts" -C2 | head -50

echo -e "\n=== Checking for window.history usage patterns ==="
rg -n "window\.history" -g "*.tsx" -g "*.ts" -C2

echo -e "\n=== Checking how other components handle back navigation ==="
rg -n "onBackClick" -g "*.tsx" -g "*.ts" -C2 | head -50

echo -e "\n=== Checking for useCallback usage patterns with sorting/filtering ==="
rg -n "useCallback.*sort|useCallback.*filter" -g "*.tsx" -g "*.ts" -C3 | head -50

Length of output: 13633


🏁 Script executed:

#!/bin/bash

# Check for checkIfInternalNavigation function
echo "=== Finding checkIfInternalNavigation function ==="
rg -n "checkIfInternalNavigation" -g "*.tsx" -g "*.ts" -B5 -A10

echo -e "\n=== Check PaymentForm balance validation patterns ==="
rg -n "peanutWalletBalance" -g "*.tsx" -g "*.ts" -C3 | head -100

echo -e "\n=== Check for similar insufficient balance error handling ==="
rg -n "Insufficient balance" -g "*.tsx" -g "*.ts" -C2

echo -e "\n=== Check how skipChargeCreation is used ==="
rg -n "skipChargeCreation" -g "*.tsx" -g "*.ts" -C3

Length of output: 23815


🔄 Updated Analysis Based on Codebase Patterns

After examining the codebase, I found important context that changes some of my recommendations:

Critical Finding: Inconsistent Navigation Pattern 🔴

Your codebase has a utility function checkIfInternalNavigation() that properly detects internal navigation:

// src/utils/general.utils.ts
export function checkIfInternalNavigation(): boolean {
    return !!document.referrer && new URL(document.referrer).origin === window.location.origin
}

This is used in Profile and PublicProfile components:

const isInternalReferrer = checkIfInternalNavigation()
if (isInternalReferrer && window.history.length > 1) {
    router.back()
} else {
    router.push('/home')
}

But your PR's add-money/page.tsx is NOT using it! This creates inconsistency and breaks the navigation pattern used elsewhere.

Recommended Fix for add-money/page.tsx:

import { checkIfInternalNavigation } from '@/utils'

const handleBack = () => {
    const isInternalReferrer = checkIfInternalNavigation()
    
    if (isInternalReferrer && window.history.length > 1) {
        router.back()
    } else {
        router.push('/home')
    }
}

This matches the pattern in your Profile components and provides more reliable internal navigation detection.


📊 Updated Priority Recommendations

Priority File Issue Fix
🔴 CRITICAL add-money/page.tsx Not using checkIfInternalNavigation() Use existing utility function for consistency
🟡 MEDIUM ActionList.tsx Race condition in payment method selection Use functional state update
🟡 MEDIUM PaymentForm/index.tsx Missing null checks (low risk - codebase pattern) Add guards for safety
🟢 LOW usePaymentInitiator.ts Complex logic but correct per semantics Consider refactor for clarity later

Positive Findings:

  1. Excellent documentation - The markdown file is comprehensive and well-structured
  2. useCallback fix is correct - Properly stabilizes the function reference with correct dependencies
  3. Balance check removal is correct - The original guard was indeed wrong
  4. skipChargeCreation logic is actually correct - It properly distinguishes prepare mode from confirm mode

🔧 Ready-to-Use Fix:

Here's the corrected add-money/page.tsx:

'use client'

import AddWithdrawRouterView from '@/components/AddWithdraw/AddWithdrawRouterView'
import { useOnrampContext } from '@/context/onramp/onrampContext'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
import { checkIfInternalNavigation } from '@/utils'

export default function AddMoneyPage() {
    const router = useRouter()
    const { resetOnrampFlow } = useOnrampContext()

    useEffect(() => {
        resetOnrampFlow()
    }, [])

    const handleBack = () => {
        const isInternalReferrer = checkIfInternalNavigation()
        
        if (isInternalReferrer && window.history.length > 1) {
            router.back()
        } else {
            router.push('/home')
        }
    }

    return (
        <AddWithdrawRouterView
            flow="add"
            pageTitle="Add Money"
            mainHeading="Where to add money from?"
            onBackClick={handleBack}
        />
    )
}

🎯 Final Verdict:

The PR correctly fixes the stated issues, but has one consistency issue with the navigation pattern. The balance check fix and sorting stabilization are solid.

Would you like me to:

  1. Create a follow-up issue for the ActionList race condition?
  2. Open an issue for adding test coverage?
  3. Should I prepare a suggestion for the navigation fix?

🧠 Learnings used
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#823
File: src/context/kernelClient.context.tsx:79-86
Timestamp: 2025-04-29T19:36:38.121Z
Learning: When Hugo0 asks to "resolve coderabbit comments", they want to acknowledge the comment without necessarily implementing the suggested changes, as the current implementation might be intentional for their specific use case.

Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.

Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.

Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Payment/PaymentForm/index.tsx (1)

372-380: Redirect never triggers because the error flag is never set

handleInitiatePayment now pivots on isInsufficientBalanceError, but the balance guard above dispatches 'Insufficient balance', while isInsufficientBalanceError only matches "You don't have enough balance.". The new redirect, button label, and enabled state therefore never engage—users see the error yet cannot proceed to Add Money (the button stays disabled), which is a regression in every insufficient-peanut-balance flow. Align the detection with the dispatched copy (or vice versa) so the condition actually flips.

-    return error?.includes("You don't have enough balance.")
+    return (
+        !!error &&
+        (error.includes("You don't have enough balance.") || error.includes('Insufficient balance'))
+    )
🧹 Nitpick comments (2)
src/components/Common/ActionListDaimoPayButton.tsx (1)

1-1: Remove unused useEffect import.

The useEffect import on line 1 is not used anywhere in the file and should be removed:

import { useCallback, useState, useRef } from 'react'
src/hooks/usePaymentInitiator.ts (1)

2-2: Remove unused imports and variables.

Verification confirms these are genuinely unused and can be safely removed:

  • BALANCE_DECREASE and INITIATE_PAYMENT (line 2) — imported but never referenced in code
  • useQueryClient import (line 30) and queryClient variable (line 87) — initialized but never used

Removing them will reduce unnecessary dependencies and cleanup dead code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae85b3d and b2771ea.

📒 Files selected for processing (6)
  • docs/fixes/request-pot-balance-and-navigation-fixes.md (1 hunks)
  • src/app/(mobile-ui)/add-money/page.tsx (1 hunks)
  • src/components/Common/ActionList.tsx (6 hunks)
  • src/components/Common/ActionListDaimoPayButton.tsx (4 hunks)
  • src/components/Payment/PaymentForm/index.tsx (5 hunks)
  • src/hooks/usePaymentInitiator.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (29)
📓 Common learnings
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page.
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1104
File: src/components/Payment/PaymentForm/index.tsx:522-545
Timestamp: 2025-08-22T07:28:32.281Z
Learning: In `src/components/Payment/PaymentForm/index.tsx`, the `handleCompleteDaimoPayment` function is only for updating payment status in the backend after a successful Daimo payment. Payment success/failure is handled by Daimo itself, so try/catch error handling and error display are not needed for backend sync failures - users shouldn't see errors if payment succeeded but database update failed.
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
  • src/hooks/usePaymentInitiator.ts
  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
  • src/components/Common/ActionListDaimoPayButton.tsx
  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
📚 Learning: 2025-08-22T07:28:32.281Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1104
File: src/components/Payment/PaymentForm/index.tsx:522-545
Timestamp: 2025-08-22T07:28:32.281Z
Learning: In `src/components/Payment/PaymentForm/index.tsx`, the `handleCompleteDaimoPayment` function is only for updating payment status in the backend after a successful Daimo payment. Payment success/failure is handled by Daimo itself, so try/catch error handling and error display are not needed for backend sync failures - users shouldn't see errors if payment succeeded but database update failed.

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
  • src/components/Common/ActionListDaimoPayButton.tsx
  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-23T09:38:04.446Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:04.446Z
Learning: Within `src/app/request/pay/page.tsx`, extracting the `getBaseUrl` function does not add significant readability, and the host URL construction code is expected to change soon.

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
📚 Learning: 2024-10-18T01:51:35.247Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#458
File: src/components/Offramp/Confirm.view.tsx:141-141
Timestamp: 2024-10-18T01:51:35.247Z
Learning: The `handleConfirm` function in `src/components/Create/Link/Confirm.view.tsx` is separate from the one in `src/components/Offramp/Confirm.view.tsx` and does not need to be renamed when refactoring `handleConfirm` in `src/components/Offramp/Confirm.view.tsx`.

Applied to files:

  • src/app/(mobile-ui)/add-money/page.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/hooks/usePaymentInitiator.ts
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/hooks/usePaymentInitiator.ts
  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-01-16T13:14:40.363Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#631
File: src/components/Create/Create.tsx:108-112
Timestamp: 2025-01-16T13:14:40.363Z
Learning: In the Peanut UI codebase, the `resetTokenContextProvider` function from `tokenSelectorContext` is a stable function reference that doesn't change, so it doesn't need to be included in useEffect dependencies.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
  • src/components/Common/ActionListDaimoPayButton.tsx
📚 Learning: 2025-06-18T19:56:55.443Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/hooks/usePaymentInitiator.ts
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#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/hooks/usePaymentInitiator.ts
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-09-08T03:11:00.114Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:31-41
Timestamp: 2025-09-08T03:11:00.114Z
Learning: In QR payment flows, the `createPayment` API call for payment tracking can be non-awaited when called after successful transaction execution, as it's a non-blocking logging operation and doesn't affect the user's success state.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1132
File: src/app/[...recipient]/client.tsx:394-397
Timestamp: 2025-08-26T15:25:53.328Z
Learning: In `src/components/Common/ActionListDaimoPayButton.tsx`, the `handleCompleteDaimoPayment` function should not display error messages to users when DB update fails because the Daimo payment itself has succeeded - showing errors would be confusing since the payment was successful.

Applied to files:

  • src/components/Common/ActionListDaimoPayButton.tsx
  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
📚 Learning: 2025-08-26T17:38:37.055Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1132
File: src/components/Common/ActionList.tsx:153-156
Timestamp: 2025-08-26T17:38:37.055Z
Learning: In ActionList.tsx, when there are circular dependency concerns with ACTION_METHODS being imported by other components, the preferred solution is to move ACTION_METHODS to a separate constants file (like src/constants/actionlist.consts.ts) rather than using prop drilling. This centralizes constants management and creates a cleaner dependency graph.

Applied to files:

  • src/components/Common/ActionListDaimoPayButton.tsx
  • src/components/Common/ActionList.tsx
📚 Learning: 2024-10-07T15:50:29.173Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-07T15:50:29.173Z
Learning: In `src/components/Request/Pay` components, the `tokenPrice` property in the `IPayScreenProps` interface is only relevant to these views. Other components using `IPayScreenProps` do not need to handle `tokenPriceData` when it's updated in these components.

Applied to files:

  • src/components/Common/ActionListDaimoPayButton.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-29T16:06:38.812Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.

Applied to files:

  • src/components/Common/ActionListDaimoPayButton.tsx
📚 Learning: 2025-07-05T16:58:25.340Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#0
File: :0-0
Timestamp: 2025-07-05T16:58:25.340Z
Learning: Hugo0 successfully refactored sessionStorage usage to React Context in the onramp flow, demonstrating preference for centralized state management over browser storage for component-shared state in React applications.

Applied to files:

  • src/components/Common/ActionListDaimoPayButton.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/Common/ActionList.tsx
  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-07-24T13:26:10.290Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#1014
File: src/components/Claim/Link/Initial.view.tsx:413-413
Timestamp: 2025-07-24T13:26:10.290Z
Learning: In the peanut-ui repository, the change from `${SQUID_API_URL}/route` to `${SQUID_API_URL}/v2/route` in src/components/Claim/Link/Initial.view.tsx was a typo fix, not an API migration, as the codebase was already using Squid API v2.

Applied to files:

  • docs/fixes/request-pot-balance-and-navigation-fixes.md
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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:

  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.

Applied to files:

  • docs/fixes/request-pot-balance-and-navigation-fixes.md
  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/Payment/PaymentForm/index.tsx
📚 Learning: 2025-07-07T20:22:11.092Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/components/Payment/PaymentForm/index.tsx
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.

Applied to files:

  • src/components/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#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/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-24T12:39:09.318Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#478
File: src/components/Request/Create/Views/Initial.view.tsx:93-94
Timestamp: 2024-10-24T12:39:09.318Z
Learning: In `src/components/Request/Create/Views/Initial.view.tsx`, the `inputValue` variable passed to `peanut.createRequestLink` is expected to be a string.

Applied to files:

  • src/components/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (4)
src/app/(mobile-ui)/add-money/page.tsx (1)
src/components/AddWithdraw/AddWithdrawRouterView.tsx (1)
  • AddWithdrawRouterView (50-330)
src/hooks/usePaymentInitiator.ts (2)
src/utils/general.utils.ts (1)
  • areEvmAddressesEqual (412-419)
src/constants/zerodev.consts.ts (2)
  • PEANUT_WALLET_TOKEN (20-20)
  • PEANUT_WALLET_CHAIN (18-18)
src/components/Common/ActionList.tsx (2)
src/constants/actionlist.consts.ts (1)
  • PaymentMethod (5-12)
src/hooks/useGeoFilteredPaymentOptions.ts (1)
  • useGeoFilteredPaymentOptions (30-77)
src/components/Payment/PaymentForm/index.tsx (2)
src/utils/general.utils.ts (1)
  • areEvmAddressesEqual (412-419)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN (20-20)
🪛 LanguageTool
docs/fixes/request-pot-balance-and-navigation-fixes.md

[style] ~3-~3: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...Navigation Fixes Date: October 29, 2025 Issues: Balance check skipped, ba...

(MISSING_COMMA_AFTER_YEAR)


[style] ~181-~181: Consider using a different adverb to strengthen your wording.
Context: ...={() => router.push('/home')} ``` This completely ignores the browser's navigation histor...

(COMPLETELY_ENTIRELY)

🪛 markdownlint-cli2 (0.18.1)
docs/fixes/request-pot-balance-and-navigation-fixes.md

29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ 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 (6)
src/app/(mobile-ui)/add-money/page.tsx (1)

16-24: LGTM - Navigation fix with sensible fallback.

The new back navigation logic properly respects browser history while providing a fallback for direct navigation scenarios. The window.history.length > 1 check is a reasonable heuristic, though note that in some edge cases (e.g., after a full page reload), this might still redirect to home instead of the expected previous page. This is acceptable given the complexity of tracking navigation state across page reloads.

src/components/Common/ActionListDaimoPayButton.tsx (1)

149-166: LGTM - Well-structured interception flow.

The onBeforeShow hook provides a clean extension point for pre-payment checks. The three-phase flow (parent interception → invite modal → payment initiation) is logical and properly handles async operations with early returns.

src/hooks/usePaymentInitiator.ts (1)

644-666: LGTM - Guard prevents premature payment execution.

The new shouldReturnAfterCharge logic correctly addresses the bug where fetching an existing charge without skipChargeCreation=true would prematurely execute payment. The condition at lines 656-658 ensures that when a charge already exists and we're not explicitly skipping charge creation (meaning we're in "prepare" mode from the INITIAL view), the function returns early without executing the payment.

This aligns with the two-phase flow: charge preparation (no balance mutation) followed by explicit payment execution (balance decrease).

docs/fixes/request-pot-balance-and-navigation-fixes.md (1)

1-295: Excellent documentation practice.

This comprehensive documentation file is exemplary. It clearly explains:

  • The root causes of each issue
  • Why previous approaches were wrong
  • The fixes applied with before/after code examples
  • Testing checklists

Minor optional improvements flagged by linters:

  • Line 3: Consider adding a comma after the year in the date format
  • Line 29: The code fence could specify a language identifier

These are purely stylistic and don't diminish the value of this documentation.

src/components/Common/ActionList.tsx (2)

104-114: LGTM - useCallback fixes payment method reshuffling.

Wrapping isMethodUnavailable in useCallback with requiresVerification as the dependency stabilizes the function reference. This prevents useGeoFilteredPaymentOptions's useMemo from seeing a dependency change on every render, which was causing the unstable Array.sort() to reshuffle methods.

This is the correct fix for the reported reshuffling issue.


120-128: LGTM - Balance check modal integration.

The changes properly integrate the new balance check flow:

  • selectedPaymentMethod state tracks the user's choice when the balance modal intervenes
  • bypassBalanceModal parameter allows proceeding after modal dismissal
  • onBeforeShow hook in ActionListDaimoPayButton enables pre-payment interception

The flow correctly shows the balance modal before initiating payment and allows users to either use their Peanut balance or continue with their original method choice.

Also applies to: 249-264

@Hugo0 Hugo0 merged commit 9fb2516 into peanut-wallet-dev Oct 29, 2025
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant