Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { useCreateOnramp } from '@/hooks/useCreateOnramp'
import { useRouter, useParams } from 'next/navigation'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { formatUnits } from 'viem'
import Icon from '@/components/Global/Icon'
import PeanutLoading from '@/components/Global/PeanutLoading'
import EmptyState from '@/components/Global/EmptyStates/EmptyState'
import { UserDetailsForm, type UserDetailsFormData } from '@/components/AddMoney/UserDetailsForm'
Expand All @@ -25,6 +24,7 @@ import AddMoneyBankDetails from '@/components/AddMoney/components/AddMoneyBankDe
import { getCurrencyConfig, getCurrencySymbol, getMinimumAmount } from '@/utils/bridge.utils'
import { OnrampConfirmationModal } from '@/components/AddMoney/components/OnrampConfirmationModal'
import { InitiateBridgeKYCModal } from '@/components/Kyc/InitiateBridgeKYCModal'
import InfoCard from '@/components/Global/InfoCard'

type AddStep = 'inputAmount' | 'kyc' | 'loading' | 'collectUserDetails' | 'showDetails'

Expand Down Expand Up @@ -342,10 +342,12 @@ export default function OnrampBankPage() {
}
hideBalance={true}
/>
<div className="flex items-center gap-2 text-xs text-grey-1">
<Icon name="info" width={16} height={16} />
<span>This must exactly match what you send from your bank</span>
</div>

<InfoCard
variant="error"
icon="alert"
description="This must match what you send from your bank!"
/>
<Button
variant="purple"
shadowSize="4"
Expand All @@ -368,6 +370,8 @@ export default function OnrampBankPage() {
visible={showWarningModal}
onClose={handleWarningCancel}
onConfirm={handleWarningConfirm}
amount={rawTokenAmount}
currency={getCurrencySymbol(getCurrencyConfig(selectedCountry.id, 'onramp').currency)}
/>
</div>
)
Expand Down
138 changes: 71 additions & 67 deletions src/components/AddMoney/components/AddMoneyBankDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@

