Skip to content

feat: manteca kyc flow#1189

Merged
kushagrasarathe merged 21 commits intofeat/manteca-integrationfrom
feat/manteca-kyc
Sep 17, 2025
Merged

feat: manteca kyc flow#1189
kushagrasarathe merged 21 commits intofeat/manteca-integrationfrom
feat/manteca-kyc

Conversation

@kushagrasarathe
Copy link
Contributor

@kushagrasarathe kushagrasarathe commented Sep 5, 2025

  • contributes to TASK-12886
  • and also contributes to TASK-12893

description

  • handles manteca kyc integration in deposit flow
  • adds new reusable component for manteca kyc to be integrated in qr, req, claim and withdraw flows
  • updates user settings verification flow to handle geo based kyc

@notion-workspace
Copy link

Implement 2nd KYC Flow

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 5, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds Manteca KYC integration alongside existing Bridge KYC: new hooks, modals, interfaces, API, and websocket events; updates MercadoPago flow with KYC gating and currency data; refactors KYC consumers (history, drawers, status items); introduces unified KYC status hook; revises profile/verification flows; deletes Global Footer.

Changes

Cohort / File(s) Summary
Manteca KYC core integration
src/components/Kyc/InitiateMantecaKYCModal.tsx, src/hooks/useMantecaKycFlow.ts, src/interfaces/interfaces.ts, src/services/manteca.ts, src/hooks/useWebSocket.ts, src/services/websocket.ts, src/app/kyc/success/page.tsx
Adds Manteca KYC modal and hook, new user KYC types, onboarding API, websocket event/handler for Manteca KYC status, and a KYC success page for iframe return.
Add Money: MercadoPago gating & currency wiring
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx, src/components/AddMoney/components/InputAmountStep.tsx, src/components/AddMoney/consts/index.ts, src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
Integrates Manteca KYC gating into MercadoPago, refactors InputAmountStep to accept currencyData, adds MantecaSupportedExchanges, and generalizes MercadoPago enablement per country.
Bridge KYC hook rename and consumers
src/hooks/useBridgeKycFlow.ts, src/components/Kyc/KycFlow.tsx, src/components/Kyc/KycStatusDrawer.tsx, src/components/Kyc/KycStatusItem.tsx, src/components/Kyc/states/* (KycProcessing.tsx, KycCompleted.tsx, KycFailed.tsx), src/app/(mobile-ui)/history/page.tsx, src/components/Home/HomeHistory.tsx
Renames/use useBridgeKycFlow; updates drawer/status item and state components to support Bridge and Manteca data; expands history to include verification entries and new props.
KYC initiation modal swap to Bridge
src/components/Kyc/InitiateBridgeKYCModal.tsx, src/app/(mobile-ui)/add-money/[country]/bank/page.tsx, src/components/AddWithdraw/AddWithdrawCountriesList.tsx, src/components/Claim/Link/views/BankFlowManager.view.tsx, src/components/Request/views/ReqFulfillBankFlowManager.tsx
Replaces InitiateKYCModal with InitiateBridgeKYCModal; aligns imports/usages, preserving props.
Unified KYC status and profile/header usage
src/hooks/useKycStatus.tsx, src/app/(mobile-ui)/home/page.tsx, src/components/Profile/index.tsx, src/components/Profile/views/ProfileEdit.view.tsx, src/components/Profile/components/ProfileHeader.tsx, src/components/UserHeader/index.tsx, src/components/Profile/components/PublicProfile.tsx
Adds useKycStatus; updates profile/home/header components to consume combined KYC status; extends PublicProfile verification to include Manteca ACTIVE.
Identity verification flow and country UI
src/components/Profile/views/IdentityVerification.view.tsx, src/components/Common/CountryList.tsx, src/components/Kyc/KycVerificationInProgressModal.tsx, src/components/Kyc/Country-Flag-And-Name.tsx, src/components/Kyc/CountryRegionRow.tsx, src/components/Global/IconStack.tsx
Overhauls identity verification to choose Bridge vs Manteca per country; extends CountryList API and support checks; adds regional flag components; updates in-progress modal footer; IconStack gets imageClassName.
User/service data and client context
src/services/users.ts, src/hooks/useSavedAccounts.tsx
Extends ApiUser with kycVerifications; marks useSavedAccounts as client.
Global Footer removal
src/components/Global/Footer/index.tsx, src/components/Global/Layout/index.tsx
Deletes Footer component and removes its import from Layout.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: manteca kyc flow" is concise, follows conventional commit style, and accurately captures the primary change — adding Manteca KYC integration across deposit and related flows — so it is relevant and clear for a quick history scan.
Description Check ✅ Passed The PR description references the related tasks and succinctly summarizes the main objectives (Manteca KYC integration in deposit flow, a reusable component, and geo-based KYC updates), which aligns with the changes in the diff; it is brief but on-topic and satisfies the lenient description check.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52a7b71 and 0f90d9d.

📒 Files selected for processing (20)
  • src/app/(mobile-ui)/add-money/[country]/bank/page.tsx (2 hunks)
  • src/app/(mobile-ui)/history/page.tsx (5 hunks)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (3 hunks)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx (3 hunks)
  • src/components/Claim/Link/Initial.view.tsx (3 hunks)
  • src/components/Claim/Link/views/BankFlowManager.view.tsx (2 hunks)
  • src/components/Global/IconStack.tsx (2 hunks)
  • src/components/Home/HomeHistory.tsx (6 hunks)
  • src/components/Kyc/Country-Flag-And-Name.tsx (1 hunks)
  • src/components/Kyc/CountryRegionRow.tsx (1 hunks)
  • src/components/Kyc/InitiateBridgeKYCModal.tsx (2 hunks)
  • src/components/Kyc/KycStatusDrawer.tsx (4 hunks)
  • src/components/Kyc/KycStatusItem.tsx (3 hunks)
  • src/components/Kyc/states/KycCompleted.tsx (2 hunks)
  • src/components/Kyc/states/KycFailed.tsx (2 hunks)
  • src/components/Kyc/states/KycProcessing.tsx (2 hunks)
  • src/components/Request/views/ReqFulfillBankFlowManager.tsx (2 hunks)
  • src/hooks/useBridgeKycFlow.ts (1 hunks)
  • src/interfaces/interfaces.ts (2 hunks)
  • src/services/manteca.ts (1 hunks)

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.

@vercel
Copy link

vercel bot commented Sep 5, 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 17, 2025 9:10am

@coderabbitai coderabbitai bot added the enhancement New feature or request label Sep 5, 2025
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/components/RegionalMethods/MercadoPago/index.tsx (1)

41-63: Enforce KYC gating at submit to prevent bypass.

If the user dismisses the modal, they can still submit. Add a guard so submission re-opens KYC until it’s completed. Relying only on server-side gating degrades UX and can cause confusion.

     const handleAmountSubmit = async () => {
         if (!selectedCountry?.currency) return
 
         try {
             setError(null)
+            // Block submission until KYC is complete
+            if (isMantecaKycRequired) {
+                setIsKycModalOpen(true)
+                return
+            }
             setIsCreatingDeposit(true)
             const depositData = await createMantecaOnramp({
                 usdAmount: tokenUSDAmount.replace(/,/g, ''),
                 currency: selectedCountry.currency,
             })
             if (depositData.error) {
                 setError(depositData.error)
                 return
             }
             setDepositDetails(depositData.data)
             setStep('depositDetails')
         } catch (error) {
-            console.log(error)
-            setError(error as string)
+            console.log(error)
+            const msg = error instanceof Error ? error.message : String(error)
+            setError(msg)
         } finally {
             setIsCreatingDeposit(false)
         }
     }
🧹 Nitpick comments (7)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2)

28-33: Avoid redundant state churn when auto-opening modal.

Guard to only set when transitioning from closed → open.

-    useEffect(() => {
-        if (isMantecaKycRequired) {
-            setIsKycModalOpen(true)
-        }
-    }, [isMantecaKycRequired])
+    useEffect(() => {
+        if (isMantecaKycRequired && !isKycModalOpen) {
+            setIsKycModalOpen(true)
+        }
+    }, [isMantecaKycRequired, isKycModalOpen])

84-87: Optional: Auto-continue after successful KYC.

If product wants a streamlined flow, optionally trigger handleAmountSubmit() after success (when the amount is already filled), or surface a toast instructing users to continue.

src/components/Kyc/InitiateMantecaKYCModal.tsx (2)

14-18: Surface onboarding errors in the modal UI.

Failures from openMantecaKyc are stored in the hook’s error state but never shown. Show a short error message and keep the CTA for retry.

Apply this diff:

-    const { isLoading, iframeOptions, openMantecaKyc, handleIframeClose } = useMantecaKycFlow({
+    const { isLoading, iframeOptions, openMantecaKyc, handleIframeClose, error } = useMantecaKycFlow({
         onClose: onManualClose, // any non-success close from iframe is a manual close in case of Manteca KYC
         onSuccess: onKycSuccess,
         onManualClose,
     })
@@
-            <ActionModal
+            <ActionModal
                 visible={isOpen && !iframeOptions.visible}
                 onClose={onClose}
                 title="Verify your identity first"
-                description="To continue, you need to complete identity verification. This usually takes just a few minutes."
+                description={
+                    error
+                        ? `We couldn't start verification. ${error}`
+                        : 'To continue, you need to complete identity verification. This usually takes just a few minutes.'
+                }
                 icon={'badge' as IconName}
                 modalPanelClassName="max-w-full m-2"
                 ctaClassName="grid grid-cols-1 gap-3"
                 ctas={[
                     {
                         text: isLoading ? 'Loading...' : 'Verify now',
-                        onClick: () => openMantecaKyc(),
+                        onClick: openMantecaKyc,
                         variant: 'purple',
                         disabled: isLoading,
                         shadowSize: '4',
                         icon: 'check-circle',
                         className: 'h-11',
                     },
                 ]}
             />

Also applies to: 22-41


3-3: Use a type-only import for IconName.

Prevents pulling runtime code into the bundle for a type.

-import { IconName } from '@/components/Global/Icons/Icon'
+import type { IconName } from '@/components/Global/Icons/Icon'
src/hooks/useMantecaKycFlow.ts (3)

29-45: Guard against double-invocation and type the return.

Minor race: rapid clicks can trigger multiple requests before isLoading disables the CTA. Add an early guard and explicit return type; also coalesce error strings once.

-    const openMantecaKyc = useCallback(async () => {
+    const openMantecaKyc = useCallback(
+        async (): Promise<{ success: true } | { success: false; error: string }> => {
+            if (isLoading || iframeOptions.visible) {
+                return { success: false as const, error: 'Onboarding already in progress' }
+            }
         setIsLoading(true)
         setError(null)
         try {
-            const { url } = await mantecaApi.initiateOnboarding({ returnUrl: window.location.href })
+            const { url } = await mantecaApi.initiateOnboarding({
+                returnUrl: window.location.href,
+                failureUrl: window.location.href,
+            })
             setIframeOptions({
                 src: url,
                 visible: true,
+                closeConfirmMessage: 'Are you sure you want to cancel verification?',
             })
             return { success: true as const }
         } catch (e: any) {
-            setError(e?.message ?? 'Failed to initiate onboarding')
-            return { success: false as const, error: e?.message as string }
+            const message = e?.message ?? 'Failed to initiate onboarding'
+            setError(message)
+            return { success: false as const, error: message }
         } finally {
             setIsLoading(false)
         }
-    }, [])
+        },
+        [isLoading, iframeOptions.visible]
+    )

25-27: Derivation looks good; tiny nit on dependencies.

The dependency uses an optional chain expression. Consider deriving a local status = user?.user?.mantecaKycStatus and using [status] for readability.


63-70: Expose error handling guidance to consumers.

You already return error. Document in the JSDoc or README that UIs should surface it and allow retry; tie into toast/logging if available.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bfe4da5 and 3afb9af.

📒 Files selected for processing (5)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (1 hunks)
  • src/hooks/useMantecaKycFlow.ts (1 hunks)
  • src/interfaces/interfaces.ts (2 hunks)
  • src/services/manteca.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
🧬 Code graph analysis (4)
src/hooks/useMantecaKycFlow.ts (3)
src/components/Global/IframeWrapper/index.tsx (1)
  • IFrameWrapperProps (9-14)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/services/manteca.ts (1)
  • mantecaApi (5-23)
src/components/Kyc/InitiateMantecaKYCModal.tsx (2)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (12-71)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (62-120)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (4)
src/types/manteca.types.ts (1)
  • MantecaDepositDetails (1-5)
src/components/AddMoney/consts/index.ts (1)
  • countryData (256-1945)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (12-71)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • InitiateMantecaKYCModal (13-45)
src/services/manteca.ts (2)
src/utils/sentry.utils.ts (1)
  • fetchWithSentry (11-89)
src/constants/general.consts.ts (1)
  • PEANUT_API_URL (43-47)
🔇 Additional comments (2)
src/interfaces/interfaces.ts (1)

224-225: Confirm Manteca KYC status lifecycle.

Are these the complete statuses? If there are states like REJECTED/SUSPENDED/PENDING-REVIEW, encode them now to avoid downstream condition churn.

src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

14-18: Confirm callback wiring semantics.

You map the hook’s onClose to onManualClose, and also pass onManualClose directly. This makes all non-success iframe closures invoke onManualClose (including tos_accepted). Confirm that this is intended and that parents don’t expect different behavior for onClose vs onManualClose.

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/hooks/useBridgeKycFlow.ts (1)

128-167: Fix stale callback risk: add onManualClose to deps of handleIframeClose.
onManualClose is captured but not listed in the dependency array; it can become stale and fail to fire in some renders.

Apply:

-        [iframeOptions.src, apiResponse, flow, router]
+        [iframeOptions.src, apiResponse, flow, router, onManualClose]
🧹 Nitpick comments (5)
src/hooks/useBridgeKycFlow.ts (4)

95-99: Remove redundant setIsLoading(false) — already handled in finally.
Prevents double state churn.

             if (response.error) {
                 setError(response.error)
-                setIsLoading(false)
                 return { success: false, error: response.error }
             }

10-19: Delete unused Persona event types.
They’re not referenced in this hook.

-// persona event detail types
-interface PersonaEventDetail {
-    inquiryId: string
-    status: string
-    sessionToken?: string
-}
-
-interface PersonaEvent extends Event {
-    detail: PersonaEventDetail
-}

121-123: Harden error handling: avoid any in catch.
Preserve type safety and robustly derive message.

-        } catch (e: any) {
-            setError(e.message)
-            return { success: false, error: e.message }
+        } catch (e: unknown) {
+            const message = e instanceof Error ? e.message : String(e)
+            setError(message)
+            return { success: false, error: message }

88-126: Add explicit result type for handleInitiateKyc.
Improves call-site ergonomics and narrows unions.

+    type InitiateKycResult =
+        | { success: true; data: InitiateKycResponse }
+        | { success: false; error: string }
-    const handleInitiateKyc = async () => {
+    const handleInitiateKyc = async (): Promise<InitiateKycResult> => {
src/components/Kyc/index.tsx (1)

44-61: Error CTA currently closes the modal instead of retrying; make it a real “Retry”.

Clicking the error CTA should retry initiation rather than dismiss the prompt.

Apply:

-                    {
-                        hidden: !error,
-                        text: error ?? 'Retry',
-                        onClick: onClose,
-                        variant: 'transparent',
-                        className:
-                            'underline text-xs md:text-sm !font-normal w-full !transform-none !pt-2 text-error-3 px-0',
-                    },
+                    {
+                        hidden: !error,
+                        text: 'Retry',
+                        onClick: handleVerifyClick,
+                        variant: 'transparent',
+                        className:
+                            'underline text-xs md:text-sm !font-normal w-full !transform-none !pt-2 text-error-3 px-0',
+                    },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3afb9af and 781501a.

📒 Files selected for processing (6)
  • src/app/(mobile-ui)/history/page.tsx (1 hunks)
  • src/components/Home/HomeHistory.tsx (1 hunks)
  • src/components/Kyc/KycFlow.tsx (1 hunks)
  • src/components/Kyc/KycStatusDrawer.tsx (2 hunks)
  • src/components/Kyc/index.tsx (2 hunks)
  • src/hooks/useBridgeKycFlow.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/hooks/useBridgeKycFlow.ts
  • src/components/Kyc/index.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/hooks/useBridgeKycFlow.ts
  • src/components/Kyc/index.tsx
  • src/components/Kyc/KycFlow.tsx
🧬 Code graph analysis (3)
src/components/Kyc/index.tsx (1)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
src/components/Kyc/KycFlow.tsx (2)
src/components/0_Bruddle/Button.tsx (1)
  • ButtonProps (22-40)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
src/components/Kyc/KycStatusDrawer.tsx (1)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
🔇 Additional comments (11)
src/hooks/useBridgeKycFlow.ts (3)

38-38: Rename looks good — consistent with bridge flow migration.


154-161: Confirm expected behavior on manual close for flow === 'withdraw'.
Currently only add redirects and request_fulfillment invokes onManualClose. If withdraw should also notify/close, wire it similarly.

Optionally:

-            if (source === 'manual') {
+            if (source === 'manual') {
                 setIframeOptions((prev) => ({ ...prev, visible: false }))
                 if (flow === 'add') {
                     router.push('/add-money')
-                } else if (flow === 'request_fulfillment') {
+                } else if (flow === 'request_fulfillment' || flow === 'withdraw') {
                     onManualClose?.()
                 }
                 return
             }

71-86: Remove KYC success ordering suggestion — inapplicable
useBridgeKycFlow’s onKycSuccess is only passed to KycStatusDrawer (invoking onClose) and there are no setJustCompletedKyc or fetchUser calls in that callback, so the loop concern doesn’t apply.

Likely an incorrect or invalid review comment.

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

16-16: Import migration to bridge hook is correct.
No other logic changes; history rendering remains unaffected.

src/components/Home/HomeHistory.tsx (1)

17-17: Switched KYC utilities import to bridge hook — looks good.
Downstream typing (KycHistoryEntry) still aligns.

src/components/Kyc/KycStatusDrawer.tsx (2)

9-9: Hook import updated to bridge variant — OK.


57-58: Using useBridgeKycFlow({ onKycSuccess: onClose }) is appropriate.
Ensures drawer closes post-approval; iframe visibility wiring remains intact.

src/components/Kyc/KycFlow.tsx (2)

3-3: Import updated to bridge hook — OK.


9-9: Hook usage swap LGTM.
Button disabled state and iframe wiring unchanged.

src/components/Kyc/index.tsx (2)

15-25: Hook call and destructuring align with useBridgeKycFlow API.

Props forwarded as expected; no behavior change in this component beyond the hook source.


2-2: Migration off useKycFlow verified: no remaining imports or usages of useKycFlow; all KYC components consistently use useBridgeKycFlow.

Copy link
Contributor

@jjramirezn jjramirezn left a comment

Choose a reason for hiding this comment

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

Looks good, left some comments.

How do you want to handle the work needed after today workshop and manu designs. Do you want to merge this and then do that? Or do it in this PR?

Im slightly in favor of merging if that makes sense

visible: true,
})
return { success: true as const }
} catch (e: any) {
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: don't use any

Error catching in typescript is a little painful, but I prefer to have e as unknown and then explicitly check for isinstanceof Error and then use e.message instead of marking is as any.

@jjramirezn
Copy link
Contributor

Also, failing tests are fixed in feat/manteca-integration pull from there

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

Caution

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

⚠️ Outside diff range comments (2)
src/services/websocket.ts (1)

217-231: Singleton masks username/path changes — key by URL+path.

If username changes, reconnects reuse the first path. Make the cache key include URL+path or drop the singleton.

Proposed replacement:

// replace the singleton with a small pool keyed by endpoint
const sockets = new Map<string, PeanutWebSocket>()

export const getWebSocketInstance = (username?: string): PeanutWebSocket => {
  if (typeof window === 'undefined') throw new Error('No window')
  let wsUrl = process.env.NEXT_PUBLIC_PEANUT_WS_URL || ''
  const path = username ? `/ws/charges/${username}` : '/ws/charges'
  const isLocal = ['localhost','127.0.0.1','::1'].includes(window.location.hostname)
  if (isLocal && wsUrl.startsWith('wss://')) wsUrl = wsUrl.replace('wss://','ws://')
  const key = `${wsUrl}${path}`
  if (!sockets.has(key)) sockets.set(key, new PeanutWebSocket(wsUrl, path))
  return sockets.get(key)!
}
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (1)

79-82: Harden error handling (avoid casting to string and log as error).

Casting may show “[object Object]”. Extract a proper message and log with console.error.

-        } catch (error) {
-            console.log(error)
-            setError(error as string)
+        } catch (err) {
+            console.error(err)
+            const message =
+                err instanceof Error ? err.message : typeof err === 'string' ? err : 'Unexpected error'
+            setError(message)
         } finally {
♻️ Duplicate comments (2)
src/services/manteca.ts (2)

13-16: Redact secrets before logging to Sentry.

fetchWithSentry captures request headers; redact Authorization/Cookie there to stop JWT leakage across the codebase.

Add in fetchWithSentry:

const redactHeaders = (h: RequestInit['headers']) => {
  const entries = h instanceof Headers ? Array.from(h.entries())
    : Array.isArray(h) ? h : Object.entries(h || {})
  return Object.fromEntries(
    entries.map(([k, v]) =>
      /authorization|cookie|set-cookie|x-api-key/i.test(k)
        ? [k, 'REDACTED']
        : [k, v]
    )
  )
}
// when capturing to Sentry:
extra: {
  // ...
  requestHeaders: redactHeaders(options.headers || {}),
  // ...
}

11-18: JWT leakage risk + missing auth/timeout/validation.

Authorization header flows into fetchWithSentry which logs headers; add auth guard, timeout, and response validation. Also ensure headers are redacted in Sentry (see next comment).

-        const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/initiate-onboarding`, {
+        const token = Cookies.get('jwt-token')
+        if (!token) {
+            throw new Error('Not authenticated')
+        }
+        const controller = new AbortController()
+        const timeoutId = setTimeout(() => controller.abort(), 15000)
+        const response = await fetchWithSentry(`${PEANUT_API_URL}/manteca/initiate-onboarding`, {
             method: 'POST',
             headers: {
                 'Content-Type': 'application/json',
-                Authorization: `Bearer ${Cookies.get('jwt-token')}`,
+                Accept: 'application/json',
+                Authorization: `Bearer ${token}`,
             },
-            body: JSON.stringify(params),
+            body: JSON.stringify(params),
+            signal: controller.signal,
         })
 
         if (!response.ok) {
             const errorData = await response.json().catch(() => ({}))
             throw new Error(errorData.message || `Failed to get onboarding URL`)
         }
 
-        return response.json()
+        const data = await response.json().catch(() => ({}))
+        clearTimeout(timeoutId)
+        if (!data || typeof data.url !== 'string' || !data.url) {
+            throw new Error('Invalid onboarding response')
+        }
+        return data
🧹 Nitpick comments (11)
src/services/websocket.ts (2)

4-10: Tighten message typing: data is not always HistoryEntry.

Switch to a discriminated union so kyc events carry {status: string} and history carries HistoryEntry. This removes unsafe casts and “in” checks.

Example:

type PongMsg = { type: 'pong' }
type PingMsg = { type: 'ping' }
type HistoryMsg = { type: 'history_entry'; data: HistoryEntry }
type KycMsg = { type: 'kyc_status_update' | 'manteca_kyc_status_update' | 'persona_tos_status_update'; data: { status: string } }

export type WebSocketMessage = PongMsg | PingMsg | HistoryMsg | KycMsg

219-225: Broaden localhost detection for ws downgrade.

Covers only 'localhost'. Include 127.0.0.1 and ::1. Also guard against empty env.

-        if (window.location.hostname === 'localhost' && wsUrl.startsWith('wss://')) {
+        const isLocalHost =
+            ['localhost', '127.0.0.1', '::1'].includes(window.location.hostname)
+        if (isLocalHost && wsUrl && wsUrl.startsWith('wss://')) {
             wsUrl = wsUrl.replace('wss://', 'ws://')
         }
src/app/(mobile-ui)/kyc/success/page.tsx (2)

7-9: Fix typos in comment.

“Incase” → “In case”; “webosckets” → “websockets”.

-This page is just to let users know that their KYC was successful. Incase there's some issue with webosckets closing the modal, ideally this should not happen but added this as fallback guide
+This page lets users know their KYC was successful. In case there's an issue with websockets closing the modal, this serves as a fallback guide.

12-15: Constrain postMessage targetOrigin.

Avoid ''. Use a known origin via env, falling back to ''.

-        if (window.parent) {
-            window.parent.postMessage({ source: 'peanut-kyc-success' }, '*')
-        }
+        if (window.parent) {
+            const target = process.env.NEXT_PUBLIC_PARENT_APP_ORIGIN || '*'
+            window.parent.postMessage({ source: 'peanut-kyc-success' }, target)
+        }
src/components/AddMoney/consts/index.ts (2)

7-19: Solid: explicit Manteca support map. Consider literal typing.

Mark as const to preserve literal keys/values for safer indexing.

-export const MantecaSupportedExchanges = {
+export const MantecaSupportedExchanges = {
   AR: 'ARGENTINA',
   CL: 'CHILE',
   BR: 'BRAZIL',
   CO: 'COLOMBIA',
   PA: 'PANAMA',
   CR: 'COSTA_RICA',
   GT: 'GUATEMALA',
   MX: 'MEXICO',
   PH: 'PHILIPPINES',
   BO: 'BOLIVIA',
-}
+} as const

1616-1634: Avoid unsafe cast when gating Mercado Pago.

Use a boolean helper and check method first to short-circuit safely.

-                return !!MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
+                const supportsManteca =
+                    Boolean((MantecaSupportedExchanges as Record<string, unknown>)[countryCode])
+                return supportsManteca
-            } else if (
-                newMethod.id === 'mercado-pago-add' &&
-                MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
-            ) {
+            } else if (newMethod.id === 'mercado-pago-add' && (MantecaSupportedExchanges as Record<string, unknown>)[countryCode]) {
                 newMethod.isSoon = false
                 newMethod.path = `/add-money/${country.path}/mercadopago`
             } else if (newMethod.id === 'mercado-pago-add') {
src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx (1)

12-17: Safer guard order and no casts.

Check method first; then ensure countryDetails exists; then key presence.

-    if (
-        MantecaSupportedExchanges[countryDetails?.id as keyof typeof MantecaSupportedExchanges] &&
-        method === 'mercadopago'
-    ) {
+    if (
+        method === 'mercadopago' &&
+        countryDetails &&
+        countryDetails.id in MantecaSupportedExchanges
+    ) {
         return <MercadoPago />
     }
src/components/AddMoney/components/InputAmountStep.tsx (1)

51-57: Avoid non-null assertions; derive currency object safely.

Prevents accidental undefineds if symbol/code desyncs.

-                    currency={
-                        currencyData && currencyData.price && currencyData.price > 0
-                            ? {
-                                  code: currencyData.code!,
-                                  symbol: currencyData.symbol!,
-                                  price: currencyData.price!,
-                              }
-                            : undefined
-                    }
+                    currency={
+                        currencyData &&
+                        typeof currencyData.price === 'number' &&
+                        currencyData.price > 0 &&
+                        currencyData.code &&
+                        currencyData.symbol
+                            ? {
+                                  code: currencyData.code,
+                                  symbol: currencyData.symbol,
+                                  price: currencyData.price,
+                              }
+                            : undefined
+                    }
src/components/Kyc/InitiateMantecaKYCModal.tsx (2)

29-29: Drop unnecessary type cast.

'badge' is already part of IconName; the cast can be removed.

-                icon={'badge' as IconName}
+                icon="badge"

22-43: Surface onboarding errors to the user.

useMantecaKycFlow exposes error but the modal hides it. Consider a minimal inline error or toast on failure.

src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (1)

30-30: Avoid defaulting to ARS before selectedCountry resolves.

Prevents unnecessary ARS price fetch when country is unknown.

-    const currencyData = useCurrency(selectedCountry?.currency ?? 'ARS')
+    const currencyData = useCurrency(selectedCountry?.currency ?? null)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 781501a and 4228d45.

📒 Files selected for processing (12)
  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx (1 hunks)
  • src/app/(mobile-ui)/kyc/success/page.tsx (1 hunks)
  • src/components/AddMoney/components/InputAmountStep.tsx (2 hunks)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2 hunks)
  • src/components/AddMoney/consts/index.ts (3 hunks)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx (1 hunks)
  • src/components/Global/Footer/index.tsx (0 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (1 hunks)
  • src/hooks/useMantecaKycFlow.ts (1 hunks)
  • src/hooks/useWebSocket.ts (6 hunks)
  • src/services/manteca.ts (1 hunks)
  • src/services/websocket.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Global/Footer/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useMantecaKycFlow.ts
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-05-15T14:45:45.632Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#857
File: src/hooks/useWebSocket.ts:5-5
Timestamp: 2025-05-15T14:45:45.632Z
Learning: For the WebSocketStatus type in useWebSocket.ts, it was decided to keep it as a non-exported type until there's a specific need to export it. The team prefers to address potential issues when they become actual requirements rather than preemptively.

Applied to files:

  • src/services/websocket.ts
  • src/hooks/useWebSocket.ts
📚 Learning: 2025-05-15T14:47:26.891Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#857
File: src/hooks/useWebSocket.ts:77-82
Timestamp: 2025-05-15T14:47:26.891Z
Learning: The useWebSocket hook in src/hooks/useWebSocket.ts is designed to provide raw history entries, while the components using it (such as HomeHistory.tsx) are responsible for implementing deduplication logic based on UUID to prevent duplicate entries when combining WebSocket data with other data sources.

Applied to files:

  • src/services/websocket.ts
  • src/hooks/useWebSocket.ts
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#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
📚 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/components/AddMoney/components/InputAmountStep.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/components/AddMoney/components/InputAmountStep.tsx
  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/consts/index.ts
📚 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/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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-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/components/AddMoney/components/InputAmountStep.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
🧬 Code graph analysis (5)
src/components/AddMoney/components/InputAmountStep.tsx (1)
src/hooks/useCurrency.ts (1)
  • useCurrency (10-48)
src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx (1)
src/components/AddMoney/consts/index.ts (2)
  • CountryData (153-161)
  • MantecaSupportedExchanges (8-19)
src/services/manteca.ts (2)
src/utils/sentry.utils.ts (1)
  • fetchWithSentry (11-89)
src/constants/general.consts.ts (1)
  • PEANUT_API_URL (43-47)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (7)
src/types/manteca.types.ts (1)
  • MantecaDepositDetails (1-5)
src/components/AddMoney/consts/index.ts (1)
  • countryData (270-1959)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (14-77)
src/hooks/useCurrency.ts (1)
  • useCurrency (10-48)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useWebSocket.ts (1)
  • useWebSocket (19-186)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • InitiateMantecaKYCModal (15-47)
src/components/Kyc/InitiateMantecaKYCModal.tsx (3)
src/components/AddMoney/consts/index.ts (1)
  • CountryData (153-161)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (14-77)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (62-120)
🔇 Additional comments (7)
src/services/websocket.ts (1)

119-124: Good addition: emits Manteca KYC updates.

Case handling mirrors existing KYC events and is consistent.

src/hooks/useWebSocket.ts (3)

12-13: Nice: dedicated callback for Manteca KYC.

Matches the event name and keeps API symmetric with existing KYC/TOS callbacks.


136-142: Handler mirrors existing KYC flow.

Looks good; logs when unset is helpful during integration.


158-159: Confirm instance identity across username changes.

Since getWebSocketInstance caches a singleton, reconnecting after username change may reuse the old path. Pair this with the proposed Map-by-endpoint change in websocket.ts.

Would you like a follow-up PR to key the WS instance by url+path and add tests?

Also applies to: 173-174

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

34-36: Good UX: block screen until currency loads.

Spinner gating is appropriate here.

src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

16-20: Confirm intent: mapping all non-success closes to onManualClose.

Passing onClose: onManualClose means any non-manual, non-success close (e.g., 'tos_accepted' or unexpected) is treated as manual. Verify this matches product expectations.

src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (1)

61-64: Idempotent open is fine; optional: guard redundant sets.

setIsKycModalOpen(true) is harmless even if already open. No change required unless you see flicker.

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

♻️ Duplicate comments (1)
src/hooks/useMantecaKycFlow.ts (1)

74-88: Clear iframe src on close to avoid token/PII persistence (and mark KYC done on success)

On close, only visible flips; keep src around. Clear it. Optionally, set isMantecaKycRequired to false on completion to improve UX until user refresh completes.

-    const handleIframeClose = useCallback(
-        (source?: 'manual' | 'completed' | 'tos_accepted') => {
-            setIframeOptions((prev) => ({ ...prev, visible: false }))
+    const handleIframeClose = useCallback(
+        (source?: 'manual' | 'completed' | 'tos_accepted') => {
+            setIframeOptions({ src: '', visible: false, closeConfirmMessage: undefined })
             if (source === 'completed') {
+                setNeedsMantecaKyc(false)
                 onSuccess?.()
                 return
             }
             if (source === 'manual') {
                 onManualClose?.()
                 return
             }
             onClose?.()
         },
         [onClose, onSuccess, onManualClose]
     )
🧹 Nitpick comments (6)
src/interfaces/interfaces.ts (1)

230-238: Use a discriminated union; avoid typing BRIDGE verifications with Manteca status

provider: 'BRIDGE' with status: MantecaKycStatus is misleading and invites bugs. Model provider-specific fields and statuses via a discriminated union (or narrow to MANTECA only until BRIDGE is migrated).

Apply:

-export interface IUserKycVerification {
-    provider: 'MANTECA' | 'BRIDGE'
-    mantecaGeo?: string | null
-    bridgeGeo?: string | null
-    status: MantecaKycStatus
-    approvedAt?: string | null
-    providerUserId?: string | null
-    providerRawStatus?: string | null
-}
+export type IUserKycVerification =
+    | {
+          provider: 'MANTECA'
+          mantecaGeo?: string | null
+          status: MantecaKycStatus
+          approvedAt?: string | null
+          providerUserId?: string | null
+          providerRawStatus?: string | null
+      }
+    | {
+          provider: 'BRIDGE'
+          bridgeGeo?: string | null
+          status: BridgeKycStatus
+          approvedAt?: string | null
+          providerUserId?: string | null
+          providerRawStatus?: string | null
+      }
src/hooks/useMantecaKycFlow.ts (3)

59-64: Add a close confirmation to reduce accidental exits

Provide a closeConfirmMessage while the iframe is open.

-            setIframeOptions({
-                src: url,
-                visible: true,
-            })
+            setIframeOptions({
+                src: url,
+                visible: true,
+                closeConfirmMessage: 'Are you sure you want to exit verification?',
+            })

33-41: Rename local to isUserActiveForSelectedGeo

Minor readability fix.

-            const isuserActiveForSelectedGeo = userKycVerifications.some(
+            const isUserActiveForSelectedGeo = userKycVerifications.some(
@@
-            setNeedsMantecaKyc(!isuserActiveForSelectedGeo)
+            setNeedsMantecaKyc(!isUserActiveForSelectedGeo)

44-49: Comment-code mismatch

Comment mentions “keep as null (undetermined)” but state is boolean. Align comment or switch to tri-state.

-        // if no verifications data available, keep as null (undetermined)
-        // only set to true if we have user data but no matching verification
+        // If no verifications data is available but we do have user data,
+        // assume KYC is required until proven otherwise (set to true).
src/app/kyc/success/page.tsx (2)

12-15: Restrict postMessage targetOrigin and only post when in an iframe

Avoid '*'; use same-origin, and skip when not framed.

-        if (window.parent) {
-            window.parent.postMessage({ source: 'peanut-kyc-success' }, '*')
-        }
+        if (window.parent && window.parent !== window) {
+            window.parent.postMessage({ source: 'peanut-kyc-success' }, window.location.origin)
+        }

7-9: Fix typos in comment

“webosckets” → “websockets”, “Incase” → “In case”.

-This page is just to let users know that their KYC was successful. Incase there's some issue with webosckets closing the modal, ideally this should not happen but added this as fallback guide
+This page lets users know their KYC was successful. In case there's an issue with websockets closing the modal, this serves as a fallback guide.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4228d45 and d066eb4.

📒 Files selected for processing (6)
  • src/app/kyc/success/page.tsx (1 hunks)
  • src/components/AddMoney/components/InputAmountStep.tsx (3 hunks)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (1 hunks)
  • src/hooks/useMantecaKycFlow.ts (1 hunks)
  • src/interfaces/interfaces.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/Kyc/InitiateMantecaKYCModal.tsx
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-02-17T14:08:59.164Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#669
File: src/components/Global/Icon/index.tsx:216-216
Timestamp: 2025-02-17T14:08:59.164Z
Learning: Avoid using 'any' type in TypeScript code as it bypasses type checking. Use more specific types like string, union types, or keyof typeof for better type safety.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/hooks/useMantecaKycFlow.ts
🧬 Code graph analysis (1)
src/hooks/useMantecaKycFlow.ts (4)
src/components/AddMoney/consts/index.ts (2)
  • CountryData (153-161)
  • MantecaSupportedExchanges (8-19)
src/components/Global/IframeWrapper/index.tsx (1)
  • IFrameWrapperProps (9-14)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/services/manteca.ts (1)
  • mantecaApi (5-27)
🔇 Additional comments (3)
src/interfaces/interfaces.ts (2)

224-228: Confirm enum coverage vs provider statuses

Are ONBOARDING/ACTIVE/INACTIVE exhaustive for Manteca? Please confirm mapping against backend/Webhook statuses to avoid “unknown” states leaking into UI.


249-249: LGTM: optional array preserves backward compatibility

Making kycVerifications optional avoids breaking older user payloads.

src/hooks/useMantecaKycFlow.ts (1)

65-69: Good: error typed as unknown with instanceof guard

Matches our “no any in errors” guideline.

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 (3)
src/components/Profile/views/IdentityVerification.view.tsx (3)

22-29: Refresh user on KYC approval via onKycSuccess

Pass onKycSuccess to ensure store consistency immediately after approval (no need to rely on later background updates).

Apply:

-    } = useBridgeKycFlow()
+    } = useBridgeKycFlow({
+        onKycSuccess: async () => {
+            await fetchUser()
+        },
+    })

34-41: Derive and sanitize name inside useMemo; trim/condense whitespace

Reduces redundant deps and avoids trailing/multiple spaces.

Apply:

-    const initialUserDetails: Partial<UserDetailsFormData> = useMemo(
-        () => ({
-            firstName: user?.user.fullName ? firstName : '',
-            lastName: user?.user.fullName ? lastName : '',
-            email: user?.user.email ?? '',
-        }),
-        [user?.user.fullName, user?.user.email, firstName, lastName]
-    )
+    const initialUserDetails: Partial<UserDetailsFormData> = useMemo(() => {
+        const [first, ...rest] = (user?.user.fullName ?? '').trim().split(/\s+/)
+        const last = rest.join(' ')
+        return {
+            firstName: first ?? '',
+            lastName: last ?? '',
+            email: user?.user.email ?? '',
+        }
+    }, [user?.user.fullName, user?.user.email])

And remove the now-unneeded firstName/lastName vars above this block.


47-52: Normalize fullName before sending to API

Prevents trailing spaces when last name is empty and collapses multi-spaces.

Apply:

-                fullName: `${data.firstName} ${data.lastName}`,
+                fullName: `${data.firstName} ${data.lastName}`.trim().replace(/\s+/g, ' '),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d066eb4 and d7e6c0f.

📒 Files selected for processing (6)
  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx (1 hunks)
  • src/components/AddMoney/components/InputAmountStep.tsx (2 hunks)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (3 hunks)
  • src/components/AddMoney/consts/index.ts (3 hunks)
  • src/components/Global/Layout/index.tsx (0 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Global/Layout/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/(mobile-ui)/add-money/[country]/[regional-method]/page.tsx
  • src/components/AddMoney/components/InputAmountStep.tsx
  • src/components/AddMoney/consts/index.ts
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/Profile/views/IdentityVerification.view.tsx
🧬 Code graph analysis (1)
src/components/Profile/views/IdentityVerification.view.tsx (1)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
🔇 Additional comments (3)
src/components/Profile/views/IdentityVerification.view.tsx (3)

10-12: Hook migration + import cleanup LGTM

Switch to useBridgeKycFlow and removal of default React import are correct and align with prior KYC invariants (ToS acceptance -> KYC link always present). Leveraging the new hook’s API matches our earlier learnings.


81-89: Button disabled/loading state wiring looks good

Combining isUpdatingUser and isKycLoading for loading/disable states yields clean UX.


95-101: IframeWrapper emits 'completed' — no change required.
Confirmed: src/components/Global/IframeWrapper/index.tsx calls onClose('completed') when receiving a postMessage {name: 'complete', metadata.status: 'completed'}, and src/hooks/useBridgeKycFlow.ts's handleIframeClose('completed') opens the KycVerificationInProgressModal.

@notion-workspace
Copy link

Manteca kyc updates

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 (3)
src/hooks/useSavedAccounts.tsx (1)

17-22: Fix unsafe optional chaining to avoid a runtime TypeError.

If user is null/undefined, user?.accounts becomes undefined and .filter(...) will throw before ?? [] runs. Chain the call or default earlier.

Apply this diff:

-        return (
-            user?.accounts.filter(
+        return (
+            user?.accounts?.filter(
                 (acc) => acc.type === AccountType.IBAN || acc.type === AccountType.US || acc.type === AccountType.CLABE
             ) ?? []
         )
-    }, [user])
+    }, [user?.accounts])
src/hooks/useDetermineBankClaimType.ts (1)

31-37: Effect missing dependency: receiver KYC status won’t react to changes.

Add isUserBridgeKycApproved to the dependency array to recompute claim type when KYC status updates (e.g., after completing KYC).

-    }, [user, senderUserId, setSenderDetails])
+    }, [user, senderUserId, setSenderDetails, isUserBridgeKycApproved])

Also applies to: 75-76

src/components/Claim/Link/Initial.view.tsx (1)

334-346: Harden account lookup against undefined arrays.

user.accounts might be undefined; add optional chaining to prevent a crash.

-                if (isUserBridgeKycApproved) {
-                    const account = user.accounts.find(
+                if (isUserBridgeKycApproved) {
+                    const account = user?.accounts?.find(
                         (account) =>
                             account.identifier.replaceAll(/\s/g, '').toLowerCase() ===
                             recipient.address.replaceAll(/\s/g, '').toLowerCase()
                     )
♻️ Duplicate comments (2)
src/hooks/useMantecaKycFlow.ts (2)

74-87: Clear iframe URL on close to avoid retaining tokens/PII.

Reset src when hiding the iframe.

-        (source?: 'manual' | 'completed' | 'tos_accepted') => {
-            setIframeOptions((prev) => ({ ...prev, visible: false }))
+        (source?: 'manual' | 'completed' | 'tos_accepted') => {
+            setIframeOptions({ src: '', visible: false, closeConfirmMessage: undefined })
             if (source === 'completed') {
                 onSuccess?.()
                 return
             }
             if (source === 'manual') {
                 onManualClose?.()
                 return
             }
             onClose?.()
         },

55-58: Don’t default unknown regions to AR; validate and normalize country id.

Defaulting to AR can onboard users to the wrong exchange. Normalize to uppercase and error out when unsupported.

-            const exchange = countryParam?.id
-                ? MantecaSupportedExchanges[countryParam.id as keyof typeof MantecaSupportedExchanges]
-                : MantecaSupportedExchanges.AR
+            const id = (countryParam?.id || country?.id)?.toUpperCase()
+            const exchange =
+                id && id in MantecaSupportedExchanges
+                    ? MantecaSupportedExchanges[id as keyof typeof MantecaSupportedExchanges]
+                    : undefined
+            if (!exchange) {
+                const msg = 'Unsupported region for Manteca KYC'
+                setError(msg)
+                return { success: false as const, error: msg }
+            }
🧹 Nitpick comments (17)
src/hooks/useSavedAccounts.tsx (2)

19-20: Tighten the predicate for readability.

Use includes to reduce repetition.

-                (acc) => acc.type === AccountType.IBAN || acc.type === AccountType.US || acc.type === AccountType.CLABE
+                (acc) => [AccountType.IBAN, AccountType.US, AccountType.CLABE].includes(acc.type)

16-23: Consider whether useMemo is needed.

If user.accounts is small or already stable, computing on render is fine and simpler. Otherwise, keep useMemo but depend on user?.accounts (as in the fix).

src/components/UserHeader/index.tsx (1)

69-79: Avoid duplicate Tooltip ids

Using a static id risks collisions when multiple labels render. Make it unique per user.

-            {badge && (
-                <Tooltip id="verified-user-label" content={tooltipContent} position="top">
+            {badge && (
+                <Tooltip id={`verified-user-label-${name}`} content={tooltipContent} position="top">
                     {badge}
                 </Tooltip>
             )}
src/components/AddMoney/consts/index.ts (2)

7-19: Tighten typing of MantecaSupportedExchanges

Mark as const to get literal keys/values and safer indexing downstream.

-export const MantecaSupportedExchanges = {
+export const MantecaSupportedExchanges = {
   AR: 'ARGENTINA',
   CL: 'CHILE',
   BR: 'BRAZIL',
   CO: 'COLOMBIA',
   PA: 'PANAMA',
   CR: 'COSTA_RICA',
   GT: 'GUATEMALA',
   // MX: 'MEXICO', // manteca supports MEXICO, but mercado pago doesnt support qr payments for mexico
   PH: 'PHILIPPINES',
   BO: 'BOLIVIA',
-}
+} as const

2594-2616: Normalize/robustify Manteca membership checks

Indexing the object directly can be brittle if countryCode ever isn’t uppercase. Prefer an uppercase hasOwnProperty check.

-            if (method.id === 'mercado-pago-add') {
-                return !!MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
+            if (method.id === 'mercado-pago-add') {
+                return Object.prototype.hasOwnProperty.call(
+                    MantecaSupportedExchanges,
+                    countryCode.toUpperCase()
+                )
             }
@@
-            } else if (
-                newMethod.id === 'mercado-pago-add' &&
-                MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
-            ) {
+            } else if (
+                newMethod.id === 'mercado-pago-add' &&
+                Object.prototype.hasOwnProperty.call(MantecaSupportedExchanges, countryCode.toUpperCase())
+            ) {
                 newMethod.isSoon = false
                 newMethod.path = `/add-money/${country.path}/mercadopago`
src/components/Common/CountryList.tsx (3)

113-116: Prefer iso2 fallback over lowercasing id for flags

Some entries have iso3 ids not covered by the map; prefer iso2 first.

-                            const twoLetterCountryCode =
-                                countryCodeMap[country.id.toUpperCase()] ?? country.id.toLowerCase()
+                            const twoLetterCountryCode = (
+                                country.iso2 ??
+                                countryCodeMap[country.id.toUpperCase()] ??
+                                country.id
+                            ).toLowerCase()

117-126: Normalize id before membership check

country.id is mixed ISO2/ISO3; normalize to uppercase to avoid future case issues.

-                            const isBridgeSupportedCountry = [
+                            const isBridgeSupportedCountry = [
                                 'US',
                                 'MX',
                                 ...Object.keys(countryCodeMap),
                                 ...Object.values(countryCodeMap),
-                            ].includes(country.id)
+                            ].includes(country.id.toUpperCase())

130-142: Comment mismatch with branch

Comments say “claim-request” in the add-withdraw branch and “support all countries” in claim-request. Update comments for clarity.

src/components/Profile/views/IdentityVerification.view.tsx (1)

126-159: Handle unsupported-country clicks explicitly

If a country is neither Bridge- nor Manteca-supported, clicks currently no-op. Consider surfacing a modal/toast to explain “Not supported yet”.

Would you like me to wire an “Unsupported region” ActionModal here?

src/components/Profile/views/ProfileEdit.view.tsx (1)

12-18: Use combined KYC flag for consistency

Elsewhere (Home) you show verified when either Bridge or Manteca is approved. Mirror that here.

-import useKycStatus from '@/hooks/useKycStatus'
+import useKycStatus from '@/hooks/useKycStatus'
@@
-    const { isUserBridgeKycApproved } = useKycStatus()
+    const { isUserKycApproved } = useKycStatus()
@@
-            <ProfileHeader name={fullName} username={username} isVerified={isUserBridgeKycApproved} />
+            <ProfileHeader name={fullName} username={username} isVerified={isUserKycApproved} />

Also applies to: 118-118

src/components/Kyc/KycVerificationInProgressModal.tsx (1)

52-59: Minor a11y + copy nit: hide decorative icon and add trailing period.

  • Mark the info icon as decorative (screen readers) and add a period to the sentence for consistency with other UI strings.
 export const PeanutDoesntStoreAnyPersonalInformation = ({ className }: { className?: string }) => {
   return (
-        <div className={twMerge('flex items-center gap-2 text-[11px] text-grey-1', className)}>
-            <Icon name="info" className="h-3 w-3" />
-            <span>Peanut doesn't store any personal information</span>
+        <div className={twMerge('flex items-center gap-2 text-[11px] text-grey-1', className)}>
+            <Icon name="info" className="h-3 w-3" aria-hidden="true" />
+            <span>Peanut doesn't store any personal information.</span>
         </div>
   )
 }
src/hooks/useDetermineBankClaimType.ts (1)

28-72: Optional: avoid state updates after unmount in async path.

Guard the async function with an isMounted flag to prevent setting state after unmount during fetches.

     useEffect(() => {
-        const determineBankClaimType = async () => {
+        let isMounted = true
+        const determineBankClaimType = async () => {
             // ...
-                if (senderKycApproved) {
+                if (!isMounted) return
+                if (senderKycApproved) {
                     setSenderDetails(senderDetails)
                     setClaimType(BankClaimType.GuestBankClaim)
                 } else {
                     // ...
                 }
             } catch (error) {
                 // ...
             }
         }
 
         determineBankClaimType()
-    }, [user, senderUserId, setSenderDetails, isUserBridgeKycApproved])
+        return () => {
+            isMounted = false
+        }
+    }, [user, senderUserId, setSenderDetails, isUserBridgeKycApproved])
src/components/Profile/index.tsx (1)

57-61: Remove redundant onClick; href already navigates.

href="/profile/identity-verification" is sufficient; the explicit router.push duplicates navigation.

-                        <ProfileMenuItem
+                        <ProfileMenuItem
                             icon="shield"
                             label="Identity Verification"
                             href="/profile/identity-verification"
-                            onClick={() => {
-                                router.push('/profile/identity-verification')
-                            }}
                             position="middle"
                         />
src/hooks/useKycStatus.tsx (1)

12-12: Add explicit return type for the hook.

Improves DX and guards future changes.

-export default function useKycStatus() {
+export default function useKycStatus(): {
+    isUserBridgeKycApproved: boolean
+    isUserMantecaKycApproved: boolean
+    isUserKycApproved: boolean
+} {
src/hooks/useMantecaKycFlow.ts (2)

33-41: Nit: fix variable name casing for readability.

Rename isuserActiveForSelectedGeoisUserActiveForSelectedGeo.

-            const isuserActiveForSelectedGeo = userKycVerifications.some(
+            const isUserActiveForSelectedGeo = userKycVerifications.some(
                 (v) =>
                     v.provider === 'MANTECA' &&
                     (v.mantecaGeo || '').toUpperCase() === selectedGeo.toUpperCase() &&
                     v.status === MantecaKycStatus.ACTIVE
             )
-            setNeedsMantecaKyc(!isuserActiveForSelectedGeo)
+            setNeedsMantecaKyc(!isUserActiveForSelectedGeo)

44-48: Comment accuracy: state isn’t “null.”

isMantecaKycRequired is boolean; adjust the comment to reflect actual behavior.

-        // if no verifications data available, keep as null (undetermined)
-        // only set to true if we have user data but no matching verification
+        // If no verifications data available yet, we don't assert required.
+        // We only set to true when we have user data but no matching verification.
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

34-39: Surface onboarding errors in the modal.

Expose error from the hook and show it to the user.

-    const { isLoading, iframeOptions, openMantecaKyc, handleIframeClose } = useMantecaKycFlow({
+    const { isLoading, error, iframeOptions, openMantecaKyc, handleIframeClose } = useMantecaKycFlow({
         onClose: onManualClose, // any non-success close from iframe is a manual close in case of Manteca KYC
         onSuccess: onKycSuccess,
         onManualClose,
         country,
     })
-                description={
-                    description ??
-                    'To continue, you need to complete identity verification. This usually takes just a few minutes.'
-                }
+                description={
+                    description ?? (
+                        <>
+                            <p>To continue, you need to complete identity verification. This usually takes just a few minutes.</p>
+                            {error && <p className="mt-2 text-xs text-red-500">{error}</p>}
+                        </>
+                    )
+                }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7e6c0f and 963b87d.

📒 Files selected for processing (17)
  • src/app/(mobile-ui)/home/page.tsx (3 hunks)
  • src/components/AddMoney/consts/index.ts (3 hunks)
  • src/components/Claim/Link/Initial.view.tsx (3 hunks)
  • src/components/Common/CountryList.tsx (3 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (1 hunks)
  • src/components/Kyc/KycVerificationInProgressModal.tsx (2 hunks)
  • src/components/Profile/components/ProfileHeader.tsx (2 hunks)
  • src/components/Profile/components/PublicProfile.tsx (3 hunks)
  • src/components/Profile/index.tsx (3 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (4 hunks)
  • src/components/Profile/views/ProfileEdit.view.tsx (2 hunks)
  • src/components/UserHeader/index.tsx (1 hunks)
  • src/hooks/useDetermineBankClaimType.ts (2 hunks)
  • src/hooks/useDetermineBankRequestType.ts (2 hunks)
  • src/hooks/useKycStatus.tsx (1 hunks)
  • src/hooks/useMantecaKycFlow.ts (1 hunks)
  • src/hooks/useSavedAccounts.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/Profile/views/ProfileEdit.view.tsx
  • src/hooks/useDetermineBankRequestType.ts
  • src/hooks/useDetermineBankClaimType.ts
  • src/components/Claim/Link/Initial.view.tsx
  • src/components/Profile/components/PublicProfile.tsx
  • src/app/(mobile-ui)/home/page.tsx
  • src/hooks/useMantecaKycFlow.ts
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-11T17:46:12.459Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.459Z
Learning: In Next.js applications, functions marked with "use server" are server actions that are safe to import in client components. Next.js generates proxy stubs for these functions, ensuring the actual implementation and any secrets (like process.env variables) remain on the server and are not bundled into the client JavaScript.

Applied to files:

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

Applied to files:

  • src/hooks/useSavedAccounts.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/Claim/Link/Initial.view.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/components/Claim/Link/Initial.view.tsx
  • src/hooks/useMantecaKycFlow.ts
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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)/home/page.tsx
📚 Learning: 2025-02-17T14:08:59.164Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#669
File: src/components/Global/Icon/index.tsx:216-216
Timestamp: 2025-02-17T14:08:59.164Z
Learning: Avoid using 'any' type in TypeScript code as it bypasses type checking. Use more specific types like string, union types, or keyof typeof for better type safety.

Applied to files:

  • src/hooks/useMantecaKycFlow.ts
📚 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/hooks/useMantecaKycFlow.ts
  • src/components/AddMoney/consts/index.ts
  • src/components/Common/CountryList.tsx
📚 Learning: 2025-06-22T16:10:53.167Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/components/Common/CountryList.tsx
🧬 Code graph analysis (14)
src/components/Profile/views/ProfileEdit.view.tsx (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/hooks/useKycStatus.tsx (1)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/components/Kyc/KycVerificationInProgressModal.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (195-204)
src/hooks/useDetermineBankRequestType.ts (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Profile/index.tsx (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/hooks/useDetermineBankClaimType.ts (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Claim/Link/Initial.view.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Profile/components/PublicProfile.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/app/(mobile-ui)/home/page.tsx (2)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/UserHeader/index.tsx (1)
  • UserHeader (18-32)
src/components/Profile/components/ProfileHeader.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/hooks/useMantecaKycFlow.ts (4)
src/components/AddMoney/consts/index.ts (2)
  • CountryData (153-162)
  • MantecaSupportedExchanges (8-19)
src/components/Global/IframeWrapper/index.tsx (1)
  • IFrameWrapperProps (9-14)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/services/manteca.ts (1)
  • mantecaApi (5-27)
src/components/Kyc/InitiateMantecaKYCModal.tsx (5)
src/components/AddMoney/consts/index.ts (1)
  • CountryData (153-162)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (16-98)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (64-124)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
src/components/Kyc/KycVerificationInProgressModal.tsx (1)
  • PeanutDoesntStoreAnyPersonalInformation (52-59)
src/components/Profile/views/IdentityVerification.view.tsx (6)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/AddMoney/consts/index.ts (2)
  • countryCodeMap (2470-2512)
  • MantecaSupportedExchanges (8-19)
src/components/Common/CountryList.tsx (1)
  • CountryList (34-191)
src/components/Kyc/KycVerificationInProgressModal.tsx (2)
  • PeanutDoesntStoreAnyPersonalInformation (52-59)
  • KycVerificationInProgressModal (13-50)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • MantecaGeoSpecificKycModal (72-123)
src/components/Common/CountryList.tsx (3)
src/components/AddMoney/consts/index.ts (4)
  • CountryData (153-162)
  • countryData (271-2439)
  • countryCodeMap (2470-2512)
  • MantecaSupportedExchanges (8-19)
src/hooks/useGeoLocaion.ts (1)
  • useGeoLocaion (8-34)
src/components/SearchUsers/SearchResultCard.tsx (1)
  • SearchResultCard (19-70)
🔇 Additional comments (9)
src/hooks/useSavedAccounts.tsx (1)

1-1: Client-only hook directive is correct.

Marking this hook as a client component is appropriate since it consumes a React context hook.

src/components/UserHeader/index.tsx (1)

49-63: LGTM: badge and tooltip logic reads cleanly

Single vs double-check and tooltip messaging are consistent with the new prop.

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

16-21: API extension looks good

New viewMode and getRightContent props are clear and backwards compatible.

src/hooks/useDetermineBankRequestType.ts (1)

26-31: Effect misses KYC status in deps

Exclude isUserBridgeKycApproved and the effect won’t react when the payer’s KYC flips to approved.

[建议重要重构 -> suggest_essential_refactor]

-    useEffect(() => {
+    useEffect(() => {
         const determineBankRequestType = async () => {
             const payerKycApproved = isUserBridgeKycApproved
             ...
         }
 
         determineBankRequestType()
-    }, [user, requesterUserId, setRequesterDetails])
+    }, [user, requesterUserId, setRequesterDetails, isUserBridgeKycApproved])

Also applies to: 69-71

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

45-45: LGTM: centralized KYC status usage

Using useKycStatus().isUserKycApproved for the header keeps Home aligned with multi-provider KYC.

Also applies to: 65-66, 219-220

src/components/Kyc/KycVerificationInProgressModal.tsx (1)

47-47: Good reuse: extracted footer message component.

Replacing inline JSX with a shared component improves consistency and reuse across KYC flows.

src/hooks/useDetermineBankClaimType.ts (1)

31-37: Confirm whether Manteca KYC should count as “approved” here.

If bank claims should proceed when either Bridge or Manteca KYC is approved, use isUserKycApproved; if it must be Bridge-only, keep as-is.

Would you like me to switch this to isUserKycApproved?

src/components/Claim/Link/Initial.view.tsx (1)

54-55: Verify intent: Bridge-only gating vs unified KYC approval.

Here you use isUserBridgeKycApproved to branch KYC steps. If Manteca KYC should also unlock later steps (for non-bank/IBAN flows it might), consider switching to isUserKycApproved. If IBAN/US strictly requires Bridge KYC, current check is correct.

I can send a patch using isUserKycApproved if that’s the desired behavior.

Also applies to: 115-116, 334-352

src/components/Profile/index.tsx (1)

41-41: LGTM: Verified state now derives from unified KYC status.

Passing isUserKycApproved keeps the UI consistent with the new hook.

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 (2)
src/hooks/useDetermineBankClaimType.ts (1)

28-36: Effect is missing isUserBridgeKycApproved in its dependency array.

Without it, updates to receiver KYC approval won’t recompute claimType.

-    }, [user, senderUserId, setSenderDetails])
+    }, [user, senderUserId, setSenderDetails, isUserBridgeKycApproved])

Also applies to: 74-76

src/hooks/useDetermineBankRequestType.ts (1)

28-36: Add isUserBridgeKycApproved to the effect dependencies.

Prevents stale requestType when payer KYC status changes.

-    }, [user, requesterUserId, setRequesterDetails])
+    }, [user, requesterUserId, setRequesterDetails, isUserBridgeKycApproved])

Also applies to: 69-71

🧹 Nitpick comments (18)
src/hooks/useSavedAccounts.tsx (2)

16-22: Simplify the filter predicate (readability + tiny perf win)

Replace the long OR-chain with a Set/includes for clarity.

Apply:

-    const savedAccounts = useMemo(() => {
-        return (
-            user?.accounts.filter(
-                (acc) => acc.type === AccountType.IBAN || acc.type === AccountType.US || acc.type === AccountType.CLABE
-            ) ?? []
-        )
-    }, [user])
+    const savedAccounts = useMemo(() => {
+        const allowed = new Set([AccountType.IBAN, AccountType.US, AccountType.CLABE])
+        return user?.accounts?.filter((acc) => allowed.has(acc.type)) ?? []
+    }, [user])

12-25: Optional: tighten memo deps and add explicit return type

  • Dependency array: if only accounts affect the result, narrow the dependency to avoid needless recalcs when other user fields change.
  • Return type: consider annotating the hook’s return to your concrete account shape for stronger TS guarantees.

Example (adjust types/imports to your codebase):

 import { useMemo } from 'react'
 
 export default function useSavedAccounts() {
     const { user } = useAuth()
+    const accounts = user?.accounts
 
-    const savedAccounts = useMemo(() => {
+    const savedAccounts = useMemo(() => {
-        return (
-            user?.accounts.filter(
+        return (
+            accounts?.filter(
                 (acc) => acc.type === AccountType.IBAN || acc.type === AccountType.US || acc.type === AccountType.CLABE
             ) ?? []
         )
-    }, [user])
+    }, [accounts])
 
     return savedAccounts
 }

If available, also do:

// e.g., import type { BankAccount } from '@/interfaces'
export default function useSavedAccounts(): BankAccount[] { /* ... */ }
src/components/UserHeader/index.tsx (3)

75-77: Make Tooltip id unique to avoid collisions on pages with multiple labels.

-                <Tooltip id="verified-user-label" content={tooltipContent} position="top">
+                <Tooltip id={tooltipId} content={tooltipContent} position="top">

Support code outside this range:

// add to imports
import { useMemo, useId } from 'react';

// inside VerifiedUserLabel, before return
const tooltipId = useId();

55-56: a11y: add accessible names to verification icons.

-        badge = <Icon name="check" size={iconSize} className="text-success-1" />
+        badge = <Icon name="check" size={iconSize} className="text-success-1" aria-label="Verified user" />

-        badge = <Icon name="double-check" size={iconSize} className="text-success-1" />
+        badge = <Icon name="double-check" size={iconSize} className="text-success-1" aria-label="Verified user (you have sent them money)" />

Also applies to: 61-63


56-63: Localize user-facing strings.

Hard-coded tooltip strings should use the app’s i18n utility for consistency and future translations.

src/components/Kyc/KycVerificationInProgressModal.tsx (1)

52-59: Minor a11y/copy polish for the footer component.

Mark the info icon as decorative and add a terminal period.

 export const PeanutDoesntStoreAnyPersonalInformation = ({ className }: { className?: string }) => {
   return (
-        <div className={twMerge('flex items-center gap-2 text-[11px] text-grey-1', className)}>
-            <Icon name="info" className="h-3 w-3" />
-            <span>Peanut doesn't store any personal information</span>
+        <div className={twMerge('flex items-center gap-2 text-[11px] text-grey-1', className)}>
+            <Icon name="info" className="h-3 w-3" aria-hidden="true" focusable="false" />
+            <span>Peanut doesn't store any personal information.</span>
         </div>
   )
 }
