Skip to content

[TASK-18810] fix: restrict send flow to supported countries + validate country par…#1764

Merged
jjramirezn merged 2 commits intodevfrom
fix/send-flow-country-validation
Mar 18, 2026
Merged

[TASK-18810] fix: restrict send flow to supported countries + validate country par…#1764
jjramirezn merged 2 commits intodevfrom
fix/send-flow-country-validation

Conversation

@jjramirezn
Copy link
Contributor

…am on withdraw

  • Pass enforceSupportedCountries to CountryList when coming from send flow (unsupported countries show 'Soon' badge and are disabled)
  • Validate country param on withdraw/[country]/bank — redirect to /withdraw if country is not bridge-supported
  • Validate country param on withdraw/manteca — redirect to /withdraw if country is not in MANTECA_COUNTRIES_CONFIG

…am on withdraw

- Pass enforceSupportedCountries to CountryList when coming from send flow
  (unsupported countries show 'Soon' badge and are disabled)
- Validate country param on withdraw/[country]/bank — redirect to /withdraw
  if country is not bridge-supported
- Validate country param on withdraw/manteca — redirect to /withdraw
  if country is not in MANTECA_COUNTRIES_CONFIG
@vercel
Copy link

vercel bot commented Mar 18, 2026

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Mar 18, 2026 8:28pm

Request Review

@notion-workspace
Copy link

@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 Mar 18, 2026

Walkthrough

Adds runtime country validation to the bank withdrawal flow and country selection logic, implementing redirect behavior for unsupported countries and enforcing country configuration checks before rendering.

Changes

Cohort / File(s) Summary
Bank Withdrawal Country Validation
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
Introduces useEffect hook that validates country support using getCountryFromPath and isBridgeSupportedCountry, redirecting to /withdraw for unsupported countries.
Manteca Country Validation
src/app/(mobile-ui)/withdraw/manteca/page.tsx
Adds redirect effect for countries not in MANTECA_COUNTRIES_CONFIG and extends loading guard to require countryConfig before rendering PeanutLoading.
Country List Component
src/components/AddWithdraw/AddWithdrawRouterView.tsx
Propagates isBankFromSend condition to CountryList component via new enforceSupportedCountries prop.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: restricting send flow to supported countries and validating country parameters on withdraw routes.
Description check ✅ Passed The description clearly explains all three key changes: enforceSupportedCountries prop for CountryList, country validation on withdraw/[country]/bank, and country validation on withdraw/manteca.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/send-flow-country-validation
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx:
- Around line 434-440: The current useEffect only redirects when selectedCountry
exists but lacks a config; update the effect that references selectedCountry,
MANTECA_COUNTRIES_CONFIG and router.replace so it also triggers a redirect when
selectedCountry is falsy (meaning the query param was invalid/unresolved).
Specifically, inside the useEffect (the one watching [selectedCountry, router])
check for !selectedCountry OR missing
MANTECA_COUNTRIES_CONFIG[selectedCountry.id] and call
router.replace('/withdraw') in either case so invalid country query params don't
leave the component stuck on PeanutLoading.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0770a8be-e986-4c39-a9ce-880b3310a3ff

📥 Commits

Reviewing files that changed from the base of the PR and between 29e61ac and 6a4fb29.

📒 Files selected for processing (3)
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
  • src/components/AddWithdraw/AddWithdrawRouterView.tsx

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the PR diff
gh pr diff 1764 --repo peanutprotocol/peanut-ui

Length of output: 3626


🏁 Script executed:

#!/bin/bash
# Read the relevant files for deeper context
echo "=== CountryList component ==="
fd "CountryList" --type f | head -5

echo "=== useIdentityVerification hook ==="
fd "useIdentityVerification" --type f | head -5

echo "=== bridge.utils ==="
fd "bridge.utils" --type f | head -5

echo "=== manteca page ==="
fd "manteca" --type f | grep "page.tsx"

echo "=== AddWithdrawRouterView ==="
fd "AddWithdrawRouterView" --type f | head -5

Length of output: 481


🏁 Script executed:

#!/bin/bash
echo "=== CountryList component ==="
cat src/components/Common/CountryList.tsx

echo ""
echo "=== useIdentityVerification hook ==="
cat src/hooks/useIdentityVerification.tsx

echo ""
echo "=== getCountryFromPath in bridge.utils ==="
grep -n "getCountryFromPath\|isBridgeSupportedCountry\|enforceSupportedCountries" src/utils/bridge.utils.ts