import Card from '@/components/Global/Card'
import NavHeader from '@/components/Global/NavHeader'
import PeanutActionDetailsCard, { type PeanutActionDetailsCardProps } from '@/components/Global/PeanutActionDetailsCard'
import ShareButton from '@/components/Global/ShareButton'
import { PaymentInfoRow } from '@/components/Payment/PaymentInfoRow'
import { PEANUT_WALLET_TOKEN_SYMBOL } from '@/constants'
import { useOnrampFlow } from '@/context/OnrampFlowContext'
import { useRouter, useParams } from 'next/navigation'
import { useCallback, useEffect, useMemo } from 'react'
import { ALL_COUNTRIES_ALPHA3_TO_ALPHA2, type CountryData, countryData } from '@/components/AddMoney/consts'
import { type CountryData, countryData } from '@/components/AddMoney/consts'
import { formatCurrencyAmount } from '@/utils/currency'
import { formatBankAccountDisplay } from '@/utils/format.utils'
import Icon from '@/components/Global/Icon'
import { getCurrencyConfig, getCurrencySymbol } from '@/utils/bridge.utils'
import { RequestFulfillmentBankFlowStep, useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext'
import { usePaymentStore } from '@/redux/hooks'
import { formatAmount, printableAddress } from '@/utils'
import { formatAmount } from '@/utils'
import useGetExchangeRate from '@/hooks/useGetExchangeRate'
import { AccountType } from '@/interfaces'
import InfoCard from '@/components/Global/InfoCard'
import CopyToClipboard from '@/components/Global/CopyToClipboard'
import { Button } from '@/components/0_Bruddle'

interface IAddMoneyBankDetails {
flow?: 'add-money' | 'request-fulfillment'
Expand Down Expand Up @@ -91,12 +91,6 @@ export default function AddMoneyBankDetails({ flow = 'add-money' }: IAddMoneyBan
return countryData.find((c) => c.id === 'US')
}, [isAddMoneyFlow, requestFulfilmentSelectedCountry, currentCountryName])

const countryCodeForFlag = useMemo(() => {
const countryId = currentCountryDetails?.id || 'USA'
const countryCode = ALL_COUNTRIES_ALPHA3_TO_ALPHA2[countryId] || countryId // if countryId is not in countryCodeMap, use countryId because for some countries countryId is of 2 digit and countryCodeMap is a mapping of 3 digit to 2 digit country codes
return countryCode?.toLowerCase() || 'us'
}, [currentCountryDetails])

const onrampCurrency = getCurrencyConfig(currentCountryDetails?.id || 'US', 'onramp').currency

useEffect(() => {
Expand All @@ -109,8 +103,18 @@ export default function AddMoneyBankDetails({ flow = 'add-money' }: IAddMoneyBan
}
}, [amount, router, isAddMoneyFlow])

const formattedCurrencyAmount = useMemo(() => {
if (!amount) return ''

if (flow === 'request-fulfillment') {
return formatCurrencyAmount(amount, 'USD') // Request fulfillment flow is in USD
}

return formatCurrencyAmount(amount, onrampCurrency)
}, [amount, onrampCurrency, flow])

const generateBankDetails = async () => {
const formattedAmount = formatCurrencyAmount(amount ?? '0', onrampCurrency)
const formattedAmount = formattedCurrencyAmount
const isMexico = currentCountryDetails?.id === 'MX'

let bankDetails = `Bank Transfer Details:
Expand Down Expand Up @@ -141,7 +145,7 @@ ${routingLabel}: ${routingValue}`
}

bankDetails += `
Deposit Message: ${onrampData?.depositInstructions?.depositMessage || 'Loading...'}
Deposit Reference: ${onrampData?.depositInstructions?.depositMessage || 'Loading...'}

Please use these details to complete your bank transfer.`

Expand All @@ -156,67 +160,63 @@ Please use these details to complete your bank transfer.`
}
}

const peanutActionDetailsCardProps = useMemo<PeanutActionDetailsCardProps>(() => {
if (isAddMoneyFlow) {
return {
avatarSize: 'small',
transactionType: 'ADD_MONEY_BANK_ACCOUNT',
recipientType: 'BANK_ACCOUNT',
recipientName: 'Your Bank Account',
amount: amount ?? '0',
tokenSymbol: PEANUT_WALLET_TOKEN_SYMBOL,
countryCodeForFlag: countryCodeForFlag,
currencySymbol: getCurrencySymbol(onrampCurrency),
}
} else {
return {
avatarSize: 'small',
transactionType: 'REQUEST_PAYMENT',
recipientType: 'USERNAME',
recipientName:
chargeDetails?.requestLink.recipientAccount.user.username ??
printableAddress(chargeDetails?.requestLink.recipientAddress as string),
amount: isFetchingRate ? '-' : amountBasedOnCurrencyExchangeRate(amount ?? '0'),
tokenSymbol: '',
countryCodeForFlag: countryCodeForFlag,
currencySymbol: getCurrencySymbol(onrampCurrency),
}
}
}, [
isAddMoneyFlow,
amount,
countryCodeForFlag,
onrampCurrency,
chargeDetails,
isFetchingRate,
amountBasedOnCurrencyExchangeRate,
])

if (!amount) {
return null
}

return (
<div className="flex h-full w-full flex-col justify-start gap-8 self-start">
<NavHeader title={isAddMoneyFlow ? 'Add Money' : 'Send'} onPrev={handleBack} />
<NavHeader title={'Transfer details'} onPrev={handleBack} />

<div className="my-auto flex h-full w-full flex-col justify-center space-y-4 pb-5">
<PeanutActionDetailsCard {...peanutActionDetailsCardProps} />
<Card className="p-4">
<p className="text-xs font-normal text-gray-1">Amount to send</p>
<div className="flex items-baseline gap-2">
<p className="text-2xl font-extrabold text-black md:text-4xl">{formattedCurrencyAmount}</p>
<CopyToClipboard textToCopy={formattedCurrencyAmount} fill="black" iconSize="3" />
</div>

<InfoCard variant="error" className="mt-4" icon="alert" description="Send exactly this amount!" />
</Card>

<Card className="p-4">
<p className="text-xs font-normal text-gray-1">Deposit reference</p>
<div className="flex items-baseline gap-2">
<p className="text-xl font-extrabold text-black md:text-4xl">
{onrampData?.depositInstructions?.depositMessage || 'Loading...'}
</p>
{onrampData?.depositInstructions?.depositMessage && (
<CopyToClipboard
textToCopy={onrampData.depositInstructions.depositMessage}
fill="black"
iconSize="3"
/>
)}
</div>

<InfoCard
variant="error"
className="mt-4"
icon="alert"
description="Paste in your bank's reference field"
/>
</Card>

<Card className="gap-2 rounded-sm">
<h1 className="text-xs">Bank Details</h1>

<Card className="rounded-sm">
{flow === 'add-money' && (
<PaymentInfoRow label={'Amount'} value={formatCurrencyAmount(amount, onrampCurrency)} />
)}
<PaymentInfoRow
label={'Bank Name'}
value={onrampData?.depositInstructions?.bankName || 'Loading...'}
allowCopy={!!onrampData?.depositInstructions?.bankName}
hideBottomBorder
/>
{currentCountryDetails?.id !== 'MX' && (
<PaymentInfoRow
label={'Bank Address'}
value={onrampData?.depositInstructions?.bankAddress || 'Loading...'}
allowCopy={!!onrampData?.depositInstructions?.bankAddress}
hideBottomBorder
/>
)}
{currentCountryDetails?.id !== 'MX' && (
Expand All @@ -239,6 +239,7 @@ Please use these details to complete your bank transfer.`
onrampData?.depositInstructions?.bankAccountNumber ||
onrampData?.depositInstructions?.iban
}
hideBottomBorder
/>
)}
{currentCountryDetails?.id !== 'MX' && (
Expand All @@ -255,26 +256,29 @@ Please use these details to complete your bank transfer.`
onrampData?.depositInstructions?.bic
)
}
hideBottomBorder
/>
)}
<PaymentInfoRow
hideBottomBorder
label={'Deposit Message'}
value={onrampData?.depositInstructions?.depositMessage || 'Loading...'}
moreInfoText="Make sure you enter this exact message as the transfer concept or description. If it's not included, the deposit can't be processed."
allowCopy={!!onrampData?.depositInstructions?.depositMessage}
/>
</Card>

<div className="flex items-center gap-2 text-xs text-grey-1">
<Icon name="info" width={16} height={16} />
<span>Include the Deposit Message exactly as shown. It's required to process your deposit.</span>
</div>
<InfoCard
variant="warning"
icon="alert"
title="Double check in your bank before sending:"
items={[
`Amount: ${formattedCurrencyAmount} (exact)`,
`Reference: ${onrampData?.depositInstructions?.depositMessage || 'Loading...'} (included)`,
]}
/>

<Button onClick={() => router.push('/home')} variant="purple" className="w-full" shadowSize="4">
I've sent the transfer
</Button>

<ShareButton
generateText={generateBankDetails}
title="Bank Transfer Details"
variant="purple"
variant="primary-soft"
className="w-full"
>
Share Details
Expand Down
53 changes: 41 additions & 12 deletions src/components/AddMoney/components/OnrampConfirmationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
'use client'

import ActionModal from '@/components/Global/ActionModal'
import InfoCard from '@/components/Global/InfoCard'
import { Slider } from '@/components/Slider'

interface OnrampConfirmationModalProps {
visible: boolean
onClose: () => void
onConfirm: () => void
amount: string
currency: string
}

export const OnrampConfirmationModal = ({ visible, onClose, onConfirm }: OnrampConfirmationModalProps) => {
export const OnrampConfirmationModal = ({
visible,
onClose,
onConfirm,
amount,
currency,
}: OnrampConfirmationModalProps) => {
return (
<ActionModal
visible={visible}
Expand All @@ -18,22 +27,42 @@ export const OnrampConfirmationModal = ({ visible, onClose, onConfirm }: OnrampC
iconContainerClassName="bg-yellow-400"
iconProps={{ className: 'text-black' }}
title="IMPORTANT!"
description={
<>
In the following step you'll see a <br /> <strong>"Deposit Message" item</strong> <br /> copy and
paste it exactly as it is on <br /> the description field of your transfer.
<br />
<br />
<strong>
Without it your deposit will be returned and might take 2-10 working days to process.
</strong>
</>
}
footer={
<div className="w-full">
<Slider onValueChange={(v) => v && onConfirm()} />
</div>
}
content={
<div className="flex w-full flex-col gap-4">
<InfoCard variant="default" items={['Bank details to send money to', 'A deposit reference code']} />
<h2 className="mr-auto font-bold">You MUST:</h2>
<InfoCard
variant="info"
itemIcon="check"
itemIconClassName="text-secondary-7"
items={[
<>
Send exactly{' '}
<b>
{currency}
{amount}
</b>{' '}
(the exact amount shown)
</>,
'Copy the reference code exactly',
'Paste it in the description/reference field',
]}
/>

<InfoCard
variant="error"
icon="alert"
iconClassName="text-error-5"
title="If the amount or reference don't match:"
description="Your deposit will fail and it will take 2 to 10 days to return to your bank and might incur fees."
/>
</div>
}
preventClose={false}
modalPanelClassName="max-w-md mx-8"
/>
Expand Down
6 changes: 6 additions & 0 deletions src/components/AddWithdraw/DynamicBankAccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useSavedAccounts from '@/hooks/useSavedAccounts'
import { useAppDispatch, useAppSelector } from '@/redux/hooks'
import { bankFormActions } from '@/redux/slices/bank-form-slice'
import { useDebounce } from '@/hooks/useDebounce'
import { Icon } from '../Global/Icons/Icon'

const isIBANCountry = (country: string) => {
return BRIDGE_ALPHA3_TO_ALPHA2[country.toUpperCase()] !== undefined
Expand Down Expand Up @@ -428,6 +429,11 @@ export const DynamicBankAccountForm = forwardRef<{ handleSubmit: () => void }, D
})}
</>
)}

<div className="flex items-center gap-2 text-gray-1">
<Icon name="info" className="size-4" />
<p className="text-sm">You can only withdraw to accounts under your name.</p>
</div>
<Button
type="submit"
variant="purple"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Global/CopyToClipboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface Props {
textToCopy: string
fill?: string
className?: string
iconSize?: '2' | '4' | '6' | '8'
iconSize?: '2' | '3' | '4' | '6' | '8'
type?: 'button' | 'icon'
buttonSize?: ButtonSize
}
Expand Down
Loading
Loading