Skip to content

feat: url as state poc#1602

Merged
kushagrasarathe merged 5 commits intopeanut-wallet-devfrom
feat/poc-url-state
Jan 8, 2026
Merged

feat: url as state poc#1602
kushagrasarathe merged 5 commits intopeanut-wallet-devfrom
feat/poc-url-state

Conversation

@jjramirezn
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Dec 31, 2025

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

Project Deployment Review Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jan 1, 2026 8:02pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

Walkthrough

Replaces local UI state in the add-money onramp flow with URL-persisted query state using nuqs, wraps app providers with NuqsAdapter, and refactors OnrampFlowContext to remove UI state exposing transient onramp data and flags.

Changes

Cohort / File(s) Summary
Dependencies & App Provider
package.json, src/app/ClientProviders.tsx
Added nuqs@^2.8.6 and wrapped provider tree with NuqsAdapter (next/app) to enable URL query state management.
Mobile bank page (URL-driven flow)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
Replaced local step/amount with useQueryStates (step, amount), derive initial step from KYC/user when absent, persist all step transitions and amount edits to URL, and update loading/redirect logic to depend on URL state.
AddMoney components (amount & step sync)
src/components/AddMoney/components/AddMoneyBankDetails.tsx, src/components/AddMoney/components/InputAmountStep.tsx, src/components/AddMoney/components/MantecaAddMoney.tsx, src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
AddMoneyBankDetails: source amount from URL query for add-money flow. InputAmountStep: broadened setTokenAmount prop type and added optional initialDenomination and setDisplayedAmount props. MantecaAddMoney: moved step/amount/currency to URL state, added URL-synced handlers, validation, query invalidation on step change, removed source prop (component no longer accepts props). Regional method page updated to call <MantecaAddMoney /> without source.
Manteca deposit UI changes
src/components/AddMoney/components/MantecaDepositShareDetails.tsx
Removed public source and onBack props; NavHeader now uses router.back() directly for previous navigation.
AmountInput public API
src/components/Global/AmountInput/index.tsx
Added initialDenomination?: string and setDisplayedAmount?: (value: string) => void to AmountInput props; initial symbol selection honors initialDenomination, and setDisplayedAmount is invoked when display value updates.
Rhino deposit view typing
src/components/AddMoney/views/RhinoDeposit.view.tsx
Added explicit type annotation for refetchInterval callback parameter (typing/formatting only).
Onramp flow context refactor
src/context/OnrampFlowContext.tsx
Removed OnrampView type and UI state fields (amountToOnramp, currentView and their setters); exposed fromBankSelected, onrampData, their setters, and resetOnrampFlow; updated provider value and docs to reflect URL-based step management.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author. The description field is completely empty, leaving no context about the changeset beyond the commit history. Add a detailed pull request description explaining the goal of URL-as-state migration, affected flows (bank and Manteca), key changes, and any known limitations or follow-up work required.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'feat: url as state poc' is vague and generic. While it references URL and state, 'poc' (proof-of-concept) is non-descriptive and doesn't convey what specific changes or features are being implemented. Replace 'poc' with a more descriptive term. For example: 'feat: implement url-based state management for add-money flows' or 'feat: migrate add-money step/amount to URL state' to clarify the actual scope and intent.
✨ 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 feat/poc-url-state

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

@jjramirezn
Copy link
Contributor Author

@coderabbitai review in depth.

Evaluate:

  • risk of introducing new bugs and breaking existing functionality
  • performance impact and latency
  • code quality and DRY

Also, write a very concise testing guide for QA

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Get PR details
echo "=== PR Details ==="
gh pr view 1602 --json title,body,state,files

echo ""
echo "=== Changed Files ==="
gh pr diff 1602 --name-only

echo ""
echo "=== Full Diff ==="
gh pr diff 1602

Length of output: 41682


🏁 Script executed:

#!/bin/bash

echo "=== Searching for usages of removed context properties ==="
echo ""
echo "--- amountToOnramp usages ---"
rg -n "amountToOnramp" --type=tsx --type=ts -C2

echo ""
echo "--- currentView usages ---"
rg -n "currentView" --type=tsx --type=ts -C2

echo ""
echo "--- useOnrampFlow hook usages (to see what properties are accessed) ---"
rg -n "useOnrampFlow" --type=tsx --type=ts -C3

echo ""
echo "=== Checking for other add-money related files that might be affected ==="
fd -e tsx -e ts . src/components/AddMoney src/app | head -20

echo ""
echo "=== Checking what other files import OnrampFlowContext ==="
rg -l "OnrampFlowContext" --type=tsx --type=ts

Length of output: 1326


🏁 Script executed:

#!/bin/bash

echo "=== Searching for usages of removed context properties ==="
echo ""
echo "--- amountToOnramp usages ---"
rg -n "amountToOnramp" -g "*.tsx" -g "*.ts" -C2

echo ""
echo "--- currentView usages ---"
rg -n "currentView" -g "*.tsx" -g "*.ts" -C2

echo ""
echo "=== useOnrampFlow hook usages (to see what properties are accessed) ==="
rg -n "useOnrampFlow" -g "*.tsx" -g "*.ts" -C3

echo ""
echo "=== Files that import or use OnrampFlowContext ==="
rg -l "OnrampFlowContext|useOnrampFlow" -g "*.tsx" -g "*.ts"

echo ""
echo "=== Check request fulfillment flow (might use onramp context) ==="
rg -n "request.*fulfill" -g "*.tsx" -g "*.ts" -i | head -20

Length of output: 29274


🏁 Script executed:

#!/bin/bash

echo "=== Check other add-money page implementations ==="
echo ""
echo "--- US bank add-money page ---"
cat src/app/\(mobile-ui\)/add-money/us/bank/page.tsx

echo ""
echo "--- Regional method add-money page ---"
cat src/app/\(mobile-ui\)/add-money/\[country\]/\[regional-method\]/page.tsx

echo ""
echo "--- Main add-money country selector page ---"
cat src/app/\(mobile-ui\)/add-money/\[country\]/page.tsx | head -100

echo ""
echo "=== Check for any hardcoded navigation to these routes ==="
rg -n "add-money.*bank.*step" -g "*.tsx" -g "*.ts"

echo ""
echo "=== Check TypeScript errors for strict null checks on URL state ==="
rg -n "urlState\." -g "*.tsx" -g "*.ts" -A2 -B2

Length of output: 7158


🏁 Script executed:

#!/bin/bash

echo "=== Check if onrampData is validated before use in showDetails step ==="
echo ""
ast-grep --pattern $'if (urlState.step === $$$) {
    $$$
}'

echo ""
echo "=== Check request fulfillment flow - does it set URL state? ==="
cat src/context/RequestFulfillmentFlowContext.tsx | head -80

echo ""
echo "=== Check if there are tests for these components ==="
fd -e test.tsx -e test.ts . src/app src/components | rg -i "addmoney|onramp|bank"

echo ""
echo "=== Check for any URL query param validation utilities ==="
rg -n "parseAs|validation.*url|validate.*query" -g "*.tsx" -g "*.ts" | head -20

Length of output: 17081


🏁 Script executed:

#!/bin/bash

echo "=== Check if onrampData is validated before rendering showDetails ==="
cat src/components/AddMoney/components/AddMoneyBankDetails.tsx | head -150

echo ""
echo "=== Check createOnramp API and error handling ==="
rg -n "createOnramp|useCreateOnramp" -g "*.tsx" -g "*.ts" -A3 -B1 | head -50

echo ""
echo "=== Look for nuqs documentation or configuration ==="
rg -n "NuqsAdapter|nuqs" -g "*.tsx" -g "*.ts" -g "*.json"

echo ""
echo "=== Check if there's validation middleware or guards ==="
rg -n "middleware|guard.*route|protected.*route" -g "*.tsx" -g "*.ts" | head -20

Length of output: 11239


@jjramirezn, I've completed an in-depth review of this PR. Here are my findings:

🚨 Critical Issues

1. Missing Data Validation for Deep Links

Risk: HIGH - Can cause runtime errors

In src/app/(mobile-ui)/add-money/[country]/bank/page.tsx, the showDetails step renders AddMoneyBankDetails without validating that onrampData exists:

if (urlState.step === 'showDetails') {
    return <AddMoneyBankDetails />
}

Problem: If a user navigates directly to ?step=showDetails (via bookmark, manual URL edit, or browser back button), onrampData will be null, causing the component to fail since it expects onrampData.depositInstructions.

Recommendation:

if (urlState.step === 'showDetails') {
    if (!onrampData?.transferId) {
        // Redirect back to input step if data is missing
        setUrlState({ step: 'inputAmount', amount: rawTokenAmount || null })
        return <PeanutLoading />
    }
    return <AddMoneyBankDetails />
}