echo ""
echo "=== Manteca page path ==="
fd "manteca" --type f

echo ""
echo "=== AddWithdrawRouterView - isBankFromSend ==="
grep -n "isBankFromSend\|enforceSupportedCountries\|isBridgeSupportedCountry" src/components/AddWithdraw/AddWithdrawRouterView.tsx

echo ""
echo "=== MANTECA_COUNTRIES_CONFIG ==="
grep -rn "MANTECA_COUNTRIES_CONFIG" --include="*.ts" --include="*.tsx" | head -20

Length of output: 26083


🏁 Script executed:

#!/bin/bash
echo "=== getCountryFromPath implementation ==="
grep -n -A 15 "export function getCountryFromPath" src/utils/bridge.utils.ts

echo ""
echo "=== manteca page.tsx (withdraw) ==="
cat src/app/(mobile-ui)/withdraw/manteca/page.tsx | head -150

echo ""
echo "=== manteca page.tsx useEffect around line 430 ==="
sed -n '400,460p' src/app/(mobile-ui)/withdraw/manteca/page.tsx

echo ""
echo "=== AddWithdrawRouterView around isBankFromSend (lines 70-95 and 300-360) ==="
sed -n '70,100p' src/components/AddWithdraw/AddWithdrawRouterView.tsx
echo "..."
sed -n '300,360p' src/components/AddWithdraw/AddWithdrawRouterView.tsx

echo ""
echo "=== withdraw bank page full (around new useEffect) ==="
sed -n '40,100p' src/app/(mobile-ui)/withdraw/\[country\]/bank/page.tsx

echo ""
echo "=== MANTECA_COUNTRIES_CONFIG definition ==="
cat src/constants/manteca.consts.ts | head -120

Length of output: 1044


🏁 Script executed:

#!/bin/bash
echo "=== manteca withdraw page ==="
MANTECA_PAGE=$(fd "page.tsx" src/app --full-path | grep "withdraw/manteca")
echo "Found: $MANTECA_PAGE"
head -150 "$MANTECA_PAGE"

echo ""
echo "=== manteca withdraw page around line 400-460 ==="
sed -n '400,465p' "$MANTECA_PAGE"

echo ""
echo "=== withdraw bank page ==="
BANK_PAGE=$(fd "page.tsx" src/app --full-path | grep "withdraw.*country.*bank")
echo "Found: $BANK_PAGE"
sed -n '40,100p' "$BANK_PAGE"

echo ""
echo "=== AddWithdrawRouterView around isBankFromSend ==="
sed -n '70,100p' src/components/AddWithdraw/AddWithdrawRouterView.tsx
echo "---"
sed -n '295,360p' src/components/AddWithdraw/AddWithdrawRouterView.tsx

echo ""
echo "=== MANTECA_COUNTRIES_CONFIG ==="
head -120 src/constants/manteca.consts.ts

Length of output: 22481


@jjramirezn Here's my in-depth analysis of PR #1764:


🔍 In-Depth Review

1. Risk of Bugs & Breaking Existing Functionality

withdraw/[country]/bank/page.tsx — redirect guard ✅ Low risk

