Skip to content

[TASK-12884] feat:manteca withdraw#1227

Merged
jjramirezn merged 4 commits intofeat/manteca-integrationfrom
feat/manteca-withdraw
Sep 18, 2025
Merged

[TASK-12884] feat:manteca withdraw#1227
jjramirezn merged 4 commits intofeat/manteca-integrationfrom
feat/manteca-withdraw

Conversation

@jjramirezn
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Sep 18, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Sep 18, 2025 3:37pm

@notion-workspace
Copy link

Implement withdraw Flow

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Walkthrough

Adds a method-driven withdraw flow with a new Manteca multi-step withdrawal page (including KYC and backend wiring), centralizes Manteca constants and country list, extends withdraw context with selectedMethod/setSelectedMethod, updates country/method configs and routing, and adjusts related redirects and transaction/loading types.

Changes

Cohort / File(s) Summary of changes
Manteca withdraw flow & platform types
src/app/(mobile-ui)/withdraw/manteca/page.tsx, src/constants/manteca.consts.ts, src/hooks/useTransactionHistory.ts, src/components/TransactionDetails/transactionTransformer.ts, src/constants/loadingStates.consts.ts
Adds a client-side Manteca withdrawal page component with multi-step UI, KYC flow, WebSocket status handling, and API integration. Introduces MANTECA_COUNTRIES, MantecaCountry type, and isMantecaCountry helper; adds MANTECA_OFFRAMP history type and maps it as a bank withdraw; adds 'Withdrawing' to LoadingStates.
Withdraw flow refactor (method-driven)
src/context/WithdrawFlowContext.tsx, src/app/(mobile-ui)/withdraw/page.tsx, src/components/AddWithdraw/AddWithdrawCountriesList.tsx, src/components/AddWithdraw/AddWithdrawRouterView.tsx, src/app/(mobile-ui)/withdraw/crypto/page.tsx, src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
Introduces WithdrawMethod interface and exposes selectedMethod/setSelectedMethod in WithdrawFlowContext. Reworks withdraw pages and components to drive flow via selectedMethod (crypto/bridge/manteca), changes navigation/redirect logic and effect dependencies, and updates UI step orchestration.
Country/method configs
src/components/AddMoney/consts/index.ts
Expands withdraw/add method data shape to include isSoon; updates Argentina Mercado Pago entry; injects SEPA Instant for EUR countries; computes dynamic method IDs and, for Manteca countries, adds paths to /withdraw/manteca?method=...&country=...; updates add/withdraw paths and availability flags.
Add/withdraw UIs & routing adjustments
src/components/AddWithdraw/AddWithdrawCountriesList.tsx, src/components/AddWithdraw/AddWithdrawRouterView.tsx
Changes method selection handling: sets selected method in context for bridge/crypto flows and navigates centrally to /withdraw; routes Manteca paths directly; removes some view-toggling and logging related to missing amount.
Withdraw utilities & validators
src/utils/withdraw.utils.ts
Adds validateCbuCvuAlias(value: string): boolean (currently returns true).
QR pay address import
src/app/(mobile-ui)/qr-pay/page.tsx
Replaces a locally defined MANTECA_DEPOSIT_ADDRESS constant with an import from @/constants/manteca.consts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Zishan-7
  • kushagrasarathe

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
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.
Description Check ❓ Inconclusive No pull request description was provided by the author; while the raw summary shows an extensive Manteca withdraw implementation, the absent PR description makes intent, testing guidance, and deployment/migration notes unclear to reviewers. Please add a short PR description summarizing what and why (e.g., new Manteca withdraw page and routes, additions to manteca.consts, WithdrawFlowContext changes, new loading state and history enum), any runtime or migration considerations (KYC flow, API endpoints, feature flags), and minimal QA/testing steps so reviewers can verify behavior and risk.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "[TASK-12884] feat:manteca withdraw" concisely indicates the primary work—adding Manteca withdraw functionality—and matches the changeset that introduces a Manteca withdrawal flow, constants, routing, and related context updates; it is focused and not off-topic.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/manteca-withdraw

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Caution

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

⚠️ Outside diff range comments (1)
src/components/TransactionDetails/transactionTransformer.ts (1)

317-323: Include MANTECA_OFFRAMP in bridge-like status mapping.

Without this, Manteca off-ramp statuses will show generic statuses.

-    if (
-        entry.type === EHistoryEntryType.BRIDGE_OFFRAMP ||
+    if (
+        entry.type === EHistoryEntryType.BRIDGE_OFFRAMP ||
+        entry.type === EHistoryEntryType.MANTECA_OFFRAMP ||
         entry.type === EHistoryEntryType.BRIDGE_ONRAMP ||
         entry.type === EHistoryEntryType.BANK_SEND_LINK_CLAIM ||
         entry.extraData?.fulfillmentType === 'bridge'
     ) {
🧹 Nitpick comments (12)
src/hooks/useTransactionHistory.ts (1)

23-24: Add explicit handling for MANTECA_OFFRAMP in the amount/token mapping switch.

Default covers it, but being explicit keeps parity with other off-ramp types and future-proofs changes.

Apply this diff within the switch:

-                case EHistoryEntryType.WITHDRAW:
-                case EHistoryEntryType.BRIDGE_OFFRAMP:
+                case EHistoryEntryType.WITHDRAW:
+                case EHistoryEntryType.BRIDGE_OFFRAMP:
+                case EHistoryEntryType.MANTECA_OFFRAMP:
                 case EHistoryEntryType.BRIDGE_ONRAMP:
                 case EHistoryEntryType.BANK_SEND_LINK_CLAIM: {
src/components/TransactionDetails/transactionTransformer.ts (1)

474-481: Expose bankAccountDetails for MANTECA_OFFRAMP too.

Parity with BRIDGE_OFFRAMP so the drawer shows bank info.

-        bankAccountDetails:
-            entry.type === EHistoryEntryType.BRIDGE_OFFRAMP ||
+        bankAccountDetails:
+            entry.type === EHistoryEntryType.BRIDGE_OFFRAMP ||
+            entry.type === EHistoryEntryType.MANTECA_OFFRAMP ||
             (entry.type === EHistoryEntryType.BANK_SEND_LINK_CLAIM && entry.userRole === EHistoryUserRole.RECIPIENT)
                 ? {
                       identifier: entry.recipientAccount.identifier,
                       type: entry.recipientAccount.type,
                   }
                 : undefined,
src/components/AddMoney/consts/index.ts (2)

2547-2572: DRY: use isMantecaCountry helper and URL-encode method param.

Inline slug list duplicates src/constants/manteca.consts.ts and risks drift; also encode query params.

Diff for this hunk:

-                const methodId = `${countryCode.toLowerCase()}-${method.title.toLowerCase().replace(/\s+/g, '-')}-withdraw`
-
-                // Check if this is a Manteca country to add appropriate routing
-                const isMantecaCountry = [
-                    'argentina',
-                    'chile',
-                    'brazil',
-                    'colombia',
-                    'panama',
-                    'costa-rica',
-                    'guatemala',
-                    'philippines',
-                    'bolivia',
-                ].includes(country.path)
+                const methodId = `${countryCode.toLowerCase()}-${method.title.toLowerCase().replace(/\s+/g, '-')}-withdraw`

And adjust the path usage:

-                    path: isMantecaCountry
-                        ? `/withdraw/manteca?method=${method.title.toLowerCase().replace(/\s+/g, '-')}&country=${country.path}`
+                    path: isMantecaCountry(country.path)
+                        ? `/withdraw/manteca?method=${encodeURIComponent(method.title.toLowerCase().replace(/\s+/g, '-'))}&country=${encodeURIComponent(country.path)}`
                         : undefined,

Add this import at the top of the file:

import { isMantecaCountry } from '@/constants/manteca.consts'

2603-2614: Reuse isMantecaCountry for default bank path as well.

Avoid maintaining the slug array twice.

-            const isMantecaCountry = [
-                'argentina',
-                'chile',
-                'brazil',
-                'colombia',
-                'panama',
-                'costa-rica',
-                'guatemala',
-                'philippines',
-                'bolivia',
-            ].includes(country.path)
+            const isManteca = isMantecaCountry(country.path)
...
-                path: isMantecaCountry
-                    ? `/withdraw/manteca?method=bank-transfer&country=${country.path}`
-                    : `/withdraw/${countryCode.toLowerCase()}/bank`,
+                path: isManteca
+                    ? `/withdraw/manteca?method=bank-transfer&country=${encodeURIComponent(country.path)}`
+                    : `/withdraw/${countryCode.toLowerCase()}/bank`,

Also applies to: 2618-2621

src/context/WithdrawFlowContext.tsx (1)

148-151: Nit: trim stable setters from deps.

setShowAllWithdrawMethods and other React setters are stable; they can be dropped from the useMemo deps to avoid unnecessary lint exceptions.

-        [
+        [
             amountToWithdraw,
             currentView,
             withdrawData,
             showCompatibilityModal,
             isPreparingReview,
             paymentError,
             usdAmount,
             isValidRecipient,
             inputChanging,
             recipient,
             error,
             selectedBankAccount,
             showAllWithdrawMethods,
             selectedMethod,
-            setShowAllWithdrawMethods,
             resetWithdrawFlow,
         ]
src/app/(mobile-ui)/withdraw/page.tsx (1)

35-37: Comment vs. behavior mismatch.

Comment says “Only crypto gets amount input,” but initialStep shows input for any selected method. Either update the comment or gate inputAmount to crypto only.

-// FIXED FLOW: Only crypto gets amount input on main page, countries route directly
-const initialStep: WithdrawStep = selectedMethod ? 'inputAmount' : 'selectMethod'
+// When a method is selected, we take amount on this page and route next.
+const initialStep: WithdrawStep = selectedMethod ? 'inputAmount' : 'selectMethod'
src/app/(mobile-ui)/withdraw/manteca/page.tsx (6)

132-136: Unreachable null check for KYC flag.

useMantecaKycFlow returns a boolean; === null never hits. Remove this guard or switch to an explicit “unknown/loading” state.

-        // Check if we still need to determine KYC status
-        if (isMantecaKycRequired === null) {
-            // still loading/determining KYC status, don't proceed yet
-            return
-        }
+        // KYC state is boolean; proceed to gating directly below.

230-231: Fallback display for code.

Show a safe code when currency is unknown.

-                                {currencyCode} {currencyAmount}
+                                {(currencyCode ?? 'USD')} {currencyAmount}

323-324: Fallback display for code (bank details card).

Same rationale as above.

-                                    {currencyCode} {currencyAmount}
+                                    {(currencyCode ?? 'USD')} {currencyAmount}

397-398: Fallback display for code (review card).

Same rationale as above.

-                                    {currencyCode} {currencyAmount}
+                                    {(currencyCode ?? 'USD')} {currencyAmount}

404-410: Make review labels country‑aware and tolerate missing FX price.

Align the label with the country and avoid non‑null assertions.

-                        <PaymentInfoRow label="CBU, CVU or Alias" value={bankDetails.destinationAddress} />
+                        <PaymentInfoRow
+                            label={countryPath === 'argentina' ? 'CBU, CVU or Alias' : 'Account address'}
+                            value={bankDetails.destinationAddress}
+                        />
                         <PaymentInfoRow
                             label="Exchange Rate"
-                            value={`1 USD = ${currencyPrice!.sell} ${currencyCode!.toUpperCase()}`}
+                            value={`1 USD = ${(currencyPrice?.sell ?? 1)} ${(currencyCode ?? 'USD').toUpperCase()}`}
                         />

333-341: Minor: country‑specific error copy.

Use country‑aware copy for missing destination address.

-            setErrorMessage('Please enter your account address')
+            setErrorMessage(countryPath === 'argentina' ? 'Please enter your CBU, CVU or Alias' : 'Please enter your account address')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e952b5 and 5390b7b.

📒 Files selected for processing (13)
  • src/app/(mobile-ui)/qr-pay/page.tsx (1 hunks)
  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx (1 hunks)
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx (2 hunks)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx (1 hunks)
  • src/app/(mobile-ui)/withdraw/page.tsx (6 hunks)
  • src/components/AddMoney/consts/index.ts (3 hunks)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx (3 hunks)
  • src/components/AddWithdraw/AddWithdrawRouterView.tsx (5 hunks)
  • src/components/TransactionDetails/transactionTransformer.ts (1 hunks)
  • src/constants/loadingStates.consts.ts (1 hunks)
  • src/constants/manteca.consts.ts (1 hunks)
  • src/context/WithdrawFlowContext.tsx (6 hunks)
  • src/hooks/useTransactionHistory.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
  • src/app/(mobile-ui)/withdraw/crypto/page.tsx
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx
  • src/context/WithdrawFlowContext.tsx
  • src/components/AddWithdraw/AddWithdrawRouterView.tsx
  • src/app/(mobile-ui)/withdraw/page.tsx
  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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)/withdraw/[country]/bank/page.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.

Applied to files:

  • src/app/(mobile-ui)/withdraw/crypto/page.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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/constants/manteca.consts.ts
  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page.

Applied to files:

  • src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page after scanning MERCADO_PAGO or PIX QR codes.

Applied to files:

  • src/app/(mobile-ui)/qr-pay/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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)/withdraw/page.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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)/withdraw/page.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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)/withdraw/page.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the ALL_METHODS_DATA structure from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/AddMoney/consts/index.ts
🧬 Code graph analysis (7)
src/app/(mobile-ui)/withdraw/crypto/page.tsx (1)
src/redux/slices/payment-slice.ts (1)
  • paymentActions (81-81)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (12)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/hooks/wallet/useWallet.ts (1)
  • useWallet (21-123)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/components/AddMoney/consts/index.ts (2)
  • CountryData (153-162)
  • countryCodeMap (2477-2519)
src/hooks/useCurrency.ts (1)
  • useCurrency (21-65)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (17-110)
src/hooks/useWebSocket.ts (1)
  • useWebSocket (19-186)
src/constants/manteca.consts.ts (1)
  • MANTECA_DEPOSIT_ADDRESS (1-1)
src/utils/general.utils.ts (2)
  • isTxReverted (1313-1316)
  • formatAmount (400-440)
src/services/manteca.ts (1)
  • mantecaApi (95-232)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN_DECIMALS (19-19)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • InitiateMantecaKYCModal (23-70)
src/components/AddWithdraw/AddWithdrawCountriesList.tsx (2)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/redux/hooks.ts (1)
  • useAppDispatch (5-5)
src/context/WithdrawFlowContext.tsx (1)
src/interfaces/interfaces.ts (1)
  • Account (272-299)
src/components/AddWithdraw/AddWithdrawRouterView.tsx (2)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/constants/manteca.consts.ts (1)
  • isMantecaCountry (21-23)
src/app/(mobile-ui)/withdraw/page.tsx (2)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/components/AddWithdraw/AddWithdrawRouterView.tsx (1)
  • AddWithdrawRouterView (29-296)
src/components/AddMoney/consts/index.ts (2)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (64-125)
src/constants/manteca.consts.ts (1)
  • isMantecaCountry (21-23)
🔇 Additional comments (21)
src/components/TransactionDetails/transactionTransformer.ts (1)

251-256: LGTM on adding the new case; matches BRIDGE_OFFRAMP mapping.

Please confirm Manteca off-ramp always targets bank accounts; otherwise we may need a different nameForDetails.

src/constants/loadingStates.consts.ts (1)

30-31: LGTM; new 'Withdrawing' state added.

Ensure any UI that renders loadingState includes this label in allowed values.

src/app/(mobile-ui)/withdraw/crypto/page.tsx (1)

92-95: LGTM on scoping cleanup to truthy amount.

Prevents unnecessary resets while user is editing.

src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx (1)

44-51: LGTM on refined redirects.

Nice UX: return to main when no amount, otherwise to country selection. Using replace avoids history stacking.

src/constants/manteca.consts.ts (1)

3-23: Central list/helper looks good.

This should be the single source of truth; deduplicate any inline country arrays elsewhere (see AddMoney consts).

src/app/(mobile-ui)/qr-pay/page.tsx (1)

19-20: LGTM; import from shared constants.

src/components/AddMoney/consts/index.ts (2)

232-235: Type tweak: good to allow isSoon on specific withdraw methods.


238-245: Argentina details: solid; icon + isSoon=false makes it actionable.

src/context/WithdrawFlowContext.tsx (5)

7-14: Type surface addition looks solid.

The WithdrawMethod shape is clear and future‑proof. No blockers.


62-64: Context API extension: LGTM.

selectedMethod/setSelectedMethod are correctly added to the public context.


87-87: State wiring is correct.

Local state for selectedMethod is initialized and typed properly.


99-100: Reset behavior is complete.

Resetting selectedMethod in resetWithdrawFlow avoids stale routing state.


130-131: Value exposure: OK.

Exposing both getter and setter in the memoized value is correct.

src/components/AddWithdraw/AddWithdrawCountriesList.tsx (4)

24-24: No comment.

Import presence acknowledged.


42-42: Context usage: LGTM.

Pulling setSelectedMethod here is consistent with the new flow.


46-46: Initial view logic is reasonable.

Defaulting to form when an amount already exists avoids extra steps.


165-189: Routing and method capture look correct.

  • Manteca routes directly (has its own amount input).
  • Bridge/crypto store selection in context and go to /withdraw.

Minor: relying on includes('...withdraw') in IDs is brittle; consider enumerating constants or a discriminant field on SpecificPaymentMethod.

src/components/AddWithdraw/AddWithdrawRouterView.tsx (3)

191-196: Saved accounts navigation fix: LGTM.

Routing directly to the saved account path for withdraw removes ambiguity.


259-269: Country selection behavior is correct.

Directly routing to the country page for withdraw aligns with the centralized flow.


278-285: Crypto selection handoff is consistent.

Setting context and returning to /withdraw keeps the UX coherent.

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

168-189: Routing depends on countryPath being a slug.

With the fix in AddWithdrawRouterView, this logic is sound. If countryPath ever contains /withdraw/..., paths will break. Please re‑test after the above fix.

Comment on lines +318 to 321
// Redirect to main withdraw page for amount input
router.push('/withdraw')
return <PeanutLoading />
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid router.push during render; move redirect to an effect.

Side-effects in render can cause hydration issues and double navigations.

-    if (!amountToWithdraw) {
-        // Redirect to main withdraw page for amount input
-        router.push('/withdraw')
-        return <PeanutLoading />
-    }
+    // Redirect to main withdraw page for amount input
+    useEffect(() => {
+        if (!amountToWithdraw) {
+            router.replace('/withdraw')
+        }
+    }, [amountToWithdraw, router])
+
+    if (!amountToWithdraw) {
+        return <PeanutLoading />
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Redirect to main withdraw page for amount input
router.push('/withdraw')
return <PeanutLoading />
}
// Redirect to main withdraw page for amount input
useEffect(() => {
if (!amountToWithdraw) {
router.replace('/withdraw')
}
}, [amountToWithdraw, router])
if (!amountToWithdraw) {
return <PeanutLoading />
}
🤖 Prompt for AI Agents
In src/app/(mobile-ui)/withdraw/crypto/page.tsx around lines 318-321, the code
calls router.push('/withdraw') during render which causes side-effects in render
and hydration/double-navigation issues; move the redirect into a client-side
effect: convert this component to a client component if not already (add "use
client" at the top), get router from useRouter(), and perform
router.push('/withdraw') inside a useEffect hook that runs on mount; meanwhile
return <PeanutLoading /> from render so the redirect runs only after the
component mounts.

Copy link
Contributor

Choose a reason for hiding this comment

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

looks relevant

Copy link
Contributor

Choose a reason for hiding this comment

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

@Zishan-7 Yes, this is definitely a relevant issue that should be addressed.

Calling router.push() during render violates React's rules about side effects and can cause:

  • Hydration mismatches between server and client
  • Double navigation issues
  • Potential infinite re-render loops

The fix I suggested moves the redirect logic into a useEffect hook, which is the proper place for side effects like navigation. I also recommended using router.replace() instead of router.push() since we don't want users to navigate back to a page that would immediately redirect them again.

This pattern aligns with React best practices and is consistent with how similar redirect logic is handled elsewhere in the codebase.


🧠 Learnings used
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.

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

Comment on lines +95 to +109
// NEW: For withdraw flow, set selected method in context instead of navigating
if (flow === 'withdraw') {
const methodType =
method.type === 'crypto' ? 'crypto' : isMantecaCountry(method.path) ? 'manteca' : 'bridge'

setSelectedMethod({
type: methodType,
countryPath: method.path,
currency: method.currency,
title: method.title,
})

// Don't navigate - let the main withdraw page handle the flow
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Bug: countryPath stores a full URL; breaks downstream routing and params.

countryPath should be the slug (e.g., argentina), not the full /withdraw/... path. Also, isMantecaCountry(method.path) is passed a URL, not a country slug. This causes:

  • /withdraw/${selectedMethod.countryPath} to double‑prefix or include query strings.
  • country query param for Manteca to be wrong.

Fix by extracting the slug and detecting Manteca via path or slug.

Apply:

-        if (flow === 'withdraw') {
-            const methodType =
-                method.type === 'crypto' ? 'crypto' : isMantecaCountry(method.path) ? 'manteca' : 'bridge'
-
-            setSelectedMethod({
-                type: methodType,
-                countryPath: method.path,
-                currency: method.currency,
-                title: method.title,
-            })
-            return
-        }
+        if (flow === 'withdraw') {
+            const path = method.path || ''
+            const qs = path.split('?')[1] || ''
+            const fromQuery = new URLSearchParams(qs).get('country') || ''
+            // slug from URL like "/withdraw/argentina" -> "argentina"
+            const fromPath = path.replace(/^\/(add-money|withdraw)\//, '').split('/')[0]
+            const countrySlug = (fromQuery || fromPath || '').replace(/\/+$/, '')
+
+            const methodType =
+                method.type === 'crypto'
+                    ? 'crypto'
+                    : path.includes('/manteca') || (countrySlug && isMantecaCountry(countrySlug))
+                      ? 'manteca'
+                      : 'bridge'
+
+            setSelectedMethod({
+                type: methodType,
+                countryPath: countrySlug,
+                currency: method.currency,
+                title: method.title,
+            })
+            return
+        }

🏁 Script executed:

#!/bin/bash
# Inspect DepositMethod.path shapes to ensure they are absolute URLs and verify slugs extraction works.
rg -n --type=ts -C2 "DepositMethod" src | sed -n '1,200p'
rg -n --type=ts -C2 "path:.*'/withdraw" src/components/AddMoney/consts 2>/dev/null || true

Length of output: 3896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== src/components/AddMoney/components/DepositMethodList.tsx (head) ==="
sed -n '1,180p' src/components/AddMoney/components/DepositMethodList.tsx || true
echo
echo "=== src/components/AddWithdraw/AddWithdrawRouterView.tsx (1-260) ==="
sed -n '1,260p' src/components/AddWithdraw/AddWithdrawRouterView.tsx || true
echo
echo "=== src/components/AddMoney/consts/index.ts (1-320) ==="
sed -n '1,320p' src/components/AddMoney/consts/index.ts || true
echo
echo "=== search: isMantecaCountry definition/usage ==="
rg -n "isMantecaCountry" -S || true
echo
echo "=== search: setSelectedMethod usage ==="
rg -n "setSelectedMethod" -S || true
echo
echo "=== search: occurrences of '/withdraw' in consts dir ==="
rg -n "/withdraw" src/components/AddMoney/consts -S || true

Length of output: 27888


Fix: normalize country slug before saving selectedMethod (withdraw flow)

setSelectedMethod currently stores method.path (which can be a full "/withdraw/..." URL) into countryPath and calls isMantecaCountry(method.path) — this leads to double-prefixed routes and wrong query params downstream. Extract the country slug and use that for both countryPath and manteca detection.

  • Action: parse method.path to prefer ?country=... if present, else strip leading "/add-money" or "/withdraw" and take the first path segment (remove querystring/trailing slashes). Use that slug for countryPath and call isMantecaCountry(countrySlug) (fall back to path.includes('/manteca') for existing full-path cases).
  • Files to change: src/components/AddWithdraw/AddWithdrawRouterView.tsx (handleMethodSelected), src/components/AddWithdraw/AddWithdrawCountriesList.tsx (setSelectedMethod calls).
🤖 Prompt for AI Agents
In src/components/AddWithdraw/AddWithdrawRouterView.tsx around lines 95-109, the
withdraw flow stores method.path (which may be a full "/withdraw/..." URL)
directly into selectedMethod.countryPath and calls
isMantecaCountry(method.path); extract a normalized country slug instead: first
check for a ?country=... query param and prefer that, otherwise strip any
leading "/add-money" or "/withdraw", remove leading/trailing slashes and
querystring, then take the first path segment as countrySlug; set
selectedMethod.countryPath to that slug and call isMantecaCountry(countrySlug)
(fall back to method.path.includes('/manteca') if you still have a full-path
case). Apply the same normalization to all setSelectedMethod calls in
src/components/AddWithdraw/AddWithdrawCountriesList.tsx so countryPath and
manteca detection always use the slug rather than full paths.

Copy link
Contributor

@Zishan-7 Zishan-7 left a comment

Choose a reason for hiding this comment

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

Lgtm: Added some questions/suggestions

guatemala: 'GTQ',
philippines: 'PHP',
bolivia: 'BOB',
}
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Do we really need this? We get currency in the selectedCountry variable as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agree

currency={{
code: currencyCode!,
symbol: currencySymbol!,
price: currencyPrice!.sell,
Copy link
Contributor

Choose a reason for hiding this comment

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

prolly dumb question: Shouldnt we use currencyPrice!.buy here? because we are selling USD and buying ARS?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from manteca point we are getting the price for the first asset of the pair USDC/ARS so we want the sell price of USDC because we are selling USDC as you say. so 1 USDC is {sellPrice} ARS.

Definitely not a dumb question, these things are good to double check

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

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/consts/index.ts (1)

547-561: Redundant country checking - consider extracting to a helper function.

The code repeats the same country list check multiple times (lines 550-561, 603-614, and potentially more). This violates the DRY principle and makes maintenance error-prone.

+import { isMantecaCountry } from '@/constants/manteca.consts'
+
 countryData.forEach((country) => {
     if (country.type === 'country') {
         const countryCode = country.id
         const countryTitle = country.title
 
         const withdrawList: SpecificPaymentMethod[] = []
 
         // 1. add specific country withdrawal methods
         const specificMethodDetails = countrySpecificWithdrawMethods[countryTitle]
         if (specificMethodDetails && specificMethodDetails.length > 0) {
             specificMethodDetails.forEach((method) => {
                 const methodId = `${countryCode.toLowerCase()}-${method.title.toLowerCase().replace(/\s+/g, '-')}-withdraw`
 
-                // Check if this is a Manteca country to add appropriate routing
-                const isMantecaCountry = [
-                    'argentina',
-                    'chile',
-                    'brazil',
-                    'colombia',
-                    'panama',
-                    'costa-rica',
-                    'guatemala',
-                    'philippines',
-                    'bolivia',
-                ].includes(country.path)
-
                 withdrawList.push({
                     id: methodId,
                     icon: method.icon ?? undefined,
                     title: method.title,
                     description: method.description,
                     isSoon: method.isSoon ?? true,
                     // Add path for Manteca countries to route to Manteca flow
-                    path: isMantecaCountry
+                    path: isMantecaCountry(country.path)
                         ? `/withdraw/manteca?method=${method.title.toLowerCase().replace(/\s+/g, '-')}&country=${country.path}`
                         : undefined,
                 })
             })
         }

Apply the same refactor to the second occurrence around lines 603-614.

♻️ Duplicate comments (2)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (2)

217-219: Avoid infinite loader when price is unavailable

Blocking on !currencyPrice can hang for unsupported currencies.

Apply:

-    if (isCurrencyLoading || !currencyPrice) {
+    if (isCurrencyLoading) {
         return <PeanutLoading />
     }

Ensure consumers tolerate missing currencyPrice.


280-284: Use safe fallbacks instead of non-null assertions for currency props

Non-null assertions can crash when price/code are missing.

Apply:

-                        currency={{
-                            code: currencyCode!,
-                            symbol: currencySymbol!,
-                            price: currencyPrice!.sell,
-                        }}
+                        currency={{
+                            code: currencyCode ?? 'USD',
+                            symbol: currencySymbol ?? '$',
+                            price: currencyPrice?.sell ?? 1,
+                        }}
🧹 Nitpick comments (10)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (8)

418-422: Harden exchange-rate row against missing price/code

Avoid ! and render a clear fallback.

Apply:

-                        <PaymentInfoRow
-                            label="Exchange Rate"
-                            value={`1 USD = ${currencyPrice!.sell} ${currencyCode!.toUpperCase()}`}
-                        />
+                        <PaymentInfoRow
+                            label="Exchange Rate"
+                            value={
+                                currencyPrice && currencyCode
+                                    ? `1 USD = ${currencyPrice.sell} ${currencyCode.toUpperCase()}`
+                                    : 'Price unavailable'
+                            }
+                        />

94-97: Default flag code to a safe value to avoid broken image URLs

If selectedCountry is undefined, the URL becomes invalid.

Apply:

-    const countryFlagCode = useMemo(() => {
-        return selectedCountry?.iso2?.toLowerCase()
-    }, [selectedCountry])
+    const countryFlagCode = useMemo(() => {
+        return selectedCountry?.iso2?.toLowerCase() ?? 'us'
+    }, [selectedCountry?.iso2])

312-318: Improve flag alt text for accessibility

Provide descriptive alt text.

Apply in both places:

-                                    alt={`flag`}
+                                    alt={`${selectedCountry?.title ?? 'Country'} flag`}

Also applies to: 395-401


165-174: Guard against missing txHash before calling withdraw API

If both receipt?.transactionHash and userOpHash are absent, the API call is made with an invalid hash.

Apply:

-            const txHash = receipt?.transactionHash ?? userOpHash
-            setLoadingState('Withdrawing')
+            const txHash = receipt?.transactionHash ?? userOpHash
+            if (!txHash) {
+                setErrorMessage('Transaction hash not available. Please try again.')
+                return
+            }
+            setLoadingState('Withdrawing')

418-418: Localize destination label by country

Show a generic label outside Argentina.

Apply:

-                        <PaymentInfoRow label="CBU, CVU or Alias" value={bankDetails.destinationAddress} />
+                        <PaymentInfoRow
+                            label={countryPath === 'argentina' ? 'CBU, CVU or Alias' : 'Account Address'}
+                            value={bankDetails.destinationAddress}
+                        />

80-91: Avoid double WebSocket subscriptions for KYC updates

This page subscribes to KYC updates while useMantecaKycFlow also registers a listener. Consolidate to one source to prevent duplicate fetchUser and state updates.

Consider removing this page-level useWebSocket and handling step transitions via useMantecaKycFlow callbacks.


235-238: Add safe fallbacks on success screen

Prevent rendering undefined values if state resets.

Apply:

-                            <div className="text-2xl font-extrabold">
-                                {currencyCode} {currencyAmount}
-                            </div>
+                            <div className="text-2xl font-extrabold">
+                                {(currencyCode ?? 'USD')} {currencyAmount ?? amount ?? '0'}
+                            </div>

111-127: Destination validation is stubbed

validateCbuCvuAlias returns true in src/utils/withdraw.utils.ts, so invalid data will pass.

I can wire a real CBU/CVU/alias validator and tests; want a follow-up PR?

src/components/AddMoney/consts/index.ts (2)

569-571: Potential URL parameter encoding issue.

The method title is being transformed to lowercase and spaces replaced with hyphens for the URL parameter. However, this doesn't handle special characters or accented characters that might appear in method titles (e.g., "SINPE Móvil").

                     // Add path for Manteca countries to route to Manteca flow
                     path: isMantecaCountry
-                        ? `/withdraw/manteca?method=${method.title.toLowerCase().replace(/\s+/g, '-')}&country=${country.path}`
+                        ? `/withdraw/manteca?method=${encodeURIComponent(method.title.toLowerCase().replace(/\s+/g, '-'))}&country=${encodeURIComponent(country.path)}`
                         : undefined,

618-620: Apply URL encoding for consistency and safety.

Similar to the previous comment, URL parameters should be properly encoded.

                 path: isMantecaCountry
-                    ? `/withdraw/manteca?method=bank-transfer&country=${country.path}`
+                    ? `/withdraw/manteca?method=bank-transfer&country=${encodeURIComponent(country.path)}`
                     : `/withdraw/${countryCode.toLowerCase()}/bank`,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5390b7b and 1b621cc.

📒 Files selected for processing (3)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx (1 hunks)
  • src/components/AddMoney/consts/index.ts (4 hunks)
  • src/utils/withdraw.utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-09-18T09:30:42.881Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.881Z
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)/withdraw/manteca/page.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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)/withdraw/manteca/page.tsx
  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-08-14T14:36:18.758Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:0-0
Timestamp: 2025-08-14T14:36:18.758Z
Learning: Bridge API requires ISO3 country codes (3-letter codes like "USA", "GBR") while flag display components need ISO2 codes (2-letter codes like "US", "GB").

Applied to files:

  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
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)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-12-02T17:21:45.515Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in the peanut-ui codebase uses uppercase country codes only, so any functions that look up values in this map should normalize input to uppercase for consistent matching.

Applied to files:

  • src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the ALL_METHODS_DATA structure from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/AddMoney/consts/index.ts
🧬 Code graph analysis (2)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (15)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/hooks/wallet/useWallet.ts (1)
  • useWallet (21-123)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/components/AddMoney/consts/index.ts (1)
  • countryData (278-2446)
src/hooks/useCurrency.ts (1)
  • useCurrency (21-65)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (17-110)
src/hooks/useWebSocket.ts (1)
  • useWebSocket (19-186)
src/utils/withdraw.utils.ts (1)
  • validateCbuCvuAlias (149-151)
src/constants/manteca.consts.ts (1)
  • MANTECA_DEPOSIT_ADDRESS (1-1)
src/utils/general.utils.ts (2)
  • isTxReverted (1313-1316)
  • formatAmount (400-440)
src/services/manteca.ts (1)
  • mantecaApi (95-232)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN_DECIMALS (19-19)
src/components/Global/PeanutLoading/index.tsx (1)
  • PeanutLoading (4-19)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • InitiateMantecaKYCModal (23-70)
src/components/Payment/PaymentInfoRow.tsx (1)
  • PaymentInfoRow (17-83)
src/components/AddMoney/consts/index.ts (2)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (64-125)
src/constants/manteca.consts.ts (1)
  • isMantecaCountry (21-23)
🪛 Biome (2.1.2)
src/app/(mobile-ui)/withdraw/manteca/page.tsx

[error] 119-120: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

🔇 Additional comments (7)
src/components/AddMoney/consts/index.ts (7)

234-234: LGTM! Proper type extension for withdrawal method configuration.

The addition of the optional isSoon property to the withdrawal method type definition is appropriate for indicating feature availability status.


238-245: LGTM! Well-structured Mercado Pago configuration for Argentina.

The expanded object structure with explicit properties provides better clarity and maintainability compared to a simple description string.


643-643: Bank transfer availability logic for Mexico needs clarification.

Mexico (MX) is explicitly marked as isSoon = true even when it might be in the enabled countries set. This seems inconsistent with the comment on line 16 about Mercado Pago not supporting QR payments for Mexico.

Is the bank transfer restriction for Mexico temporary? Should we add a comment explaining why Mexico is treated differently?

                 newMethod.path = `/add-money/${country.path}/bank`
-                newMethod.isSoon = !isCountryEnabledForBankTransfer(countryCode) || countryCode === 'MX'
+                // Mexico bank transfers are temporarily disabled (pending QR payment support)
+                newMethod.isSoon = !isCountryEnabledForBankTransfer(countryCode) || countryCode === 'MX'

576-589: LGTM! Clean SEPA integration logic.

The SEPA Instant withdrawal method is properly added for EUR countries with appropriate deduplication checks.


625-631: LGTM! Proper crypto withdrawal method handling.

The crypto withdrawal method is correctly added with deduplication logic and placed at the beginning of the list for better visibility.


521-521: Confirm bank-transfer support for Bolivia (BO)

Code now enables Bolivia: src/components/AddMoney/consts/index.ts (country entry + enabledBankTransferCountries includes 'BO') and src/constants/manteca.consts.ts (MANTECA_COUNTRIES includes 'bolivia'). Verify backend/payment rails (Manteca/bridge/payment provider), currency BOB support, and any KYC/settlement mappings are in place; if not supported, remove or feature‑gate 'BO' from enabledBankTransferCountries and MANTECA_COUNTRIES.


634-638: Manteca gating for Mercado Pago is correct — MX intentionally excluded

MantecaSupportedExchanges is the explicit gate used for Mercado Pago in add-money and regional-method flows (src/components/AddMoney/consts/index.ts:2633-2652; app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx:14-16; src/components/Common/CountryList.tsx:123-126). The Mexico entry is intentionally commented out in src/components/AddMoney/consts/index.ts (line ~16) and constants/manteca.consts.ts (line ~12) with notes that Mercado Pago QR/payments aren’t supported for Mexico — no change required.

Comment on lines +117 to +124
let isValid = false
switch (countryPath) {
case 'argentina':
isValid = validateCbuCvuAlias(value)
default:
isValid = true
break
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix switch fallthrough that disables Argentina validation

The case 'argentina' falls through to default, overwriting the result and always returning true.

Apply:

         switch (countryPath) {
             case 'argentina':
-                isValid = validateCbuCvuAlias(value)
+                isValid = validateCbuCvuAlias(value)
+                break
             default:
                 isValid = true
                 break
         }

Note: validateCbuCvuAlias currently returns true in src/utils/withdraw.utils.ts; implement real validation ASAP.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let isValid = false
switch (countryPath) {
case 'argentina':
isValid = validateCbuCvuAlias(value)
default:
isValid = true
break
}
let isValid = false
switch (countryPath) {
case 'argentina':
isValid = validateCbuCvuAlias(value)
break
default:
isValid = true
break
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 119-120: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

🤖 Prompt for AI Agents
In src/app/(mobile-ui)/withdraw/manteca/page.tsx around lines 117-124 the switch
for countryPath falls through from case 'argentina' into default, which
overwrites isValid to true and disables Argentina validation; fix by preventing
fallthrough—either add a break (or return) immediately after setting isValid =
validateCbuCvuAlias(value) in the 'argentina' case, or restructure the switch to
set isValid only in each case (leaving default to set true); keep default
behavior true and note that validateCbuCvuAlias should be replaced with real
validation later.

Comment on lines +136 to +149
// Check if we still need to determine KYC status
if (isMantecaKycRequired === null) {
// still loading/determining KYC status, don't proceed yet
return
}

// Check KYC status before proceeding to review
if (isMantecaKycRequired === true) {
setIsKycModalOpen(true)
return
}

setStep('review')
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

KYC can be bypassed before determination completes

isMantecaKycRequired from the hook never returns null (defaults to false), so users can hit “Review” and skip KYC while determination is pending.

Gate on loaded user verifications before proceeding:

-        // Check if we still need to determine KYC status
-        if (isMantecaKycRequired === null) {
-            // still loading/determining KYC status, don't proceed yet
-            return
-        }
+        // Prevent KYC bypass until user verifications are loaded
+        if (!user || user.user?.kycVerifications === undefined) {
+            return
+        }
 
         // Check KYC status before proceeding to review
         if (isMantecaKycRequired === true) {
             setIsKycModalOpen(true)
             return
         }
 
         setStep('review')

Recommended follow-up: update useMantecaKycFlow to return boolean | null (init null) for explicit “undetermined”.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Check if we still need to determine KYC status
if (isMantecaKycRequired === null) {
// still loading/determining KYC status, don't proceed yet
return
}
// Check KYC status before proceeding to review
if (isMantecaKycRequired === true) {
setIsKycModalOpen(true)
return
}
setStep('review')
}
// Prevent KYC bypass until user verifications are loaded
if (!user || user.user?.kycVerifications === undefined) {
return
}
// Check KYC status before proceeding to review
if (isMantecaKycRequired === true) {
setIsKycModalOpen(true)
return
}
setStep('review')
}
🤖 Prompt for AI Agents
In src/app/(mobile-ui)/withdraw/manteca/page.tsx lines 136-149, the null check
on isMantecaKycRequired is ineffective because it never returns null, allowing
KYC to be skipped while determination is pending. Introduce and gate on a
definitive “verifications loaded/determined” signal (e.g., a loading or
isDetermined flag from the hook) and return early until it’s true. After
determination: if KYC required, open the modal and return; only advance to
review when determination is complete and KYC is not required. As a follow-up,
update useMantecaKycFlow to return boolean | null (init null) to make
“undetermined” explicit and use that here.

Comment on lines +149 to +151
export function validateCbuCvuAlias(value: string): boolean {
return true
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement real CBU/CVU/Alias validation (returning true is unsafe).

This stub bypasses client-side validation and can let malformed AR account identifiers through. Add minimal format checks now and leave a TODO for check‑digit validation.

Apply this diff:

-export function validateCbuCvuAlias(value: string): boolean {
-    return true
-}
+// Validates Argentine CBU/CVU (22 digits) or Alias (6–20 chars: a–z, 0–9, dots).
+// Allows spaces/hyphens for readability in numeric inputs.
+export function validateCbuCvuAlias(value: string): boolean {
+    if (!value) return false
+    const raw = value.normalize('NFKC').trim()
+    const numeric = raw.replace(/[\s-]/g, '')
+
+    // CBU/CVU: exactly 22 digits
+    if (/^\d+$/.test(numeric)) {
+        return numeric.length === 22 // TODO: add CBU/CVU check‑digit verification
+    }
+
+    // Alias: 6–20 chars, letters/digits/dots only; no leading/trailing/double dots
+    const alias = raw.toLowerCase()
+    if (!/^[a-z0-9.]{6,20}$/.test(alias)) return false
+    if (alias.startsWith('.') || alias.endsWith('.') || alias.includes('..')) return false
+    return true
+}

Follow‑ups:

  • If UX needs reason strings (like other validators here), return { isValid, error } for consistency. I can provide a typed version if desired.
  • Add unit tests covering: 22‑digit numeric happy path, off‑by‑one lengths, non‑digit chars, alias with invalid chars, leading/trailing/double dots.
  • When ready, implement the official CBU/CVU check‑digit algorithm. I can wire that in once specs are confirmed.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function validateCbuCvuAlias(value: string): boolean {
return true
}
// Validates Argentine CBU/CVU (22 digits) or Alias (6–20 chars: a–z, 0–9, dots).
// Allows spaces/hyphens for readability in numeric inputs.
export function validateCbuCvuAlias(value: string): boolean {
if (!value) return false
const raw = value.normalize('NFKC').trim()
const numeric = raw.replace(/[\s-]/g, '')
// CBU/CVU: exactly 22 digits
if (/^\d+$/.test(numeric)) {
return numeric.length === 22 // TODO: add CBU/CVU check‑digit verification
}
// Alias: 6–20 chars, letters/digits/dots only; no leading/trailing/double dots
const alias = raw.toLowerCase()
if (!/^[a-z0-9.]{6,20}$/.test(alias)) return false
if (alias.startsWith('.') || alias.endsWith('.') || alias.includes('..')) return false
return true
}
🤖 Prompt for AI Agents
In src/utils/withdraw.utils.ts around lines 149-151, the validateCbuCvuAlias
function currently returns true unconditionally; replace this stub with minimal
format validation: if the value consists of exactly 22 digits pass as CBU/CVU
(use /^\d{22}$/), otherwise treat it as an alias and validate against a safe
alias regex that permits letters, digits, dots and underscores (e.g.
/^[A-Za-z0-9._]+$/), and additionally reject aliases that start or end with a
dot or contain consecutive dots; return boolean accordingly and add a TODO
comment to implement the real CBU/CVU check‑digit algorithm later; also add unit
tests for 22‑digit happy path, off‑by‑one lengths, non‑digit chars, alias
invalid chars, and leading/trailing/double dots.

🛠️ Refactor suggestion

🧩 Analysis chain

Confirm official rules before adding check‑digits and alias constraints.

Please verify with authoritative sources before we implement strict checks:

Also check call sites expect a boolean (not { isValid, error }). If mismatch, we should align the API:


🏁 Script executed:

#!/bin/bash
# Find usages and expectation patterns around validateCbuCvuAlias
rg -nP --type=ts -C3 '\bvalidateCbuCvuAlias\s*\('

Length of output: 880


🌐 Web query:

What is the official check digit algorithm for Argentina's 22-digit CBU/CVU, and what are the allowed characters and length constraints for "Alias CBU/CVU" per BCRA or official banking docs?

💡 Result:

Short answer:

  • Check-digit algorithm (CBU/CVU 22 digits): two independent verifiers — one for the first 8-digit block (digits 1–7 produce digit 8) and one for the 14‑digit block (digits 9–21 produce digit 22). Each verifier is computed by a weighted sum and a mod‑10 rule: multiply each digit by the repeating weight pattern, sum the products, compute 10 − (sum mod 10), and if that result = 10 use 0. Weights:

    • First block (7 digits → verifier): weights [7,1,3,9,7,1,3].
    • Second block (13 digits → verifier): weights [3,9,7,1,3,9,7,1,3,9,7,1,3].
      (This is the standard BCRA/CBU validation used by validators/generators and documentation describing the official pattern.) [1][2][3]
  • Alias CBU/CVU allowed characters and length (official): between 6 and 20 characters; may contain letters and digits and the characters dot (.) and hyphen (‑). No spaces; alias is unique and portable. (BCRA / Comunicación A6044 / related BCRA pages). [1][2]

Sources:

  • BCRA — Alias CBU / CBU/CVU explanatory pages. [BCRA pages on Alias CBU and CBU/CVU]. [1][2]
  • External technical description / validators of the official CBU algorithm (weights and mod‑10 implementation). [3]

If you want, I can show a short code snippet (Python, JavaScript, etc.) that implements the two check‑digit checks.


Implement BCRA CBU/CVU validation and Alias constraints

  • Replace stub validateCbuCvuAlias in src/utils/withdraw.utils.ts (lines ~149–151) with full validation: perform two mod‑10 check‑digit checks — block1 weights [7,1,3,9,7,1,3] for digits 1–7 → verifier at digit 8; block2 weights [3,9,7,1,3,9,7,1,3,9,7,1,3] for digits 9–21 → verifier at digit 22; compute verifier as 10 − (sum % 10) and treat 10 as 0; both verifiers must match the input.
  • Enforce Alias rules: length 6–20; allowed characters = letters, digits, dot (.) and hyphen (-); no spaces.
  • Keep the function signature returning boolean (call site expects boolean: src/app/(mobile-ui)/withdraw/manteca/page.tsx:120). Add unit tests covering valid/invalid CBU/CVU and alias cases.
🤖 Prompt for AI Agents
In src/utils/withdraw.utils.ts around lines 149–151 the validateCbuCvuAlias stub
always returns true; replace it with a boolean validator that (1) if the input
looks numeric (CBU/CVU) performs two mod‑10 check‑digit validations: compute sum
of digits 1–7 times weights [7,1,3,9,7,1,3] and verify digit 8 equals verifier =
(10 - (sum % 10)) % 10; compute sum of digits 9–21 times weights
[3,9,7,1,3,9,7,1,3,9,7,1,3] and verify digit 22 equals verifier = (10 - (sum %
10)) % 10; both verifiers must match the corresponding input digits; (2)
otherwise treat the input as an alias and enforce length between 6 and 20,
allowed characters only letters, digits, dot (.) and hyphen (-), and no spaces;
return true only if either CBU/CVU passes both check‑digit checks or alias
passes constraints; add unit tests covering valid and invalid CBUs/CVUs and
aliases to src/utils tests.

@jjramirezn jjramirezn merged commit 2cce14b into feat/manteca-integration Sep 18, 2025
4 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.

2 participants