2. Merge Artifact File Committed

Risk: HIGH - Repository hygiene

The file src/app/(mobile-ui)/points/page.tsx.orig should not be committed. This is a merge conflict artifact.

Action Required: Delete this file before merging.

3. Amount Validation Can Be Bypassed

Risk: MEDIUM - Business logic

Users can manually set ?amount=0.01 in the URL to bypass minimum amount validations and proceed to create an onramp with invalid amounts. While the backend should validate, client-side validation can be circumvented.

Recommendation: Add validation in the showDetails step render guard:

if (urlState.step === 'showDetails') {
    // Validate amount is still within bounds
    const amount = parseFloat(rawTokenAmount)
    if (!amount || amount < minimumAmount) {
        setUrlState({ step: 'inputAmount' })
        setError({ showError: true, errorMessage: 'Invalid amount' })
        return <PeanutLoading />
    }
    // ... rest of validation
}

⚠️ Medium Risks

4. Inconsistent State Management Pattern

The request fulfillment flow (RequestFulfillmentFlowContext) still uses context-based state management, while the main add-money flow now uses URL state. This creates maintenance burden and confusing patterns for developers.

Recommendation: Consider migrating request fulfillment flow to URL state in a follow-up PR for consistency.

5. Browser Navigation Edge Cases

Risk: MEDIUM

When users click browser back button:

  • They'll move to a previous step
  • But local state (e.g., depositDetails, liveKycStatus) may be stale or incorrect
  • API responses stored in context won't be refreshed

Current mitigation: The code does check for onrampData existence, but consider adding explicit refresh logic when navigating back to certain steps.

6. No Migration for Existing Sessions

If users have in-flight sessions with data in the old context structure, this change will lose that data. Consider the impact on active users during deployment.

✅ Positive Aspects

  1. Better UX: Users can bookmark specific steps, share URLs, and use browser back/forward naturally
  2. Performance: Likely improved since URL state changes won't cause unnecessary context re-renders
  3. Type Safety: Good use of parseAsStringEnum to ensure only valid steps/currencies
  4. Code Documentation: Excellent comments explaining the URL state pattern and rationale
  5. Consistent Implementation: Both bank and Manteca flows updated similarly

🎯 Code Quality & DRY

Duplication

There's some duplication between bank/page.tsx and MantecaAddMoney.tsx:

  • URL state setup pattern
  • Step validation logic
  • Amount handling callbacks

Recommendation: Extract a custom hook:

// hooks/useAddMoneyUrlState.ts
export function useAddMoneyUrlState<TStep extends string>(
  validSteps: TStep[],
  defaultStep: TStep
) {
  const [urlState, setUrlState] = useQueryStates({
    step: parseAsStringEnum(validSteps),
    amount: parseAsString,
  }, { history: 'push' })
  
  const step = urlState.step ?? defaultStep
  const amount = urlState.amount ?? ''
  
  return { step, amount, setUrlState }
}

Minor Issues

  • Line 58 in RhinoDeposit.view.tsx: Unnecessary type annotation change (query: { state: { data?: { status?: string } } }) - this appears unrelated to the PR's purpose
  • InputAmountStep.tsx Line 17: Type signature change for setTokenAmount is good but could use a comment explaining why it accepts both forms

⚡ Performance Impact

Assessment: NEUTRAL to SLIGHTLY POSITIVE

  • Positive: URL state changes don't trigger context consumer re-renders
  • Positive: Less prop drilling through component tree
  • Neutral: nuqs library is lightweight (~3.5KB gzipped)
  • Neutral: No additional network requests introduced

Latency: No measurable latency impact expected.

🧪 Concise QA Testing Guide

Happy Path Testing

  1. Basic Flow - Mexico Bank

    • Navigate to /add-money/mexico/bank
    • Should start at step=inputAmount
    • Enter amount: 500 MXN
    • Verify URL updates to ?step=inputAmount&amount=500
    • Click Continue → verify KYC or showDetails step appears
    • Check URL reflects current step
  2. Manteca Flow - Argentina

    • Navigate to /add-money/argentina/manteca
    • Enter 100 USD
    • Change currency to ARS
    • Verify URL has ?step=inputAmount&amount=100&currency=ARS
    • Complete KYC if needed
    • Verify deposit details appear with correct currency
  3. US Bank Flow

    • Navigate to /add-money/us/bank
    • Verify direct deposit details page loads correctly
    • Check amount displays properly

Edge Cases & Risk Testing

  1. URL Manipulation

    • Navigate to /add-money/mexico/bank?step=showDetails
    • Expected: Should redirect to inputAmount (after fix)
    • Currently: Likely crashes ❌
  2. Invalid Amount in URL

    • Navigate to /add-money/mexico/bank?step=inputAmount&amount=0.01
    • Try to proceed
    • Expected: Should show minimum amount error
  3. Browser Navigation

    • Complete flow to showDetails
    • Click browser back button
    • Verify: Previous step loads correctly
    • Verify: Amount is still populated
    • Click forward button
    • Verify: Returns to showDetails gracefully
  4. Deep Linking

    • Bookmark /add-money/mexico/bank?step=kyc&amount=500
    • Open bookmark in new session
    • Expected: Should handle gracefully (redirect or request auth)
  5. Invalid Step in URL

    • Navigate to /add-money/mexico/bank?step=invalidStep
    • Expected: Should default to inputAmount
    • Verify: parseAsStringEnum handles this correctly
  6. Currency Validation

    • Navigate to /add-money/argentina/manteca?currency=INVALID
    • Expected: Should default to USD
    • Try proceeding with deposit
  7. Concurrent Tab Testing

    • Open flow in two tabs
    • Complete step in Tab 1
    • Switch to Tab 2
    • Verify: State doesn't interfere between tabs

Regression Testing

  1. Request Fulfillment Flow

    • Test bank request fulfillment still works (uses old context pattern)
    • Verify no interference between the two patterns
  2. Existing Context Consumers

    • Test AddWithdrawRouterView still works
    • Verify setFromBankSelected functionality intact

📋 Recommendations Summary

  1. CRITICAL: Add validation guards for showDetails step
  2. CRITICAL: Delete .orig file
  3. HIGH: Add amount validation in step render guards
  4. MEDIUM: Extract shared URL state logic into custom hook
  5. MEDIUM: Consider consistency with request fulfillment flow
  6. LOW: Add integration tests for this critical flow
  7. LOW: Document the URL state pattern in architecture docs

Overall Assessment: This is a well-intentioned refactor that improves UX through URL state management, but it has HIGH RISK of runtime errors due to missing validation guards. The code quality is good with excellent documentation, but the lack of data validation for deep links is a critical gap that must be addressed before merging.

Would you like me to create issues for the follow-up items, or shall I provide complete code fixes for the critical issues?


🧠 Learnings used
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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
Repo: peanutprotocol/peanut-ui PR: 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.

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/AddMoney/components/MantecaAddMoney.tsx (1)

220-228: Redirect to inputAmount when depositDetails is missing to handle navigation back scenarios.

When a user navigates directly to ?step=depositDetails via browser history, deep link, or page refresh, depositDetails local state will be undefined while the URL step persists. The condition at line 220 prevents rendering, but the component returns null at line 230 instead of gracefully redirecting.

Add a useEffect to redirect back to inputAmount when step === 'depositDetails' but depositDetails is missing, ensuring consistent UX for these navigation scenarios.

🧹 Nitpick comments (2)
src/app/(mobile-ui)/points/page.tsx.orig (2)

65-68: Missing dependency in useEffect.

fetchUser is called inside useEffect but not included in the dependency array. While this may be intentional for mount-only behavior, it can cause issues if fetchUser reference changes.

🔎 Consider adding fetchUser to dependencies or using a ref
     useEffect(() => {
         // Re-fetch user to get the latest invitees list for showing heart Icon
         fetchUser()
-    }, [])
+    }, [fetchUser])

If mount-only behavior is truly needed, consider using a ref to track if the effect has run.


109-121: Complex inline calculation could benefit from extraction.

The progress bar width calculation is complex with multiple conditions. Consider extracting to a named function or useMemo for readability.

🔎 Suggested extraction
const progressBarWidth = useMemo(() => {
    if (tierInfo?.data.currentTier >= 2) return 100
    if (tierInfo.data.nextTierThreshold <= 0) return 0
    
    const ratio = Math.min(1, tierInfo.data.totalPoints / tierInfo.data.nextTierThreshold)
    return Math.pow(ratio, 0.6) * 100
}, [tierInfo?.data])
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f4df87 and f09ff30.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • package.json
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/app/(mobile-ui)/points/page.tsx.orig
  • src/app/ClientProviders.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/components/AddMoney/views/RhinoDeposit.view.tsx
  • src/context/OnrampFlowContext.tsx
🧰 Additional context used
🧠 Learnings (35)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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 used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1089
File: src/components/LandingPage/noFees.tsx:282-285
Timestamp: 2025-08-14T12:46:10.706Z
Learning: In the peanutprotocol/peanut-ui project, loading states are implemented only after they have been properly designed by the design team to ensure consistency across the application.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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 after scanning MERCADO_PAGO or PIX QR codes.
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.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/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.

Applied to files:

  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.

Applied to files:

  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/views/RhinoDeposit.view.tsx
  • src/context/OnrampFlowContext.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-08T20:13:42.967Z
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/AddMoney/components/InputAmountStep.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/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/views/RhinoDeposit.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/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/views/RhinoDeposit.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/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/views/RhinoDeposit.view.tsx
  • src/context/OnrampFlowContext.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/AddMoney/components/InputAmountStep.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.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/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2025-01-16T13:14:40.363Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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/app/ClientProviders.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/views/RhinoDeposit.view.tsx
  • src/context/OnrampFlowContext.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/app/(mobile-ui)/points/page.tsx.orig
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
  • src/components/AddMoney/views/RhinoDeposit.view.tsx
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 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/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2024-10-22T18:10:56.955Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:25-25
Timestamp: 2024-10-22T18:10:56.955Z
Learning: In the `src/app/request/pay/page.tsx` file, the `PreviewType` enum values are strings, so when adding `previewType` to `URLSearchParams`, there's no need to convert them to strings.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.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/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/context/OnrampFlowContext.tsx
📚 Learning: 2025-05-19T19:40:43.138Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 868
File: src/components/Payment/PaymentForm/index.tsx:284-293
Timestamp: 2025-05-19T19:40:43.138Z
Learning: When converting between USD and token amounts, always check if the token price (divisor) is valid and non-zero before performing the division to prevent Infinity, NaN, or errors. Implementing validation like `if (!tokenPrice || isNaN(tokenPrice) || tokenPrice === 0)` before division operations is crucial for handling cases where price data might be unavailable.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-11-04T17:47:06.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.328Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2024-10-23T09:38:04.446Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2024-10-04T13:10:49.199Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-04T13:10:49.199Z
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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/context/OnrampFlowContext.tsx
📚 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 845
File: src/components/Request/link/views/Create.request.link.view.tsx:81-81
Timestamp: 2025-05-13T10:05:24.057Z
Learning: In the peanut-ui project, pages that handle request flows (like Create.request.link.view.tsx) are only accessible to logged-in users who will always have a username, making null checks for user?.user.username unnecessary in these contexts.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-10-08T17:13:13.155Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1299
File: src/app/(mobile-ui)/points/page.tsx:41-51
Timestamp: 2025-10-08T17:13:13.155Z
Learning: In `src/app/(mobile-ui)/points/page.tsx`, the icon name "invite-heart" is intentionally used (not "inviter-heart") when displaying who invited the current user, as this is a deliberate design choice despite semantic differences with UserHeader usage.

Applied to files:

  • src/app/(mobile-ui)/points/page.tsx.orig
📚 Learning: 2025-07-24T13:26:10.290Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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:

  • src/app/(mobile-ui)/points/page.tsx.orig
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:0-0
Timestamp: 2025-08-13T18:22:01.941Z
Learning: In the DynamicBankAccountForm component, the countryName parameter from useParams will always resemble a country title, not a URL slug.

Applied to files:

  • src/components/AddMoney/components/AddMoneyBankDetails.tsx
📚 Learning: 2025-07-05T16:58:25.340Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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/context/OnrampFlowContext.tsx
📚 Learning: 2024-10-18T08:54:22.142Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 458
File: src/components/Offramp/Confirm.view.tsx:96-96
Timestamp: 2024-10-18T08:54:22.142Z
Learning: In the `src/components/Offramp/Confirm.view.tsx` file, it's acceptable to include crass or informal language in code comments.

Applied to files:

  • src/context/OnrampFlowContext.tsx
📚 Learning: 2024-10-24T12:45:22.708Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 478
File: src/components/Request/Create/Views/Initial.view.tsx:169-176
Timestamp: 2024-10-24T12:45:22.708Z
Learning: When calling `handleOnNext` in `src/components/Request/Create/Views/Initial.view.tsx`, it's acceptable to duplicate parameter lists for readability instead of refactoring to avoid duplication.

Applied to files:

  • src/context/OnrampFlowContext.tsx
📚 Learning: 2025-10-07T10:19:20.077Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1286
File: src/components/MultiStepBankAccountForm/FormInput.tsx:10-10
Timestamp: 2025-10-07T10:19:20.077Z
Learning: The codebase follows a strict policy of avoiding `any` types. Always use proper TypeScript types, particularly from libraries like react-hook-form (RegisterOptions, FieldErrors, UseFormSetValue, etc.) rather than using `any`.

Applied to files:

  • src/context/OnrampFlowContext.tsx
🧬 Code graph analysis (2)
src/components/AddMoney/components/MantecaAddMoney.tsx (1)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (3)
src/interfaces/interfaces.ts (1)
  • BridgeKycStatus (6-6)
src/context/OnrampFlowContext.tsx (1)
  • useOnrampFlow (90-96)
src/components/AddMoney/components/AddMoneyBankDetails.tsx (1)
  • AddMoneyBankDetails (26-376)
⏰ 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 (17)
src/context/OnrampFlowContext.tsx (3)

29-41: Good documentation for the architectural decision.

The documentation clearly explains the separation of concerns: URL-managed state (step/amount via nuqs) vs Context-managed state (transient error, navigation flag, API data). This aids maintainability and aligns with the principle of using URL state for shareable/bookmarkable values.


54-72: LGTM!

The state initialization and reset logic are correct. The empty dependency array in useCallback is appropriate since the state setters from useState are stable references. The reset function properly clears all context-managed state.


74-85: LGTM!

The useMemo dependencies are correct. State values and the memoized resetOnrampFlow callback are included, while the stable useState setters are appropriately omitted.

package.json (1)

64-64: LGTM!

The nuqs library is a well-maintained choice for URL state management in Next.js applications. Version 2.8.x is compatible with Next.js 15+ and React 19.

src/app/ClientProviders.tsx (1)

15-31: LGTM!

Correct integration of NuqsAdapter for the Next.js App Router. Placing it as the outermost wrapper ensures URL state is available throughout the component tree.

src/components/AddMoney/views/RhinoDeposit.view.tsx (1)

61-62: LGTM!

Adding explicit type annotation for the refetchInterval callback improves type safety and makes the code more self-documenting.

src/components/AddMoney/components/InputAmountStep.tsx (1)

17-17: LGTM!

The union type appropriately accommodates both URL-state callbacks ((value: string) => void) and traditional React state setters. This enables flexibility for the URL-state migration while maintaining backward compatibility.

src/components/AddMoney/components/AddMoneyBankDetails.tsx (3)

20-20: LGTM!

Appropriate import for read-only URL state consumption in the bank details view.


29-31: Good documentation of URL state source.

Clear comments explaining the data source selection pattern (URL for add-money, context for request-fulfillment). This helps maintainability.


79-82: LGTM!

The conditional amount sourcing is clean:

  • amountFromUrl ?? '' for add-money flow (URL-driven)
  • depositInstructions.amount for request-fulfillment flow (context-driven)

This aligns with the broader URL-state architecture in this PR.

src/components/AddMoney/components/MantecaAddMoney.tsx (2)

39-51: LGTM!

Well-structured URL state management with:

  • Type-safe enum parsing for step and currency
  • history: 'push' enabling browser back/forward navigation
  • Sensible defaults when URL params are absent

111-129: LGTM!

Good implementation of URL-synced handlers:

  • handleAmountChange: Uses null to remove empty values from URL (keeps URLs clean)
  • handleDenominationChange: Validates against allowed currencies before persisting
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (4)

39-48: LGTM!

Well-structured URL state setup with:

  • Type-safe step enum covering the full flow
  • Amount as a simple string
  • history: 'push' for proper navigation

100-116: Good deep-linking support with careful initialization.

The pattern of checking urlState.step before setting initial step correctly supports:

  • Deep linking (respects existing URL step)
  • Fresh navigation (derives from KYC status)
  • Waiting for user fetch before deciding

267-283: Good loading state handling during initialization.

Proper handling of the loading state while:

  1. User is being fetched (user === null)
  2. Initial step is being determined (!urlState.step)

This prevents flash of wrong content during initialization.


59-59: setError is actively used and necessary for validation; verify cleanup strategy for onrampData.

setError is used extensively throughout the component for amount validation (lines 128, 133, 137, 140, 158, 172, 190, 198), so it's not unused. setOnrampData is correctly called on line 185 to store the API response and is needed by the subsequent AddMoneyBankDetails component.

However, the component never calls resetOnrampFlow() on unmount or when navigating away from the entire onramp flow. If users navigate back to /add-money or elsewhere, stale onrampData persists in the context. Clarify whether this is intentional (to support re-entry into the flow) or if cleanup is needed.

Likely an incorrect or invalid review comment.

src/app/(mobile-ui)/points/page.tsx.orig (1)

1-1: Clarify the purpose of the .orig file extension.

This file has a .orig extension, which typically indicates a merge conflict backup file. Before proceeding, confirm whether this is:

  1. An intentional file that should be committed (in which case, rename it to page.tsx)
  2. A leftover artifact that should be removed

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

🧹 Nitpick comments (3)
src/components/AddMoney/components/MantecaAddMoney.tsx (1)

81-95: Amount validation effect is correct, but consider adding a guard in the submit handler.

The validation correctly checks against MIN_MANTECA_DEPOSIT_AMOUNT and MAX_MANTECA_DEPOSIT_AMOUNT. However, handleAmountSubmit (line 131) doesn't re-validate before calling the API. If a user deep-links with an invalid amount and clicks submit before the validation effect runs, the API call could proceed with an invalid amount.

Consider adding an early return in handleAmountSubmit:

🔎 Suggested guard in handleAmountSubmit
 const handleAmountSubmit = useCallback(async () => {
     if (!selectedCountry?.currency) return
     if (isCreatingDeposit) return
+    
+    // Re-validate amount before submitting
+    const paymentAmount = parseUnits(usdAmount || '0', PEANUT_WALLET_TOKEN_DECIMALS)
+    if (paymentAmount < parseUnits(MIN_MANTECA_DEPOSIT_AMOUNT.toString(), PEANUT_WALLET_TOKEN_DECIMALS) ||
+        paymentAmount > parseUnits(MAX_MANTECA_DEPOSIT_AMOUNT.toString(), PEANUT_WALLET_TOKEN_DECIMALS)) {
+        return
+    }
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (2)

28-31: Type definition duplicates pattern from MantecaAddMoney.

Both BridgeBankStep here and MantecaStep in MantecaAddMoney follow the same pattern. Consider extracting a shared utility or type if URL state patterns are being standardized across the codebase.


37-48: URL state setup is consistent with MantecaAddMoney.

The pattern is correct. As noted in PR comments, there's opportunity to extract shared URL state logic into a custom hook to reduce duplication between this file and MantecaAddMoney.tsx.

🔎 Example shared hook signature
// hooks/useAddMoneyUrlState.ts
type AddMoneyStep = 'inputAmount' | 'kyc' | 'collectUserDetails' | 'showDetails' | 'depositDetails'

export function useAddMoneyUrlState<T extends AddMoneyStep>(
  allowedSteps: T[],
  options?: { includeCurrency?: boolean }
) {
  // Shared URL state logic
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f09ff30 and 798b7bb.

📒 Files selected for processing (2)
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
🧰 Additional context used
🧠 Learnings (26)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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: 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: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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 used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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 after scanning MERCADO_PAGO or PIX QR codes.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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.
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-22T18:10:56.955Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:25-25
Timestamp: 2024-10-22T18:10:56.955Z
Learning: In the `src/app/request/pay/page.tsx` file, the `PreviewType` enum values are strings, so when adding `previewType` to `URLSearchParams`, there's no need to convert them to strings.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-23T09:38:04.446Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:0-0
Timestamp: 2025-08-13T18:22:01.941Z
Learning: In the DynamicBankAccountForm component, the countryName parameter from useParams will always resemble a country title, not a URL slug.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-04T13:10:49.199Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-04T13:10:49.199Z
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/app/(mobile-ui)/add-money/[country]/bank/page.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-11-04T17:47:06.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.328Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 845
File: src/components/Request/link/views/Create.request.link.view.tsx:81-81
Timestamp: 2025-05-13T10:05:24.057Z
Learning: In the peanut-ui project, pages that handle request flows (like Create.request.link.view.tsx) are only accessible to logged-in users who will always have a username, making null checks for user?.user.username unnecessary in these contexts.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-05-19T19:40:43.138Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 868
File: src/components/Payment/PaymentForm/index.tsx:284-293
Timestamp: 2025-05-19T19:40:43.138Z
Learning: When converting between USD and token amounts, always check if the token price (divisor) is valid and non-zero before performing the division to prevent Infinity, NaN, or errors. Implementing validation like `if (!tokenPrice || isNaN(tokenPrice) || tokenPrice === 0)` before division operations is crucial for handling cases where price data might be unavailable.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
🧬 Code graph analysis (2)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (3)
src/interfaces/interfaces.ts (1)
  • BridgeKycStatus (6-6)
src/context/OnrampFlowContext.tsx (1)
  • useOnrampFlow (90-96)
src/components/AddMoney/components/AddMoneyBankDetails.tsx (1)
  • AddMoneyBankDetails (26-376)
src/components/AddMoney/components/MantecaAddMoney.tsx (1)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
⏰ 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 (7)
src/components/AddMoney/components/MantecaAddMoney.tsx (3)

20-26: Well-structured type definitions for URL state.

Good use of parseAsStringEnum for type-safe step and currency parsing. The types are clear and self-documenting.

Consider extracting MantecaStep and CurrencyDenomination to a shared types file if they're needed elsewhere, or if you want to share patterns with the bank flow.


37-51: URL state setup looks good.

The use of history: 'push' enables proper browser navigation, and the derived values with defaults (lines 49-51) provide a clean API for consumers.


111-129: Handlers correctly sync state to URL.

Good defensive validation in handleDenominationChange to ensure only valid currencies are persisted. Using null to remove empty values from URL is the correct nuqs pattern.

src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (4)

100-116: Initial step determination logic is well-designed.

Good approach to:

  1. Respect existing URL step (enabling deep links)
  2. Wait for user data before determining initial step
  3. Route based on KYC status

The effect correctly includes urlState.step in dependencies to prevent re-running after step is set.


146-162: Amount handling is correctly implemented.

The handler uses null to remove empty values from URL, and the validation effect properly validates on change. The separation of concerns is clean.


266-283: Loading state handling is appropriate.

The two loading checks serve different purposes:

  1. Lines 267-269: Initial load while fetching user with no URL step
  2. Lines 281-283: Fallback while determining initial step

This prevents flickering and ensures smooth UX during initialization.


260-264: KYC modal opening correctly triggered via effect.

Unlike the showDetails guard, this correctly uses useEffect to trigger side effects (modal opening) based on URL state.

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

🧹 Nitpick comments (4)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (1)

100-116: Consider extracting initial step logic to a shared hook.

This initial step determination pattern (respecting URL state, waiting for user data, routing based on KYC status) is likely duplicated across add-money flows. Extracting it to a shared hook would improve maintainability and consistency.

Example structure:

// hooks/useOnrampInitialStep.ts
function useOnrampInitialStep(currentStep: string | null, kycStatus: BridgeKycStatus | undefined) {
  // Returns initial step and loading state
}
src/components/AddMoney/components/MantecaAddMoney.tsx (3)

39-46: Consider history: 'replace' to avoid polluting browser history.

Using history: 'push' creates a new browser history entry for every URL state change, including each keystroke when typing an amount. This can make the browser back button cumbersome for users. For intermediate UI states like amount and currency, consider using history: 'replace' instead.

🔎 Suggested change
     const [urlState, setUrlState] = useQueryStates(
         {
             step: parseAsStringEnum<MantecaStep>(['inputAmount', 'depositDetails']),
             amount: parseAsString,
             currency: parseAsStringEnum<CurrencyDenomination>(['USD', 'ARS', 'BRL', 'MXN', 'EUR']),
         },
-        { history: 'push' }
+        { history: 'replace' }
     )

119-129: Extract valid currencies to avoid duplication.

The validCurrencies array duplicates the CurrencyDenomination type definition (line 26). If the type is updated, this array must be manually synchronized, creating a maintenance burden.

🔎 Suggested refactor

Extract the valid currencies to a shared constant at the top of the file:

 // Currency denomination type for URL state
 type CurrencyDenomination = 'USD' | 'ARS' | 'BRL' | 'MXN' | 'EUR'
+const VALID_CURRENCIES: CurrencyDenomination[] = ['USD', 'ARS', 'BRL', 'MXN', 'EUR']

Then use it in the handler:

     const handleDenominationChange = useCallback(
         (value: string) => {
             // Only persist valid currency denominations to URL
-            const validCurrencies: CurrencyDenomination[] = ['USD', 'ARS', 'BRL', 'MXN', 'EUR']
-            if (validCurrencies.includes(value as CurrencyDenomination)) {
+            if (VALID_CURRENCIES.includes(value as CurrencyDenomination)) {
                 setUrlState({ currency: value as CurrencyDenomination })
             }
         },
         [setUrlState]
     )

131-177: Add explicit validation before non-null assertion on amount.

Line 152 uses amount! with a non-null assertion operator, but if both usdAmount and currencyAmount are empty/undefined, this could pass an invalid value to the API. While the UI likely prevents this scenario, adding an explicit guard improves code safety and clarity.

🔎 Suggested guard
     const handleAmountSubmit = useCallback(async () => {
         if (!selectedCountry?.currency) return
         if (isCreatingDeposit) return
 
+        // Validate we have a valid amount
+        if (!usdAmount || usdAmount === '0.00' || error) {
+            return
+        }
+
         // check if we still need to determine KYC status
         if (isMantecaKycRequired === null) {
             // still loading/determining KYC status, don't proceed yet
             return
         }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 798b7bb and 0b58d52.

📒 Files selected for processing (2)
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
🧰 Additional context used
🧠 Learnings (27)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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 used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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 after scanning MERCADO_PAGO or PIX QR codes.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-22T18:10:56.955Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:25-25
Timestamp: 2024-10-22T18:10:56.955Z
Learning: In the `src/app/request/pay/page.tsx` file, the `PreviewType` enum values are strings, so when adding `previewType` to `URLSearchParams`, there's no need to convert them to strings.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-23T09:38:04.446Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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/[country]/bank/page.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/app/(mobile-ui)/add-money/[country]/bank/page.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/bank/page.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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-11-04T17:47:06.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.328Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 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/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 845
File: src/components/Request/link/views/Create.request.link.view.tsx:81-81
Timestamp: 2025-05-13T10:05:24.057Z
Learning: In the peanut-ui project, pages that handle request flows (like Create.request.link.view.tsx) are only accessible to logged-in users who will always have a username, making null checks for user?.user.username unnecessary in these contexts.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-05-19T19:40:43.138Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 868
File: src/components/Payment/PaymentForm/index.tsx:284-293
Timestamp: 2025-05-19T19:40:43.138Z
Learning: When converting between USD and token amounts, always check if the token price (divisor) is valid and non-zero before performing the division to prevent Infinity, NaN, or errors. Implementing validation like `if (!tokenPrice || isNaN(tokenPrice) || tokenPrice === 0)` before division operations is crucial for handling cases where price data might be unavailable.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
🧬 Code graph analysis (2)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (4)
src/interfaces/interfaces.ts (1)
  • BridgeKycStatus (6-6)
src/context/OnrampFlowContext.tsx (1)
  • useOnrampFlow (90-96)
src/components/Global/PeanutLoading/index.tsx (1)
  • PeanutLoading (4-28)
src/components/AddMoney/components/AddMoneyBankDetails.tsx (1)
  • AddMoneyBankDetails (26-376)
src/components/AddMoney/components/MantecaAddMoney.tsx (1)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
⏰ 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 (7)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (2)

28-48: Clean URL state setup with nuqs.

The URL state management is well-structured with proper type safety and enum validation for steps. Using 'push' history mode enables browser navigation, and parsing amount as string is appropriate for decimal inputs.


266-271: Excellent fix for deep-link validation.

This useEffect properly guards against accessing showDetails without required onramp data, addressing the critical issue from the previous review. The redirect to inputAmount with the loading state check below (lines 335-339) provides a smooth user experience for deep-link and back-navigation scenarios.

Based on the commit message and PR objectives, this fix successfully prevents crashes when showDetails is accessed directly via deep link or browser navigation.

src/components/AddMoney/components/MantecaAddMoney.tsx (5)

97-102: LGTM - Query invalidation is appropriate.

Invalidating the transactions query when entering the deposit details step ensures users see fresh data. The dependencies are correct.


186-191: LGTM - Proper handling of deep link and back navigation edge case.

This useEffect correctly addresses the previous review concern about state updates during render. By moving the redirect logic to useEffect, it prevents React warnings and potential render loops while safely guarding against accessing depositDetails without required data.


227-231: LGTM - Safe render guard complements the redirect useEffect.

Returning null when depositDetails is missing prevents any attempt to render with incomplete data while the redirect useEffect executes. This pattern correctly handles the deep-link edge case.


37-51: URL state pattern is well-implemented for this POC.

The URL-based state management using nuqs is clean and type-safe. The separation between URL-persisted state (step, amount, currency) and transient local state (depositDetails, error, loading) is appropriate. The inline documentation at lines 37-38 showing example URLs is helpful for maintainability.


82-95: The error state is already handled via the submit button's disabled state.

The InputAmountStep component properly disables the submit button when an error exists (line 71: disabled={!!error || isLoading || !parseFloat(tokenAmount)}). This prevents handleAmountSubmit from being triggered through the UI when validation fails. An explicit error check in handleAmountSubmit would be redundant given the button-level protection already in place.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Jan 1, 2026
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/AddMoney/components/MantecaAddMoney.tsx (1)

139-178: Critical: Add server-side or synchronous validation before API call to prevent amount bypass.

As noted in PR objectives, users can deep-link with tiny amounts (e.g., ?amount=0.01&currency=USD) and potentially bypass client-side validation. The validation effect on lines 83-96 runs asynchronously on usdAmount changes, but there's a race window where the submit button could be clicked before the USD equivalent is calculated and validated.

Add synchronous validation in handleAmountSubmit before the API call:

🔎 Proposed fix
 const handleAmountSubmit = useCallback(async () => {
     if (!selectedCountry?.currency) return
     if (isCreatingDeposit) return
+    
+    // Validate amount before proceeding (synchronous check to prevent bypass)
+    const amountToValidate = currentDenomination === 'USD' 
+        ? parseFloat(displayedAmount || '0')
+        : parseFloat(usdAmount || '0')
+    
+    if (amountToValidate < MIN_MANTECA_DEPOSIT_AMOUNT) {
+        setError(`Deposit amount must be at least $${MIN_MANTECA_DEPOSIT_AMOUNT}`)
+        return
+    }
+    if (amountToValidate > MAX_MANTECA_DEPOSIT_AMOUNT) {
+        setError(`Deposit amount exceeds maximum limit of $${MAX_MANTECA_DEPOSIT_AMOUNT}`)
+        return
+    }

     // check if we still need to determine KYC status
     if (isMantecaKycRequired === null) {
🧹 Nitpick comments (3)
src/components/Global/AmountInput/index.tsx (1)

133-149: Consider adding setDisplayedAmount to the dependency array.

The setDisplayedAmount callback is used inside this effect but not listed in the dependency array. If the parent component recreates this callback on re-render (e.g., without useCallback), the effect may use a stale reference.

🔎 Proposed fix
-    }, [displayValue, alternativeDisplayValue, displaySymbol, secondaryDenomination])
+    }, [displayValue, alternativeDisplayValue, displaySymbol, secondaryDenomination, setDisplayedAmount])
src/components/AddMoney/components/MantecaAddMoney.tsx (2)

199-209: Verify prop mapping between tokenAmount/setTokenAmount and actual values.

The naming is potentially confusing:

  • tokenAmount={displayedAmount} - this is the displayed value (could be ARS, USD, etc.)
  • setTokenAmount={handleUsdAmountChange} - but this handler sets USD amount

This asymmetry (display value in, USD value out) relies on AmountInput passing the secondary (USD) amount to setSecondaryAmount. While functionally correct, consider renaming for clarity or adding a comment explaining the mapping.


51-53: Consider initializing usdAmount from URL state when denomination is USD.

If a user deep-links with ?amount=100&currency=USD, usdAmount starts empty and only gets populated after AmountInput mounts and triggers its effect. This contributes to the validation timing gap.

🔎 Proposed fix
-    // Local UI state for tracking both amounts (needed for API call and validation)
-    const [usdAmount, setUsdAmount] = useState<string>('')
-    const [localCurrencyAmount, setLocalCurrencyAmount] = useState<string>('')
+    // Local UI state for tracking both amounts (needed for API call and validation)
+    // Initialize from URL state if available and denomination matches
+    const [usdAmount, setUsdAmount] = useState<string>(() => 
+        currentDenomination === 'USD' ? (urlState.amount ?? '') : ''
+    )
+    const [localCurrencyAmount, setLocalCurrencyAmount] = useState<string>(() =>
+        currentDenomination !== 'USD' ? (urlState.amount ?? '') : ''
+    )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b58d52 and 278a994.

📒 Files selected for processing (5)
  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
  • src/components/AddMoney/components/MantecaDepositShareDetails.tsx
  • src/components/Global/AmountInput/index.tsx
🧰 Additional context used
🧠 Learnings (27)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/MantecaDepositShareDetails.tsx
  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-08T20:13:42.967Z
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/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
📚 Learning: 2024-10-23T09:38:04.446Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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/[country]/[regional-method]/page.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.

Applied to files:

  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.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/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.

Applied to files:

  • src/components/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.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/Global/AmountInput/index.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-12-02T17:21:45.515Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/context/walletContext/walletContext.tsx:87-88
Timestamp: 2024-12-02T17:21:45.515Z
Learning: When converting `totalBalance` (in USD) to a `BigInt` balance in `src/context/walletContext/walletContext.tsx`, multiplying by `1e6` is intentional to maintain compatibility with USDC's 6 decimal places. The application displays only 2 decimal places, so this level of precision is sufficient.

Applied to files:

  • src/components/Global/AmountInput/index.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/AddMoney/components/InputAmountStep.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.tsx
📚 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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/components/AddMoney/components/MantecaAddMoney.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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 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:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-26T15:25:53.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-05-19T19:40:43.138Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 868
File: src/components/Payment/PaymentForm/index.tsx:284-293
Timestamp: 2025-05-19T19:40:43.138Z
Learning: When converting between USD and token amounts, always check if the token price (divisor) is valid and non-zero before performing the division to prevent Infinity, NaN, or errors. Implementing validation like `if (!tokenPrice || isNaN(tokenPrice) || tokenPrice === 0)` before division operations is crucial for handling cases where price data might be unavailable.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
📚 Learning: 2025-11-04T17:47:06.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.328Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/components/AddMoney/components/MantecaAddMoney.tsx
🧬 Code graph analysis (1)
src/components/AddMoney/components/MantecaAddMoney.tsx (1)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
⏰ 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 (8)
src/components/AddMoney/components/MantecaDepositShareDetails.tsx (1)

20-27: Props simplified to align with URL-driven navigation - LGTM.

The removal of source and onBack props in favor of router.back() is appropriate for the URL-state pattern. Since navigation state is now persisted in the URL, router.back() will correctly restore the previous step.

Also applies to: 85-85

src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx (1)

18-18: Prop removal aligns with component signature change - LGTM.

The removal of source="regionalMethod" is consistent with MantecaAddMoney becoming a prop-less component that derives its state from URL params.

src/components/AddMoney/components/InputAmountStep.tsx (2)

17-17: Type widening for setTokenAmount is appropriate.

The union type ((value: string) => void) | React.Dispatch<React.SetStateAction<string>> allows both simple callback handlers and React state setters, supporting the URL-driven pattern while maintaining backward compatibility.


22-23: New props for URL-state integration forwarded correctly - LGTM.

The initialDenomination and setDisplayedAmount props are properly accepted and forwarded to AmountInput, enabling the parent component to control initial state and track displayed values for URL synchronization.

Also applies to: 35-36, 50-55

src/components/Global/AmountInput/index.tsx (1)

71-83: Initial denomination logic is well-implemented.

The lazy initializer correctly validates initialDenomination against available denominations and falls back to primaryDenomination.symbol when invalid or not provided. This prevents URL state with invalid currency values from causing issues.

src/components/AddMoney/components/MantecaAddMoney.tsx (3)

20-49: URL state setup using nuqs looks good.

The use of parseAsStringEnum for type-safe step and currency values, combined with history: 'push' for browser navigation support, is well-suited for the deep-link and bookmarking requirements. Based on learnings, this aligns with the codebase preference for URL state when synchronous updates are needed.


187-192: Good: Redirect logic moved to useEffect as suggested in past review.

This correctly addresses the previous review comment about state updates during render. The redirect now runs as a side effect rather than during the render phase.


230-236: Good defensive guard for missing deposit details.

The if (!depositDetails) return null guard prevents rendering MantecaDepositShareDetails without required data. Combined with the useEffect redirect on lines 187-192, this provides defense-in-depth against invalid deep-link states.

@Hugo0
Copy link
Contributor

Hugo0 commented Jan 2, 2026

@coderabbitai review in depth again

Evaluate:

risk of introducing new bugs and breaking existing functionality
performance impact and latency
code quality and DRY

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Tip

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

🧩 Analysis chain




🏁 Script executed:

#!/bin/bash

echo "=== Check if merge artifact .orig file still exists ==="
if [ -f "src/app/(mobile-ui)/points/page.tsx.orig" ]; then
    echo "❌ CRITICAL: Merge artifact file still exists"
    ls -la "src/app/(mobile-ui)/points/page.tsx.orig"
else
    echo "✓ Merge artifact file not found"
fi

echo ""
echo "=== Get current PR diff to see latest state ==="
gh pr diff 1602 --name-only

echo ""
echo "=== Examine bank/page.tsx validation logic for showDetails step ==="
cat "src/app/(mobile-ui)/add-money/[country]/bank/page.tsx" | grep -A 30 "urlState.step === 'showDetails'"

echo ""
echo "=== Check if onrampData validation exists before showDetails render ==="
rg -n "showDetails.*onrampData|onrampData.*showDetails" -g "*.tsx" -g "*.ts" -C5

Length of output: 4522


🏁 Script executed:

#!/bin/bash

echo "=== Examine MantecaAddMoney.tsx for validation patterns ==="
cat "src/components/AddMoney/components/MantecaAddMoney.tsx" | grep -A 20 "step === 'depositDetails'"

echo ""
echo "=== Check amount validation in both flows ==="
echo "--- Bank flow amount validation ---"
rg -n "minimumAmount|parseFloat.*amount|amount.*validation" "src/app/(mobile-ui)/add-money/[country]/bank/page.tsx" -C3

echo ""
echo "--- Manteca flow amount validation ---"
rg -n "minimumAmount|parseFloat.*amount|amount.*validation" "src/components/AddMoney/components/MantecaAddMoney.tsx" -C3

Length of output: 3673


🏁 Script executed:

#!/bin/bash

echo "=== Analyze duplication patterns between bank and Manteca ==="
echo ""
echo "--- URL state setup in bank/page.tsx ---"
cat "src/app/(mobile-ui)/add-money/[country]/bank/page.tsx" | head -100 | grep -A 10 "useQueryStates"

echo ""
echo "--- URL state setup in MantecaAddMoney.tsx ---"
cat "src/components/AddMoney/components/MantecaAddMoney.tsx" | head -100 | grep -A 10 "useQueryStates"

echo ""
echo "=== Check for shared hooks or utilities ==="
fd -e ts -e tsx . src/hooks | xargs grep -l "urlState\|useQueryStates" 2>/dev/null || echo "No shared URL state hooks found"

Length of output: 1759


🏁 Script executed:

#!/bin/bash

echo "=== Check RequestFulfillmentFlowContext for consistency ==="
cat "src/context/RequestFulfillmentFlowContext.tsx" | head -80

echo ""
echo "=== Find all usages of OnrampFlowContext to assess migration impact ==="
rg -l "useOnrampFlow\(" -g "*.tsx" -g "*.ts"

echo ""
echo "=== Check for error boundaries around new URL state flows ==="
rg -n "ErrorBoundary|componentDidCatch|error.*boundary" -g "*.tsx" -g "*.ts" | grep -i "addmoney\|onramp" || echo "No error boundaries found in add-money flows"

Length of output: 4036


🏁 Script executed:

#!/bin/bash

echo "=== Analyze AddMoneyBankDetails data dependencies ==="
cat "src/components/AddMoney/components/AddMoneyBankDetails.tsx" | head -150

echo ""
echo "=== Check type definitions for URL state ==="
ast-grep --pattern 'type $NAME = $$$'  | head -50

echo ""
echo "=== Check if there are integration or E2E tests ==="
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts . | grep -i "addmoney\|onramp\|bank" || echo "No test files found for add-money flows"

Length of output: 9332


@Hugo0, I've completed a fresh in-depth review of the current state of PR 1602. Here's my comprehensive assessment:

🎉 Improvements Since Last Review

Great progress! Several critical issues have been addressed:

  1. Merge artifact removed - page.tsx.orig file is gone
  2. Deep-link validation added - Both flows now have proper guards:
    • Bank flow (lines 268-271): useEffect redirects to inputAmount if showDetails accessed without onrampData
    • Manteca flow: Similar pattern for depositDetails step
  3. Render guards in place - Shows loading state while redirecting

🚨 Risk Assessment: MEDIUM (reduced from HIGH)

Remaining Risk Areas

1. Amount Validation Still Bypassable (MEDIUM Risk)

Current state: Input validation exists, but URL manipulation still possible.

// Line 136-138: Validates on input
if (amount && amount < minimumAmount) {
    setError({ showError: true, errorMessage: `Minimum deposit is ${minimumAmount}.` })
    return false
}

Problem: User can manually edit URL to ?amount=0.01&step=showDetails and the useEffect redirect only checks for onrampData existence, not amount validity.

Recommendation: Add amount validation to the redirect guard:

useEffect(() => {
    if (urlState.step === 'showDetails') {
        const amount = parseFloat(rawTokenAmount)
        if (!onrampData?.transferId || !amount || amount < minimumAmount) {
            setUrlState({ step: 'inputAmount' })
            if (amount && amount < minimumAmount) {
                setError({ showError: true, errorMessage: `Minimum deposit is ${minimumAmount}.` })
            }
        }
    }
}, [urlState.step, onrampData?.transferId, setUrlState, rawTokenAmount, minimumAmount])

2. Browser Navigation Edge Cases (LOW-MEDIUM Risk)

When users use browser back/forward:

  • URL state updates immediately
  • But context state (onrampData, depositDetails) may be stale
  • Current mitigation is adequate (redirect guards) but could cause momentary flashing

3. Inconsistent Pattern - Request Fulfillment Flow (MEDIUM Risk)

RequestFulfillmentFlowContext still uses enum-based step management:

export enum RequestFulfillmentBankFlowStep {
    BankCountryList = 'bank-country-list',
    DepositBankDetails = 'deposit-bank-details',
    OnrampConfirmation = 'onramp-confirmation',
    CollectUserDetails = 'collect-user-details',
}

This creates:

  • Two different mental models for developers
  • Potential for confusion when maintaining/extending
  • Mixed state patterns in the same codebase area

Impact: Maintenance burden, increased cognitive load, harder onboarding for new developers.

4. No Error Boundaries (LOW Risk)

If URL state parsing fails or nuqs encounters issues, there's no graceful degradation. Consider wrapping add-money routes in error boundaries.

5. No Test Coverage (MEDIUM Risk)

Zero tests found for these critical flows. This increases regression risk significantly.

⚡ Performance Impact: NEUTRAL to SLIGHTLY POSITIVE

Positive Impacts

  • Fewer re-renders: URL state changes don't trigger context consumer re-renders
  • Lightweight library: nuqs is ~3.5KB gzipped
  • No additional network requests: URL parsing is synchronous

Neutral Impacts

  • Browser history operations: history: 'push' adds entries, but this is expected behavior
  • Parse overhead: Minimal - parseAsString and parseAsStringEnum are simple operations

Performance Measurements

No measurable latency regression expected. URL updates are instant, and the validation guards prevent unnecessary API calls.

Assessment: ✅ Performance impact is positive overall.

🎨 Code Quality & DRY: GOOD with Missed Opportunities

Positive Aspects

  1. Excellent documentation: Clear comments explaining URL state pattern

    // URL state - persisted in query params
    // Example: /add-money/mexico/bank?step=inputAmount&amount=500
  2. Type safety: Good use of parseAsStringEnum with TypeScript types

    type BridgeBankStep = 'inputAmount' | 'kyc' | 'collectUserDetails' | 'showDetails'
  3. Consistent validation pattern: Both flows use useEffect + render guards

  4. Separation of concerns: URL state separate from business logic state

DRY Violations (MEDIUM Priority)

Duplication detected between bank/page.tsx and MantecaAddMoney.tsx:

Both files have nearly identical:

const [urlState, setUrlState] = useQueryStates(
    {
        step: parseAsStringEnum<StepType>([...steps]),
        amount: parseAsString,
    },
    { history: 'push' }
)

// Redirect guard pattern
useEffect(() => {
    if (urlState.step === 'targetStep' && !requiredData) {
        setUrlState({ step: 'inputAmount' })
    }
}, [urlState.step, requiredData, setUrlState])

Recommendation: Extract shared logic:

// hooks/useAddMoneyUrlState.ts
export function useAddMoneyUrlState<TStep extends string>({
    validSteps,
    defaultStep,
    additionalParams = {}
}: {
    validSteps: TStep[]
    defaultStep: TStep
    additionalParams?: Record<string, any>
}) {
    const [urlState, setUrlState] = useQueryStates(
        {
            step: parseAsStringEnum<TStep>(validSteps),
            amount: parseAsString,
            ...additionalParams,
        },
        { history: 'push' }
    )
    
    const step = urlState.step ?? defaultStep
    const amount = urlState.amount ?? ''
    
    return { 
        step, 
        amount, 
        urlState,
        setUrlState,
        rawAmount: urlState.amount 
    }
}

// Usage in bank/page.tsx
const { step, amount, setUrlState } = useAddMoneyUrlState({
    validSteps: ['inputAmount', 'kyc', 'collectUserDetails', 'showDetails'] as const,
    defaultStep: 'inputAmount'
})

// Usage in MantecaAddMoney.tsx
const { step, amount, setUrlState } = useAddMoneyUrlState({
    validSteps: ['inputAmount', 'depositDetails'] as const,
    defaultStep: 'inputAmount',
    additionalParams: {
        currency: parseAsStringEnum<CurrencyDenomination>(['USD', 'ARS', 'BRL', 'MXN', 'EUR'])
    }
})

Benefits:

  • DRY principle maintained
  • Single source of truth for URL state pattern
  • Easier to add validation logic in one place
  • Reduced maintenance burden

Minor Code Quality Issues

  1. Unrelated change (line 58, RhinoDeposit.view.tsx): Type annotation added seems unrelated to URL state migration
  2. Mixed concerns: AddMoneyBankDetails handles both flows with conditional logic - consider splitting

📋 Updated Recommendations (Prioritized)

HIGH Priority

  1. Add amount validation to redirect guards - Prevents invalid amounts from bypassing validation
  2. Extract shared URL state hook - Reduces duplication, improves maintainability

MEDIUM Priority

  1. Add integration tests - Critical flows should have test coverage:

    • Happy path: input → KYC → details
    • Deep linking edge cases
    • Browser back/forward navigation
    • Amount validation bypass attempts
  2. Consider migrating RequestFulfillmentFlow - For pattern consistency

  3. Document the pattern - Add architecture decision record (ADR) explaining:

    • Why URL state over context
    • When to use each pattern
    • Migration guide for future flows

LOW Priority

  1. Add error boundaries - Wrap add-money routes for graceful degradation
  2. Remove unrelated changes - Clean up type annotation in RhinoDeposit.view.tsx
  3. Consider splitting AddMoneyBankDetails - Separate add-money and request-fulfillment concerns

🎯 Summary

Aspect Rating Notes
Risk 🟡 MEDIUM Critical issues fixed, remaining risks manageable
Performance 🟢 POSITIVE Fewer re-renders, no latency impact
Code Quality 🟡 GOOD Well-documented, type-safe, but DRY opportunities missed
Breaking Changes 🟢 LOW OnrampFlowContext changes minimal, backwards compatible
Test Coverage 🔴 NONE Critical gap - should add tests

Overall: This PR is in much better shape than the initial review. The critical deep-link validation issues are resolved, and the implementation is solid. The main concerns are:

  1. DRY violations (duplication)
  2. Lack of tests
  3. Minor amount validation bypass

These are not blockers for merging but should be addressed in follow-up work.


Would you like me to:

  1. Create issues for the follow-up items (tests, shared hook extraction, request fulfillment migration)?
  2. Provide complete code for the shared hook extraction?
  3. Draft integration test scenarios?

🧠 Learnings used
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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
Repo: peanutprotocol/peanut-ui PR: 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: 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.

Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.

Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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.

@Hugo0
Copy link
Contributor

Hugo0 commented Jan 2, 2026

@coderabbitai also review the new library nuqs. Is this PR a complete migration to it? Or do we have inconsistent state management now?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== All nuqs usages in the codebase ==="
rg -n "from ['\"]nuqs|import.*nuqs" -g "*.tsx" -g "*.ts" -C2

echo ""
echo "=== All useQueryStates usages ==="
rg -n "useQueryStates|useQueryState" -g "*.tsx" -g "*.ts" -C3

echo ""
echo "=== All Context providers in the codebase ==="
rg -n "createContext|Context\.Provider" -g "*.tsx" -g "*.ts" -l | head -30

echo ""
echo "=== State management in add-money related files ==="
fd -e tsx -e ts "add-money|onramp|deposit" src/app src/components | xargs rg -l "useState|useContext|createContext" 2>/dev/null

echo ""
echo "=== Check if there are other flows that could use URL state but don't ==="
rg -n "useState.*step|setStep\(|currentStep" -g "*.tsx" -g "*.ts" src/app src/components | grep -i "flow\|wizard\|multi" | head -20

Length of output: 9571


🌐 Web query:

What is nuqs library for Next.js and React? Is it production ready?

💡 Result:

Short answer

  • Nuqs is a tiny (≈5–6KB gz) type-safe URL-search-params state manager for React/Next.js that exposes a useState-like API (useQueryState / useQueryStates), built-in parsers/serializers, history controls, adapters for multiple routers (Next.js app/pages, React SPA, Remix, React Router, TanStack Router), and server‑component helpers. [1][2][4]

Is it production ready?

  • Yes — nuqs is actively maintained, version 2+ targets production frameworks (Next.js >=14.2, React 18.3/19, etc.), is tested against Next.js releases, has adapters and a testing adapter, and is used in production by multiple teams. That said, the maintainers note a Next.js-specific caveat around Next.js’s internal URLSearchParams context and are collaborating with Next.js to mitigate it; check the docs/release notes for adapter guidance and the latest compatibility notes before upgrading. [1][2][4][3]

Sources

  • Official docs / features & adapters. [1][4]
  • Installation / version compatibility (Next.js >=14.2, React versions). [2]
  • Release/compatibility notes (InfoQ coverage mentioning Next.js caveat and ongoing fixes). [3]

@Hugo0, great question! Let me provide a comprehensive analysis of nuqs and the migration status.

📚 About nuqs Library

Based on my research, nuqs is production-ready and well-suited for this use case:

nuqs is a tiny (~5-6KB gzipped) type-safe URL-search-params state manager for React/Next.js that provides:

  • useState-like API (useQueryState / useQueryStates)
  • Built-in parsers/serializers (like parseAsString, parseAsStringEnum)
  • History controls (push, replace, replaceIn)
  • Adapters for multiple frameworks including Next.js App Router
  • Active maintenance and tested against Next.js releases

Verdict on library choice: ✅ Excellent choice for URL-based state management in Next.js.

🔍 Migration Status: PARTIAL (Not Complete)

✅ What Now Uses nuqs (URL State)

Add-Money flows only:

  1. Bank add-money flow (src/app/(mobile-ui)/add-money/[country]/bank/page.tsx)

    • Step management: inputAmount | kyc | collectUserDetails | showDetails
    • Amount tracking
  2. Manteca regional method (src/components/AddMoney/components/MantecaAddMoney.tsx)

    • Step management: inputAmount | depositDetails
    • Amount tracking
    • Currency selection
  3. AddMoneyBankDetails (reads URL state)

    • Reads amount from URL for display

❌ What Still Uses Context/useState (Old Patterns)

Multiple flows NOT migrated:

  1. RequestFulfillmentFlowContext (src/context/RequestFulfillmentFlowContext.tsx)

    export enum RequestFulfillmentBankFlowStep {
        BankCountryList = 'bank-country-list',
        DepositBankDetails = 'deposit-bank-details',
        OnrampConfirmation = 'onramp-confirmation',
        CollectUserDetails = 'collect-user-details',
    }
    • Still uses enum-based step management
    • Still uses context for currentStep
    • This is the most problematic inconsistency since it's another onramp-related flow
  2. OnrampFlowContext (partially reduced but still exists)

    • Removed: amountToOnramp, currentView
    • Still has: error, fromBankSelected, onrampData
    • Mixed pattern: URL state for steps/amount, context for transient data
  3. Other Context-Based Flows:

    • WithdrawFlowContext - withdraw flow steps
    • ClaimBankFlowContext - claim flow steps
    • LinkSendFlowContext - link send flow steps
    • SetupFlowContext - setup wizard (currentStep with useState)
    • DirectSendFlowContext - direct payment flow
    • SemanticRequestFlowContext - semantic request flow
    • ContributePotFlowContext - contribute to pot flow
  4. Component-Level useState:

    • MantecaFlowManager.tsx (claim flow): Uses useState<MercadoPagoStep>
    • Various other wizards/multi-step forms

⚖️ Inconsistency Assessment: MEDIUM-HIGH Concern

Current State Management Patterns in Codebase:

Pattern Examples Count Benefits Drawbacks
URL State (nuqs) Add-money bank, Manteca 2 flows Deep linking, bookmarking, browser nav None in this context
Context + Enums Request fulfillment, Withdraw, Claim ~5 flows Familiar pattern No deep linking, lost on refresh
Context + useState Setup flow 1 flow Simple No persistence
Component useState MantecaFlowManager (claim) Multiple Simplest Most fragile

Specific Inconsistencies:

1. High Impact: Onramp Flows Split 🔴

  • Add-money flow: Uses nuqs URL state ✅
  • Request fulfillment flow: Uses context with enums ❌
  • Problem: Two conceptually similar flows (both add money via bank) use completely different patterns
  • User Experience: Request fulfillment users can't bookmark/share, add-money users can

2. Medium Impact: Developer Confusion 🟡

When working on flows, developers must decide:

  • "Should I use URL state like add-money?"
  • "Should I use context like request fulfillment?"
  • "Should I use component state like MantecaFlowManager?"

No clear guidelines exist.

3. Medium Impact: Maintenance Burden 🟡

  • Must maintain expertise in multiple patterns
  • Harder to refactor/migrate features between flows
  • Testing strategies differ between patterns

4. Low Impact: Mixed Context 🟢

OnrampFlowContext now has mixed responsibilities:

  • URL state handles: step, amount (via nuqs)
  • Context handles: error, fromBankSelected, onrampData
  • This split is actually reasonable - transient/error state in context, flow state in URL

📋 Recommendations (Prioritized)

SHORT TERM (This PR)

ACCEPT the partial migration as-is because:

  1. The two migrated flows are complete and well-implemented
  2. nuqs library is solid and production-ready
  3. The migration reduces issues, doesn't introduce new ones
  4. Perfect consistency can come later

MEDIUM TERM (Follow-up PRs)

Priority 1: Migrate Request Fulfillment Flow 🔴

// HIGH PRIORITY - Same domain as add-money
// src/context/RequestFulfillmentFlowContext.tsx
// Should use nuqs for step management like add-money does
  • Most important for consistency since it's another onramp flow
  • Users would benefit from deep linking here too
  • Similar step patterns to add-money

Priority 2: Document the Pattern 🟡
Create an Architecture Decision Record (ADR):

# ADR: When to Use URL State vs Context

## Decision
- Multi-step flows with user input → URL state (nuqs)
- Transient app state (errors, loading) → Context
- Pure UI state (modal open/closed) → Component state

## Examples
- ✅ URL State: Onramp flows, withdrawal flows
- ✅ Context: Error handling, transient data
- ✅ Component State: Dropdown open/closed

Priority 3: Consider Other Flow Migrations 🟡
Evaluate if these benefit from URL state:

  • Withdraw flow (probably yes)
  • Claim flow (maybe - depends on UX goals)
  • Setup flow (probably no - one-time wizard)

LONG TERM (Architecture)

Create a Standard Pattern:

// hooks/useFlowUrlState.ts
// Shared hook for all multi-step flows
export function useFlowUrlState<TStep>({ 
  steps, 
  defaultStep,
  additionalParams 
}) {
  // Centralized URL state management
  // Consistent validation
  // Standard redirect guards
}

🎯 Summary

Is this a complete migration?
No - Only 2 out of ~10 multi-step flows use nuqs

Do we have inconsistent state management?
Yes - But it's acceptable inconsistency because:

  1. The migrated flows are well-contained (add-money)
  2. No conflicts between patterns
  3. Gradual migration is better than big-bang rewrite
  4. nuqs is production-ready and appropriate

Should this block the PR?
No - This PR improves the situation:

  • Better UX for add-money flows
  • Demonstrates the pattern for future migrations
  • No breaking changes to other flows
  • Clear path forward for consistency

Most Important Next Step:
Migrate RequestFulfillmentFlowContext to nuqs in a follow-up PR, since it's the most inconsistent pairing (both are onramp flows but use different patterns).


Would you like me to:

  1. Create a follow-up issue for RequestFulfillmentFlow migration?
  2. Draft an ADR template for state management patterns?
  3. Create a shared useFlowUrlState hook for DRY?

🐰✨
(Thoughtfully documenting patterns for future consistency)


✏️ Learnings added
Learnt from: Hugo0
URL: https://github.com/peanutprotocol/peanut-ui/pull/1602

Timestamp: 2026-01-02T13:14:32.616Z
Learning: In the peanut-ui codebase, the add-money flows (bank and Manteca) use nuqs library for URL-based state management (step, amount, currency via useQueryStates), while RequestFulfillmentFlowContext still uses enum-based context state. This creates intentional partial migration where URL state is used for user-facing flows that benefit from deep linking and bookmarking.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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.

Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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.

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.

Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 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
Repo: peanutprotocol/peanut-ui PR: 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.

@kushagrasarathe kushagrasarathe merged commit d137151 into peanut-wallet-dev Jan 8, 2026
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.

3 participants