The new useEffect is safe:

  • isBridgeSupportedCountry is created with useCallback([]) (empty deps), so it's referentially stable and won't trigger unnecessary re-runs.
  • getCountryFromPath does a simple path-match against ALL_METHODS_DATA — deterministic.
  • However, there is a subtle redundant guard: country from useParams() is always defined on this page (it's a required dynamic segment), so if (country) is a no-op. Harmless, but slightly misleading.

withdraw/manteca/page.tsx — redirect guard ✅ Low risk, with one observation

The useEffect is placed after all hooks and fires correctly. The update to the loading guard (!countryConfig) is a good defensive fix — without it, the component could reach render code with a null countryConfig and crash. There's an acceptable one-frame flash of PeanutLoading before the redirect fires for unsupported countries.

AddWithdrawRouterView.tsxenforceSupportedCountries ⚠️ One dead code path

When isBankFromSend is true, CountryList now disables all Manteca/LATAM countries (since isSupported = isBridgeSupportedCountry excludes them). However, the handler for Manteca countries in the same component still exists:

// AddWithdrawRouterView.tsx — now unreachable from the send flow
if (flow === 'withdraw' && isBankFromSend) {
    if (isMantecaCountry(country.path)) {   // ← dead code: LATAM countries are disabled
        const route = `/withdraw/manteca?method=bank-transfer&country=${country.path}`
        ...
    }
}

Since LATAM countries are disabled in CountryList when enforceSupportedCountries=true, onCountryClick will never fire for them. The Manteca branch in the isBankFromSend handler is now dead code. Not a bug, but worth cleaning up in a follow-up.


2. Performance Impact & Latency

Negligible. All three changes are:

  • O(1) lookups (set membership check in isBridgeSupportedCountry, object key check in MANTECA_COUNTRIES_CONFIG)
  • Stable useCallback/useMemo references — no extra renders triggered
  • useEffect runs once on mount with stable deps

useIdentityVerification is now imported into the bank page, but it calls useKycStatus, useUnifiedKycStatus, and useAuth which are almost certainly already mounted in the component tree. No meaningful latency increase.


3. Code Quality & DRY

  • Acceptable duplication: Both page-level guards (/bank and /manteca) validate their respective country independently. Since they use different validators (Bridge vs. Manteca config), a shared abstraction would add complexity without much gain.
  • enforceSupportedCountries prop: Well-named, narrowly scoped, backward-compatible default (undefinedfalse).
  • Dead code (noted above): The Manteca routing branch inside the isBankFromSend block in AddWithdrawRouterView should be cleaned up.
  • Minor: The if (country) guard in the bank page useEffect is always true for that route — can be simplified to remove the condition.

🧪 QA Testing Guide

Setup

  • Have accounts with and without Bridge KYC approved.
  • Test on a device or browser that supports the mobile UI.

Scenario 1 — Send flow: country restriction

Step Expected
Enter send flow → select bank as method Country list opens
Browse country list LATAM countries (Argentina, Brazil, Colombia…) show "Soon" badge and are not clickable
Select a Bridge-supported country (e.g., Germany, USA) Proceeds to amount/bank entry normally
Non-send flow (regular withdraw) → open country list All countries are selectable, no "Soon" badges

Scenario 2 — Direct URL: /withdraw/[country]/bank validation

URL Expected
/withdraw/germany/bank (Bridge country) Page loads normally
/withdraw/argentina/bank (Manteca country, not Bridge) Redirects to /withdraw
/withdraw/nonexistent-country/bank Redirects to /withdraw

Scenario 3 — Direct URL: /withdraw/manteca validation

URL Expected
/withdraw/manteca?country=argentina Page loads normally
/withdraw/manteca?country=brazil Page loads normally
/withdraw/manteca?country=mexico (Mexico uses Bridge, not in MANTECA_COUNTRIES_CONFIG) Redirects to /withdraw
/withdraw/manteca?country=garbage Redirects to /withdraw
/withdraw/manteca (no country param, defaults to argentina) Page loads normally

Regression

Scenario Expected
Add-money flow country list All countries enabled, no change
Regular withdraw (no ?method=bank) country list All countries enabled, no change
Completing a full Bridge bank withdrawal from send flow Works end-to-end

Overall verdict: The PR is safe to merge. The main follow-up item is removing the now-unreachable Manteca routing branch inside the isBankFromSend handler in AddWithdrawRouterView.tsx.


🧠 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: 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".

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".

Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 0
File: :0-0
Timestamp: 2026-03-04T18:32:51.654Z
Learning: In peanutprotocol/peanut-ui, Bridge-specific add-money and withdraw flows (`src/app/(mobile-ui)/add-money/[country]/bank/page.tsx`, `src/components/AddWithdraw/AddWithdrawCountriesList.tsx`, `src/components/Claim/Link/views/BankFlowManager.view.tsx`) have been migrated (PR `#1713`) to use the unified `isUserKycApproved` from `useKycStatus()` hook instead of per-component `liveKycStatus` + WebSocket state. The migration is complete; no more direct `bridgeKycStatus === 'approved'` checks in those files.

Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1693
File: src/app/(mobile-ui)/add-money/[country]/bank/page.tsx:206-207
Timestamp: 2026-03-04T18:35:17.673Z
Learning: In Bridge-specific add-money and withdraw flows (e.g., src/app/(mobile-ui)/add-money/[country]/bank/page.tsx), liveKycStatus from WebSocket tracks real-time bridge KYC status changes, and bridgeKycStatus is the persisted status. These flows use direct bridgeKycStatus === 'approved' checks instead of the unified isUserKycVerified helper. Migrating to the unified verifier requires careful testing of WebSocket-based real-time status tracking and is deferred to follow-up work.

@jjramirezn jjramirezn merged commit 22b4c5b into dev Mar 18, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant