From 820902b2afd77a7d8a430e3e7a1595180d9affef Mon Sep 17 00:00:00 2001
From: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com>
Date: Thu, 14 Aug 2025 22:41:36 +0530
Subject: [PATCH 01/13] HOTFIX - IBAN country detection and incorrect bank acc
details (#1094)
* Fix: Iban country detection and incorrect bank acc details
* Fix: update IBAN country validation to use correct locale string comparison
* add validations for US and mexican bank accounts
* fix typo
* fix claim flow and create a reusable function for getting 3 letter code
* fix country code mismatch
* fix: show error below input field
* remove unnecessary checks
* remove unnecessary CLABE check
---
.../AddWithdraw/AddWithdrawCountriesList.tsx | 12 +-
.../AddWithdraw/DynamicBankAccountForm.tsx | 35 +++--
.../Claim/Link/views/BankFlowManager.view.tsx | 4 +-
src/utils/withdraw.utils.ts | 147 ++++++++++++++++++
4 files changed, 184 insertions(+), 14 deletions(-)
create mode 100644 src/utils/withdraw.utils.ts
diff --git a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
index 24ce2f325..0d0ad6365 100644
--- a/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
+++ b/src/components/AddWithdraw/AddWithdrawCountriesList.tsx
@@ -1,6 +1,11 @@
'use client'
-import { COUNTRY_SPECIFIC_METHODS, countryData, SpecificPaymentMethod } from '@/components/AddMoney/consts'
+import {
+ COUNTRY_SPECIFIC_METHODS,
+ countryCodeMap,
+ countryData,
+ SpecificPaymentMethod,
+} from '@/components/AddMoney/consts'
import StatusBadge from '@/components/Global/Badges/StatusBadge'
import { IconName } from '@/components/Global/Icons/Icon'
import NavHeader from '@/components/Global/NavHeader'
@@ -11,7 +16,7 @@ import Image, { StaticImageData } from 'next/image'
import { useParams, useRouter } from 'next/navigation'
import EmptyState from '../Global/EmptyStates/EmptyState'
import { useAuth } from '@/context/authContext'
-import { useEffect, useRef, useState } from 'react'
+import { useEffect, useMemo, useRef, useState } from 'react'
import { InitiateKYCModal } from '@/components/Kyc'
import { DynamicBankAccountForm, IBankAccountDetails } from './DynamicBankAccountForm'
import { addBankAccount, updateUserById } from '@/app/actions/users'
@@ -23,6 +28,7 @@ import { useWithdrawFlow } from '@/context/WithdrawFlowContext'
import { useOnrampFlow } from '@/context/OnrampFlowContext'
import { Account } from '@/interfaces'
import PeanutLoading from '../Global/PeanutLoading'
+import { getCountryCodeForWithdraw } from '@/utils/withdraw.utils'
interface AddWithdrawCountriesListProps {
flow: 'add' | 'withdraw'
@@ -212,7 +218,7 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
diff --git a/src/components/AddWithdraw/DynamicBankAccountForm.tsx b/src/components/AddWithdraw/DynamicBankAccountForm.tsx
index 5cb050530..038ee3bed 100644
--- a/src/components/AddWithdraw/DynamicBankAccountForm.tsx
+++ b/src/components/AddWithdraw/DynamicBankAccountForm.tsx
@@ -13,6 +13,7 @@ import { getBicFromIban } from '@/app/actions/ibanToBic'
import PeanutActionDetailsCard, { PeanutActionDetailsCardProps } from '../Global/PeanutActionDetailsCard'
import { PEANUT_WALLET_TOKEN_SYMBOL } from '@/constants'
import { useWithdrawFlow } from '@/context/WithdrawFlowContext'
+import { getCountryFromIban, validateMXCLabeAccount, validateUSBankAccount } from '@/utils/withdraw.utils'
const isIBANCountry = (country: string) => {
return countryCodeMap[country.toUpperCase()] !== undefined
@@ -41,19 +42,22 @@ interface DynamicBankAccountFormProps {
initialData?: Partial
flow?: 'claim' | 'withdraw'
actionDetailsProps?: Partial
+ countryName?: string
}
export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, DynamicBankAccountFormProps>(
- ({ country, onSuccess, initialData, flow = 'withdraw', actionDetailsProps }, ref) => {
+ ({ country, countryName, onSuccess, initialData, flow = 'withdraw', actionDetailsProps }, ref) => {
const { user } = useAuth()
const [isSubmitting, setIsSubmitting] = useState(false)
const [submissionError, setSubmissionError] = useState(null)
const [showBicField, setShowBicField] = useState(false)
- const { country: countryName } = useParams()
+ const { country: countryNameParams } = useParams()
const { amountToWithdraw } = useWithdrawFlow()
const [firstName, ...lastNameParts] = (user?.user.fullName ?? '').split(' ')
const lastName = lastNameParts.join(' ')
+ let selectedCountry = (countryName ?? (countryNameParams as string)).toLowerCase()
+
const {
control,
handleSubmit,
@@ -85,9 +89,9 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
setIsSubmitting(true)
setSubmissionError(null)
try {
- const isIban = isIBANCountry(country)
- const isUs = country.toUpperCase() === 'US'
+ const isUs = country.toUpperCase() === 'USA'
const isMx = country.toUpperCase() === 'MX'
+ const isIban = isUs || isMx ? false : isIBANCountry(country)
let accountType: BridgeAccountType
if (isIban) accountType = BridgeAccountType.IBAN
@@ -121,7 +125,7 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
accountType,
accountNumber: accountNumber.replace(/\s/g, ''),
countryCode: isUs ? 'USA' : country.toUpperCase(),
- countryName: countryName as string,
+ countryName: selectedCountry,
accountOwnerType: BridgeAccountOwnerType.INDIVIDUAL,
accountOwnerName: {
firstName: firstName.trim(),
@@ -159,9 +163,9 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
}
}
- const isIban = isIBANCountry(country)
- const isUs = country.toUpperCase() === 'US'
const isMx = country.toUpperCase() === 'MX'
+ const isUs = country.toUpperCase() === 'USA'
+ const isIban = isUs || isMx ? false : isIBANCountry(country)
const renderInput = (
name: keyof IBankAccountDetails,
@@ -246,14 +250,25 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
required: 'CLABE is required',
minLength: { value: 18, message: 'CLABE must be 18 digits' },
maxLength: { value: 18, message: 'CLABE must be 18 digits' },
+ validate: async (value: string) =>
+ validateMXCLabeAccount(value).isValid || 'Invalid CLABE',
})
: isIban
? renderInput(
'accountNumber',
'IBAN',
{
- required: 'Account number is required',
- validate: async (val: string) => (await validateIban(val)) || 'Invalid IBAN',
+ required: 'IBAN is required',
+ validate: async (val: string) => {
+ const isValidIban = await validateIban(val)
+ if (!isValidIban) return 'Invalid IBAN'
+
+ if (getCountryFromIban(val)?.toLowerCase() !== selectedCountry) {
+ return 'IBAN does not match the selected country'
+ }
+
+ return true
+ },
},
'text',
undefined,
@@ -267,7 +282,7 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
{
required: 'Account number is required',
validate: async (value: string) =>
- (await validateBankAccount(value)) || 'Invalid account number',
+ validateUSBankAccount(value).isValid || 'Invalid account number',
},
'text'
)}
diff --git a/src/components/Claim/Link/views/BankFlowManager.view.tsx b/src/components/Claim/Link/views/BankFlowManager.view.tsx
index 7d98e6f3b..60afba58b 100644
--- a/src/components/Claim/Link/views/BankFlowManager.view.tsx
+++ b/src/components/Claim/Link/views/BankFlowManager.view.tsx
@@ -20,6 +20,7 @@ import { getBridgeChainName, getBridgeTokenName } from '@/utils/bridge-accounts.
import peanut from '@squirrel-labs/peanut-sdk'
import { getUserById } from '@/app/actions/users'
import NavHeader from '@/components/Global/NavHeader'
+import { getCountryCodeForWithdraw } from '@/utils/withdraw.utils'
export const BankFlowManager = (props: IClaimScreenProps) => {
const { onCustom, claimLinkData, setTransactionHash } = props
@@ -214,7 +215,8 @@ export const BankFlowManager = (props: IClaimScreenProps) => {