src/components/AddMoney/consts/index.ts (2)

7-19: Type-safety nit: freeze the exchange map.

Declare as const to preserve literal types for keys/values and catch typos at compile time.

-export const MantecaSupportedExchanges = {
+export const MantecaSupportedExchanges = {
   AR: 'ARGENTINA',
   CL: 'CHILE',
   BR: 'BRAZIL',
   CO: 'COLOMBIA',
   PA: 'PANAMA',
   CR: 'COSTA_RICA',
   GT: 'GUATEMALA',
   // MX: 'MEXICO', // manteca supports MEXICO, but mercado pago doesnt support qr payments for mexico
   PH: 'PHILIPPINES',
   BO: 'BOLIVIA',
-}
+} as const

2593-2616: Small cleanup: unreachable branch after filtering Mercado Pago.

You filter out Mercado Pago for unsupported countries; the later else-case setting isSoon back to true is redundant.

-        const currentAddMethods = UPDATED_DEFAULT_ADD_MONEY_METHODS.filter((method) => {
+        const currentAddMethods = UPDATED_DEFAULT_ADD_MONEY_METHODS.filter((method) => {
             if (method.id === 'mercado-pago-add') {
                 return !!MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
             }
             return true
         }).map((m) => {
             const newMethod = { ...m }
             if (newMethod.id === 'bank-transfer-add') {
                 newMethod.path = `/add-money/${country.path}/bank`
                 newMethod.isSoon = !isCountryEnabledForBankTransfer(countryCode) || countryCode === 'MX'
             } else if (newMethod.id === 'crypto-add') {
                 newMethod.path = `/add-money/crypto`
                 newMethod.isSoon = false
-            } else if (
-                newMethod.id === 'mercado-pago-add' &&
-                MantecaSupportedExchanges[countryCode as keyof typeof MantecaSupportedExchanges]
-            ) {
+            } else if (newMethod.id === 'mercado-pago-add') {
                 newMethod.isSoon = false
                 newMethod.path = `/add-money/${country.path}/mercadopago`
-            } else if (newMethod.id === 'mercado-pago-add') {
-                newMethod.isSoon = true
             }
             return newMethod
         })
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)

85-93: Copy nit: avoid over-specific region claims.

Bridge approval doesn’t necessarily imply “Europe, USA, and Mexico.” Consider neutral phrasing to avoid user confusion.

-                        You're already verified in Europe, USA, and Mexico, but to use features in{' '}
+                        You're already verified with our primary provider, but to use features in{' '}
                         {selectedCountry.title} you need to complete a separate verification. <br /> Since{' '}
                         <b>we don't keep personal data</b>, your previous KYC can't be reused.
src/components/Profile/components/PublicProfile.tsx (1)

47-57: Effect deps: remove hook dependency or add it

If you keep using any hook state inside this effect, add it to the deps; otherwise, after applying the fix above, remove the unused dependency entirely. Right now it risks stale reads.

src/components/Profile/views/ProfileEdit.view.tsx (1)

17-18: Unify verification with combined KYC (bridge OR Manteca)

For consistency with Profile/index.tsx and the new hook, use isUserKycApproved instead of bridge-only.

-    const { isUserBridgeKycApproved } = useKycStatus()
+    const { isUserKycApproved } = useKycStatus()
...
-            <ProfileHeader name={fullName} username={username} isVerified={isUserBridgeKycApproved} />
+            <ProfileHeader name={fullName} username={username} isVerified={isUserKycApproved} />

Also applies to: 118-118

src/components/Profile/components/ProfileHeader.tsx (1)

33-35: Use combined KYC for “authenticated user verified” state

Ensure “You’re verified” messaging covers both Bridge and Manteca approvals.

-    const { isUserBridgeKycApproved } = useKycStatus()
-    const isAuthenticatedUserVerified = isUserBridgeKycApproved && authenticatedUser?.user.username === username
+    const { isUserKycApproved } = useKycStatus()
+    const isAuthenticatedUserVerified = isUserKycApproved && authenticatedUser?.user.username === username
src/components/Profile/index.tsx (1)

58-60: Redundant navigation handler

href already routes to /profile/identity-verification; the extra router.push in onClick is unnecessary and could double-trigger.

-                        <ProfileMenuItem
+                        <ProfileMenuItem
                             icon="shield"
                             label="Identity Verification"
                             href="/profile/identity-verification"
-                            onClick={() => {
-                                router.push('/profile/identity-verification')
-                            }}
                             position="middle"
                         />
src/components/Common/CountryList.tsx (1)

130-142: Comment fixes and intent clarity

The comments don’t match the logic. Also make it explicit claim-request is bridge-only.

-                            if (viewMode === 'add-withdraw') {
-                                // all countries supported for claim-request
+                            if (viewMode === 'add-withdraw') {
+                                // all countries supported for add/withdraw
                                 isSupported = true
                             } else if (viewMode === 'general-verification') {
                                 // support all bridge and manteca supported countries
                                 isSupported = isBridgeSupportedCountry || isMantecaSupportedCountry
                             } else if (viewMode === 'claim-request') {
-                                // support all countries
+                                // claim/request only in bridge-supported countries
                                 isSupported = isBridgeSupportedCountry
                             } else {
-                                // support all countries
+                                // default: support all countries
                                 isSupported = true
                             }
src/components/Profile/views/IdentityVerification.view.tsx (4)

109-124: Fix stale-closure risk in isVerifiedForCountry dependencies.

The callback uses isUserBridgeKycApproved but doesn't list it in deps; instead it lists user?.user.bridgeKycStatus (not directly used). Add the actual variable to avoid stale reads.

-    const isVerifiedForCountry = useCallback(
+    const isVerifiedForCountry = useCallback(
         (code: string) => {
             const upper = code.toUpperCase()
             // bridge approval covers us/mx/sepa generally
             if (isBridgeSupportedCountry(upper) && isUserBridgeKycApproved) return true
             // manteca per-geo check
             const mantecaActive = user?.user.kycVerifications?.some(
                 (v) =>
                     v.provider === 'MANTECA' &&
                     (v.mantecaGeo || '').toUpperCase() === upper &&
                     v.status === MantecaKycStatus.ACTIVE
             )
             return Boolean(mantecaActive)
         },
-        [isBridgeSupportedCountry, user?.user.bridgeKycStatus, user?.user.kycVerifications]
+        [isBridgeSupportedCountry, isUserBridgeKycApproved, user?.user.kycVerifications]
     )

137-155: Centralize country-gating logic to a shared util to prevent drift.

The click-path gating here duplicates logic inside CountryList (bridge vs. Manteca support). Extract helpers like normalizeCountryCode, isBridgeSupportedCountry, and isMantecaSupportedCountry into a shared module (e.g., src/lib/countrySupport.ts) and reuse in both places.

I can draft the shared util and local replacements if you want it in this PR.


45-46: Consider handling KYC success via hook option to refresh user and set “just completed” flag.

Per prior learning, triggering “just completed KYC” before fetchUser() can cause loops. If this view needs post-approval UX, pass onKycSuccess to useBridgeKycFlow and refresh user there; only then flip any “just completed” UI state.

Minimal change (safe even without a flag):

-} = useBridgeKycFlow()
+} = useBridgeKycFlow({
+    onKycSuccess: async () => {
+        await fetchUser()
+        // if applicable in this view: setJustCompletedKyc(true) AFTER fetchUser()
+    },
+})

Please confirm whether this view requires the “just completed” behavior; if so, tell me where the setJustCompletedKyc setter lives and I’ll wire it correctly after await fetchUser().


61-83: Surface handleInitiateKyc outcome in UI state (optional).

You rely on the hook’s kycError for failures, which is fine. Optionally, check the return of handleInitiateKyc() and early-return if it fails to avoid showing the progress modal in edge cases.

- await handleInitiateKyc()
+ const started = await handleInitiateKyc()
+ if (!started?.success) return {}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7e6c0f and 823ec91.

📒 Files selected for processing (18)
  • src/app/(mobile-ui)/home/page.tsx (3 hunks)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (3 hunks)
  • src/components/AddMoney/consts/index.ts (3 hunks)
  • src/components/Claim/Link/Initial.view.tsx (3 hunks)
  • src/components/Common/CountryList.tsx (3 hunks)
  • src/components/Kyc/InitiateMantecaKYCModal.tsx (1 hunks)
  • src/components/Kyc/KycVerificationInProgressModal.tsx (2 hunks)
  • src/components/Profile/components/ProfileHeader.tsx (2 hunks)
  • src/components/Profile/components/PublicProfile.tsx (3 hunks)
  • src/components/Profile/index.tsx (3 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (4 hunks)
  • src/components/Profile/views/ProfileEdit.view.tsx (2 hunks)
  • src/components/UserHeader/index.tsx (1 hunks)
  • src/hooks/useDetermineBankClaimType.ts (2 hunks)
  • src/hooks/useDetermineBankRequestType.ts (2 hunks)
  • src/hooks/useKycStatus.tsx (1 hunks)
  • src/hooks/useMantecaKycFlow.ts (1 hunks)
  • src/hooks/useSavedAccounts.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/hooks/useMantecaKycFlow.ts
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/hooks/useKycStatus.tsx
  • src/components/Claim/Link/Initial.view.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/Profile/components/PublicProfile.tsx
  • src/components/Claim/Link/Initial.view.tsx
  • src/components/Profile/views/ProfileEdit.view.tsx
  • src/app/(mobile-ui)/home/page.tsx
  • src/hooks/useDetermineBankClaimType.ts
  • src/hooks/useDetermineBankRequestType.ts
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/Claim/Link/Initial.view.tsx
📚 Learning: 2025-05-13T10:05:24.057Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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)/home/page.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/components/Common/CountryList.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/components/Common/CountryList.tsx
  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-06-22T16:10:53.167Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/components/Profile/views/IdentityVerification.view.tsx
🧬 Code graph analysis (13)
src/hooks/useKycStatus.tsx (1)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/components/Profile/components/PublicProfile.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Claim/Link/Initial.view.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Profile/views/ProfileEdit.view.tsx (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Kyc/KycVerificationInProgressModal.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (195-204)
src/components/Profile/index.tsx (2)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/app/(mobile-ui)/home/page.tsx (2)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/UserHeader/index.tsx (1)
  • UserHeader (18-32)
src/components/Common/CountryList.tsx (3)
src/components/AddMoney/consts/index.ts (3)
  • CountryData (153-162)
  • countryCodeMap (2470-2512)
  • MantecaSupportedExchanges (8-19)
src/hooks/useGeoLocaion.ts (1)
  • useGeoLocaion (8-34)
src/components/SearchUsers/SearchResultCard.tsx (1)
  • SearchResultCard (19-70)
src/hooks/useDetermineBankClaimType.ts (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/hooks/useDetermineBankRequestType.ts (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/Kyc/InitiateMantecaKYCModal.tsx (5)
src/components/AddMoney/consts/index.ts (1)
  • CountryData (153-162)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (16-98)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (64-124)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
src/components/Kyc/KycVerificationInProgressModal.tsx (1)
  • PeanutDoesntStoreAnyPersonalInformation (52-59)
src/components/Profile/views/IdentityVerification.view.tsx (7)
src/hooks/useBridgeKycFlow.ts (1)
  • useBridgeKycFlow (38-188)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
src/components/AddMoney/consts/index.ts (2)
  • countryCodeMap (2470-2512)
  • MantecaSupportedExchanges (8-19)
src/components/Common/CountryList.tsx (1)
  • CountryList (34-191)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (195-204)
src/components/Kyc/KycVerificationInProgressModal.tsx (2)
  • PeanutDoesntStoreAnyPersonalInformation (52-59)
  • KycVerificationInProgressModal (13-50)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • MantecaGeoSpecificKycModal (72-123)
src/components/Profile/components/ProfileHeader.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
🔇 Additional comments (16)
src/hooks/useSavedAccounts.tsx (1)

1-1: Client directive is appropriate — verify no server-only imports

Sandbox repo search failed; unable to confirm. Run a local repo-wide search for useSavedAccounts and ensure this 'use client' hook is not imported by server-only files (app//route.(ts|tsx|js), app/api/, pages/api/, or any .server. components). Suggested local command:
rg -n --hidden -S '\buseSavedAccounts\b' --glob '!.git/
' --glob '!node_modules/**'

src/components/Kyc/KycVerificationInProgressModal.tsx (1)

47-48: LGTM on using the reusable footer.

Swapping the inline copy for the shared footer keeps the modal consistent across KYC flows.

src/hooks/useKycStatus.tsx (1)

15-25: Hook logic reads clean and matches the intended cross-provider semantics.

Bridge “approved” OR Manteca “ACTIVE” → approved; returned flags are clear.

src/app/(mobile-ui)/home/page.tsx (2)

44-45: Good reuse of the new hook.

Centralizing KYC state via useKycStatus reduces duplication.


65-66: LGTM on switching to derived isUserKycApproved.

This ensures verification reflects any supported provider.

src/components/Claim/Link/Initial.view.tsx (3)

54-55: LGTM on importing the shared KYC status hook.

Keeps IBAN flow logic aligned with global KYC state.


115-116: Using bridge-only approval here is appropriate.

IBAN/SEPA routing depends on Bridge KYC; keeping it provider-specific is correct.


334-352: KYC step gating reads correctly.

Receiver approved → jump to 3/4; otherwise 0/1 based on profile completeness.

src/hooks/useDetermineBankClaimType.ts (1)

26-36: Use the shared hook for receiver-approval: good.

Removes duplication and keeps semantics consistent.

src/hooks/useDetermineBankRequestType.ts (1)

26-34: Consistent use of the shared KYC hook.

Keeps payer-approval logic in sync with other flows.

src/components/Kyc/InitiateMantecaKYCModal.tsx (2)

41-66: Modal wiring looks solid.

Loading, CTA, iframe open, and close-handling are coherent; props are sensibly overridable.


100-112: Good contextual footer choice.

“Not now” for already-verified users and the privacy note otherwise reads well.

src/components/Profile/index.tsx (1)

41-41: LGTM: Verified badge sourced from combined KYC

Passing isUserKycApproved into ProfileHeader aligns with the new hook.

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

103-104: Guard: ensure flow is defined when used

onCryptoClick(flow!) assumes flow is set for add-withdraw mode. Please verify all call sites pass flow, or add a runtime guard.

You can enforce at the type level by making flow required when viewMode === 'add-withdraw', or add:

onClick={() => flow && onCryptoClick?.(flow)}
src/components/Profile/views/IdentityVerification.view.tsx (2)

203-239: Overall: solid UX split between Bridge and Manteca, clean state management.

The country-first routing, verified badges, and progress modal integration read well. Nice work.


133-136: Verify Icon name availability ("check").

Found exported CheckIcon at src/components/Global/Icons/check.tsx, but no icon-name registry (e.g. iconComponents map) mapping the string "check" was found — may render nothing. Either render directly or register "check" → CheckIcon in the Icon registry.

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

♻️ Duplicate comments (2)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2)

1-1: Resolved: "use client" directive added.

Addresses the earlier bot’s note. Good.


95-101: Make modal state follow the requirement and remove unused dep.

Closes automatically when requirement flips to false and avoids unnecessary dependency.

-// handle verification modal opening
-useEffect(() => {
-    if (isMantecaKycRequired) {
-        setIsKycModalOpen(true)
-    }
-}, [isMantecaKycRequired, countryData])
+// sync KYC modal with requirement
+useEffect(() => {
+    setIsKycModalOpen(Boolean(isMantecaKycRequired))
+}, [isMantecaKycRequired])
🧹 Nitpick comments (3)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (3)

32-35: Handle catch-all route params (string | string[]).

Prevents subtle mismatches when the route uses [...country].

-const selectedCountryPath = params.country as string
-const selectedCountry = useMemo(() => {
-    return countryData.find((country) => country.type === 'country' && country.path === selectedCountryPath)
-}, [selectedCountryPath])
+const rawCountryParam = params.country
+const selectedCountryPath =
+    Array.isArray(rawCountryParam) ? rawCountryParam.join('/') : (rawCountryParam as string)
+const selectedCountry = useMemo(() => {
+    return countryData.find((country) => country.type === 'country' && country.path === selectedCountryPath)
+}, [selectedCountryPath])

107-115: Avoid a no-op click while KYC status is unknown.

Disable the CTA by surfacing a loading state until KYC requirement resolves.

-                    isLoading={isCreatingDeposit}
+                    isLoading={isCreatingDeposit || isMantecaKycRequired === null}

121-125: Potential double fetchUser (WS + success callback).

Harmless but redundant. Consider relying on WS or debounce/dedupe to trim extra calls.

-                            setIsKycModalOpen(false)
-                            fetchUser()
+                            setIsKycModalOpen(false)
+                            // Optional: rely on WS update to refetch user to avoid duplicates.
+                            // fetchUser()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 963b87d and 823ec91.

📒 Files selected for processing (1)
  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 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/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2025-09-11T17:46:12.459Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.459Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.

Applied to files:

  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.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/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2025-09-11T17:46:12.459Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.459Z
Learning: In Next.js applications, functions marked with "use server" are server actions that are safe to import in client components. Next.js generates proxy stubs for these functions, ensuring the actual implementation and any secrets (like process.env variables) remain on the server and are not bundled into the client JavaScript.

Applied to files:

  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2024-10-18T08:54:22.142Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#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/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#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/RegionalMethods/MercadoPago/index.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx
🧬 Code graph analysis (1)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (7)
src/types/manteca.types.ts (1)
  • MantecaDepositDetails (1-5)
src/components/AddMoney/consts/index.ts (2)
  • countryData (271-2439)
  • CountryData (153-162)
src/hooks/useMantecaKycFlow.ts (1)
  • useMantecaKycFlow (16-98)
src/hooks/useCurrency.ts (1)
  • useCurrency (11-55)
src/context/authContext.tsx (1)
  • useAuth (182-188)
src/hooks/useWebSocket.ts (1)
  • useWebSocket (19-186)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • InitiateMantecaKYCModal (23-70)
🔇 Additional comments (2)
src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx (2)

40-50: Confirm backend WS status literals and align types

MantecaKycStatus enum contains 'ACTIVE' (src/interfaces/interfaces.ts:224–228) but not 'WIDGET_FINISHED'; MercadoPago checks for 'WIDGET_FINISHED' (src/components/AddMoney/components/RegionalMethods/MercadoPago/index.tsx:43–46). Verify the backend actually emits 'WIDGET_FINISHED' — if yes, add it to MantecaKycStatus or centralize/replace the string literal; if not, remove or map that check.


5-5: Confirm createMantecaOnramp is a Server Action ("use server").

export async function createMantecaOnramp is defined in src/app/actions/onramp.ts (≈lines 122–124); verification couldn't confirm a top-level 'use server' directive (file print failed). Ensure the module begins with "use server" so secrets aren't bundled client-side.

Copy link
Contributor

@jjramirezn jjramirezn left a comment

Choose a reason for hiding this comment

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

Looking good, but seems to be missing the part where we ask bridge for the country from the user and do that to allow user to use mercadopago while not having manteca KYC

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

♻️ Duplicate comments (2)
src/components/UserHeader/index.tsx (2)

11-11: LGTM: Correct import for viewer KYC status.

The default export path aligns with useKycStatus.tsx and matches the intended usage.


30-34: Fix tooltip perspective by passing viewer’s verification (matches prior guidance).

This resolves the “You’re a verified user” copy showing on others’ profiles.

🧹 Nitpick comments (1)
src/components/UserHeader/index.tsx (1)

82-84: Avoid duplicate Tooltip IDs across a list.

Static id risks collisions when multiple labels render. Use a unique id per instance.

-                <Tooltip id="verified-user-label" content={tooltipContent} position="top">
+                <Tooltip id={`verified-user-label-${name}`} content={tooltipContent} position="top">
                   {badge}
                 </Tooltip>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 823ec91 and b6e0026.

📒 Files selected for processing (4)
  • src/components/Profile/components/ProfileHeader.tsx (2 hunks)
  • src/components/Profile/components/PublicProfile.tsx (2 hunks)
  • src/components/UserHeader/index.tsx (3 hunks)
  • src/services/users.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/Profile/components/PublicProfile.tsx
  • src/components/Profile/components/ProfileHeader.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/services/users.ts (1)
src/interfaces/interfaces.ts (1)
  • IUserKycVerification (230-238)
src/components/UserHeader/index.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-26)
🔇 Additional comments (4)
src/components/UserHeader/index.tsx (2)

20-21: Correctly deriving viewer verification state.

Alias is clear and avoids shadowing; hook usage is appropriate here.


24-25: Confirm profile link target.

Linking to “/profile” suggests the viewer’s profile; if this header often shows another user, consider “/profile/{username}”.

src/services/users.ts (2)

8-8: Import addition approved — barrel export confirmed. IUserKycVerification is defined in src/interfaces/interfaces.ts and re‑exported by src/interfaces/index.ts (export * from './interfaces'); importing from '@/interfaces' is correct and there's a single source of truth.


31-31: Don't make kycVerifications non‑optional yet — normalize at the API boundary or keep it optional

rg shows many callsites using optional chaining and src/interfaces/interfaces.ts explicitly notes "bridge is not migrated", so changing the type now would require normalizing every user read.

  • Option A (safer): keep kycVerifications?: IUserKycVerification[] and keep existing guards.
  • Option B: make kycVerifications: IUserKycVerification[] and normalize responses from PEANUT at all read points (set to [] when missing). Minimum places to update: src/services/users.ts (getByUsername, search), src/app/actions/users.ts (getUserById), src/app/api/peanut/user/get-user-from-cookie/route.ts — also update any other proxies/servers that return user objects.
  • Confirm backend guarantees camelCase kycVerifications for all users (bridge-not-migrated comment indicates it may not).

@kushagrasarathe kushagrasarathe changed the base branch from feat/manteca-add-flow to feat/manteca-integration September 16, 2025 07:29
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