Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
62e3c7e
currencies fixed
FacuBozzi Jun 28, 2025
cf6d4b6
fixed mexico having a minimum TX amount of 50mxn
FacuBozzi Jun 28, 2025
c49c972
style: Apply prettier formatting
FacuBozzi Jun 28, 2025
2444603
non-US banks having details wrongly shared with ShareButton
FacuBozzi Jun 28, 2025
a9a69ab
Mexico doesnt have IBAN/BIC or equivalent, so we hide those details f…
FacuBozzi Jun 28, 2025
b5eb1d7
IBAN formatted into human readable code + also copied with ShareButto…
FacuBozzi Jun 28, 2025
5c4a838
mexico non iban/bic clarification
FacuBozzi Jun 29, 2025
67e54d4
Merge branch 'peanut-wallet-dev' into bugfix/wrong-currency
FacuBozzi Jun 29, 2025
ffaf281
style: Apply prettier formatting
FacuBozzi Jun 29, 2025
8bd8c5b
fixed MXN currency format
FacuBozzi Jun 29, 2025
26c68d2
Merge branch 'peanut-wallet-dev' into bugfix/copy-bank-details
FacuBozzi Jun 29, 2025
801351d
style: Apply prettier formatting
FacuBozzi Jun 29, 2025
5610675
Merge branch 'bugfix/wrong-currency' into bugfix/copy-bank-details
FacuBozzi Jun 29, 2025
100b862
fixed double import
FacuBozzi Jun 29, 2025
01bc540
fixed kush comment
FacuBozzi Jun 29, 2025
a546e00
Merge pull request #938 from peanutprotocol/bugfix/copy-bank-details
Hugo0 Jun 29, 2025
63900e5
Merge branch 'peanut-wallet-dev' into bugfix/wrong-currency
Hugo0 Jun 29, 2025
0456983
fix: disable mxn onramp + disable other add methods
kushagrasarathe Jun 29, 2025
f0f3c04
Merge branch 'bugfix/wrong-currency' of https://github.com/peanutprot…
kushagrasarathe Jun 29, 2025
b6e044e
fix: disable non sepa/us countries bank on/offramps
kushagrasarathe Jun 29, 2025
cc9b57a
fix: display currency symbol
jjramirezn Jun 29, 2025
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
35 changes: 31 additions & 4 deletions src/app/(mobile-ui)/add-money/[country]/bank/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import EmptyState from '@/components/Global/EmptyStates/EmptyState'
import { UserDetailsForm, type UserDetailsFormData } from '@/components/AddMoney/UserDetailsForm'
import { updateUserById } from '@/app/actions/users'
import AddMoneyBankDetails from '@/components/AddMoney/components/AddMoneyBankDetails'
import { getDisplayCurrencySymbol } from '@/utils/currency'
import { getOnrampCurrencyConfig, getCurrencySymbol, getMinimumAmount } from '@/utils/bridge.utils'

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

Expand Down Expand Up @@ -76,6 +78,21 @@ export default function OnrampBankPage() {
return formattedBalance
}, [balance])

const currencyConfig = useMemo(() => {
if (!selectedCountry?.id) return null
const { currency } = getOnrampCurrencyConfig(selectedCountry.id)
return {
code: currency.toUpperCase(),
symbol: getCurrencySymbol(currency),
price: 1, // For bridge currencies, we use 1:1 with the amount entered
}
}, [selectedCountry?.id])

const minimumAmount = useMemo(() => {
if (!selectedCountry?.id) return 1
return getMinimumAmount(selectedCountry.id)
}, [selectedCountry?.id])

useEffect(() => {
if (user === null) return // wait for user to be fetched
if (step === 'loading') {
Expand Down Expand Up @@ -119,14 +136,14 @@ export default function OnrampBankPage() {
setError({ showError: true, errorMessage: 'Please enter a valid number.' })
return false
}
if (amount && amount < 1) {
setError({ showError: true, errorMessage: 'Minimum deposit is 1.' })
if (amount && amount < minimumAmount) {
setError({ showError: true, errorMessage: `Minimum deposit is ${minimumAmount}.` })
return false
}
setError({ showError: false, errorMessage: '' })
return true
},
[setError]
[setError, minimumAmount]
)

const handleTokenAmountChange = useCallback(
Expand Down Expand Up @@ -328,6 +345,16 @@ export default function OnrampBankPage() {
setTokenValue={handleTokenAmountChange}
walletBalance={peanutWalletBalance}
hideCurrencyToggle
currency={
Copy link
Contributor

Choose a reason for hiding this comment

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

no default to usd? does undefined make sense here?

selectedCountry
? {
code: getOnrampCurrencyConfig(selectedCountry.id).currency,
symbol: getCurrencySymbol(getOnrampCurrencyConfig(selectedCountry.id).currency),
price: 1,
}
: undefined
}
hideBalance={true}
/>
<div className="flex items-center gap-2 text-xs text-grey-1">
<Icon name="info" width={16} height={16} />
Expand All @@ -339,7 +366,7 @@ export default function OnrampBankPage() {
onClick={handleAmountContinue}
disabled={
!parseFloat(rawTokenAmount) ||
parseFloat(rawTokenAmount) < 1 ||
parseFloat(rawTokenAmount) < minimumAmount ||
error.showError ||
isCreatingOnramp
}
Expand Down
94 changes: 64 additions & 30 deletions src/components/AddMoney/components/AddMoneyBankDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { useRouter, useParams } from 'next/navigation'
import { useEffect, useState, useMemo } from 'react'
import { countryCodeMap, countryData } from '@/components/AddMoney/consts'
import { formatCurrencyAmount } from '@/utils/currency'
import { formatBankAccountDisplay } from '@/utils/format.utils'
import Icon from '@/components/Global/Icon'
import { getCurrencySymbol, getOnrampCurrencyConfig } from '@/utils/bridge.utils'

export interface IOnrampData {
transferId?: string
Expand Down Expand Up @@ -62,6 +64,8 @@ export default function AddMoneyBankDetails() {
return countryCode?.toLowerCase() || 'us'
}, [currentCountryDetails])

const onrampCurrency = getOnrampCurrencyConfig(currentCountryDetails?.id || 'US').currency

useEffect(() => {
// If no amount is set, redirect back to add money page
if (!amountToOnramp || parseFloat(amountToOnramp) <= 0) {
Expand All @@ -82,13 +86,37 @@ export default function AddMoneyBankDetails() {
}, [amountToOnramp, router])

const generateBankDetails = async () => {
const formattedAmount = formatCurrencyAmount(amountToOnramp, currentCountryDetails?.currency || 'USD')
const bankDetails = `Bank Transfer Details:
const formattedAmount = formatCurrencyAmount(amountToOnramp, onrampCurrency)
const isMexico = currentCountryDetails?.id === 'MX'
Copy link
Contributor

Choose a reason for hiding this comment

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

thought: this will likely have to be reworked when we add more geos


let bankDetails = `Bank Transfer Details:
Amount: ${formattedAmount}
Bank Name: ${onrampData?.depositInstructions?.bankName || 'Loading...'}
Bank Address: ${onrampData?.depositInstructions?.bankAddress || 'Loading...'}
Account Number: ${onrampData?.depositInstructions?.bankAccountNumber || 'Loading...'}
Routing Number: ${onrampData?.depositInstructions?.bankRoutingNumber || 'Loading...'}
Bank Name: ${onrampData?.depositInstructions?.bankName || 'Loading...'}`

// Only include Bank Address for non-Mexico countries since Mexico doesn't return IBAN/BIC or equivalent
if (!isMexico) {
bankDetails += `
Bank Address: ${onrampData?.depositInstructions?.bankAddress || 'Loading...'}`

const accountLabel = onrampData?.depositInstructions?.bankAccountNumber ? 'Account Number' : 'IBAN'
const routingLabel = onrampData?.depositInstructions?.bankRoutingNumber ? 'Routing Number' : 'BIC'
const accountValue =
onrampData?.depositInstructions?.bankAccountNumber ||
(onrampData?.depositInstructions?.iban
? formatBankAccountDisplay(onrampData.depositInstructions.iban, 'iban')
: null) ||
'Loading...'
const routingValue =
onrampData?.depositInstructions?.bankRoutingNumber ||
onrampData?.depositInstructions?.bic ||
'Loading...'

bankDetails += `
${accountLabel}: ${accountValue}
${routingLabel}: ${routingValue}`
}

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

Please use these details to complete your bank transfer.`
Expand Down Expand Up @@ -117,37 +145,43 @@ Please use these details to complete your bank transfer.`
amount={amountToOnramp}
tokenSymbol={PEANUT_WALLET_TOKEN_SYMBOL}
countryCodeForFlag={countryCodeForFlag}
currencySymbol={getCurrencySymbol(onrampCurrency)}
/>

<Card className="rounded-sm">
<PaymentInfoRow
label={'Amount'}
value={formatCurrencyAmount(amountToOnramp, currentCountryDetails?.currency || 'USD')}
/>
<PaymentInfoRow label={'Amount'} value={formatCurrencyAmount(amountToOnramp, onrampCurrency)} />
<PaymentInfoRow
label={'Bank Name'}
value={onrampData?.depositInstructions?.bankName || 'Loading...'}
/>
<PaymentInfoRow
label={'Bank Address'}
value={onrampData?.depositInstructions?.bankAddress || 'Loading...'}
/>
<PaymentInfoRow
label={onrampData?.depositInstructions?.bankAccountNumber ? 'Account Number' : 'IBAN'}
value={
onrampData?.depositInstructions?.bankAccountNumber ||
onrampData?.depositInstructions?.iban ||
'Loading...'
}
/>
<PaymentInfoRow
label={onrampData?.depositInstructions?.bankRoutingNumber ? 'Routing Number' : 'BIC'}
value={
onrampData?.depositInstructions?.bankRoutingNumber ||
onrampData?.depositInstructions?.bic ||
'Loading...'
}
/>
{currentCountryDetails?.id !== 'MX' && (
<PaymentInfoRow
label={'Bank Address'}
value={onrampData?.depositInstructions?.bankAddress || 'Loading...'}
/>
)}
{currentCountryDetails?.id !== 'MX' && (
<PaymentInfoRow
label={onrampData?.depositInstructions?.bankAccountNumber ? 'Account Number' : 'IBAN'}
value={
onrampData?.depositInstructions?.bankAccountNumber ||
(onrampData?.depositInstructions?.iban
? formatBankAccountDisplay(onrampData.depositInstructions.iban, 'iban')
: null) ||
'N/A'
}
/>
)}
{currentCountryDetails?.id !== 'MX' && (
<PaymentInfoRow
label={onrampData?.depositInstructions?.bankRoutingNumber ? 'Routing Number' : 'BIC'}
value={
onrampData?.depositInstructions?.bankRoutingNumber ||
onrampData?.depositInstructions?.bic ||
'N/A'
}
/>
)}
<PaymentInfoRow
hideBottomBorder
label={'Deposit Message'}
Expand Down
7 changes: 6 additions & 1 deletion src/components/AddMoney/consts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,8 @@ export const countryCodeMap: { [key: string]: string } = {
USA: 'US',
}

const enabledBankTransferCountries = new Set([...Object.keys(countryCodeMap), 'US'])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix country code format inconsistency in enabledBankTransferCountries.

The set mixes 3-letter country codes from countryCodeMap keys with the 2-letter 'US' code. However, the countryData array uses inconsistent formats - some countries use 2-letter codes (like 'US', 'AE') while others use 3-letter codes (like 'AND', 'AUT'). This could cause incorrect matching when checking enabledBankTransferCountries.has(countryCode).

-const enabledBankTransferCountries = new Set([...Object.keys(countryCodeMap), 'US'])
+const enabledBankTransferCountries = new Set([...Object.keys(countryCodeMap), 'USA'])

Also verify that all country codes in the logic below use consistent formatting with this set.

🤖 Prompt for AI Agents
In src/components/AddMoney/consts/index.ts at line 2066, the
enabledBankTransferCountries set mixes 3-letter codes from countryCodeMap keys
with the 2-letter code 'US', causing inconsistency with countryData codes. To
fix this, standardize all country codes to either 2-letter or 3-letter format
consistently across enabledBankTransferCountries and countryData. Update the set
and any related logic to use the same country code format for accurate matching.


countryData.forEach((country) => {
if (country.type === 'country') {
const countryCode = country.id
Expand Down Expand Up @@ -2115,7 +2117,7 @@ countryData.forEach((country) => {
...DEFAULT_BANK_WITHDRAW_METHOD,
id: `${countryCode.toLowerCase()}-default-bank-withdraw`,
path: `/withdraw/${countryCode.toLowerCase()}/bank`,
isSoon: countryCode === 'MX',
isSoon: !enabledBankTransferCountries.has(countryCode),
})
}

Expand All @@ -2129,6 +2131,9 @@ countryData.forEach((country) => {
const newMethod = { ...m }
if (newMethod.id === 'bank-transfer-add') {
newMethod.path = `/add-money/${country.path}/bank`
newMethod.isSoon = !enabledBankTransferCountries.has(countryCode)
} else {
newMethod.isSoon = true
}
return newMethod
})
Expand Down
9 changes: 8 additions & 1 deletion src/components/Global/PeanutActionDetailsCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface PeanutActionDetailsCardProps {
fileUrl?: string
avatarSize?: AvatarSize
countryCodeForFlag?: string
currencySymbol?: string
}

export default function PeanutActionDetailsCard({
Expand All @@ -43,6 +44,7 @@ export default function PeanutActionDetailsCard({
fileUrl,
avatarSize = 'medium',
countryCodeForFlag,
currencySymbol,
}: PeanutActionDetailsCardProps) {
const renderRecipient = () => {
if (recipientType === 'ADDRESS') return printableAddress(recipientName)
Expand Down Expand Up @@ -157,10 +159,15 @@ export default function PeanutActionDetailsCard({
<div className="min-w-0 space-y-1">
{getTitle()}
<h2 className="text-2xl font-extrabold">
{tokenSymbol.toLowerCase() === PEANUT_WALLET_TOKEN_SYMBOL.toLowerCase() ? '$ ' : ''}
{transactionType === 'ADD_MONEY' && currencySymbol
? `${currencySymbol} `
: tokenSymbol.toLowerCase() === PEANUT_WALLET_TOKEN_SYMBOL.toLowerCase()
? '$ '
: ''}
{amount}

{tokenSymbol.toLowerCase() !== PEANUT_WALLET_TOKEN_SYMBOL.toLowerCase() &&
transactionType !== 'ADD_MONEY' &&
` ${tokenSymbol.toLowerCase() === 'pnt' ? (Number(amount) === 1 ? 'Beer' : 'Beers') : tokenSymbol}`}
</h2>

Expand Down
4 changes: 3 additions & 1 deletion src/components/Global/TokenAmountInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface TokenAmountInputProps {
price: number
}
hideCurrencyToggle?: boolean
hideBalance?: boolean
}

const TokenAmountInput = ({
Expand All @@ -32,6 +33,7 @@ const TokenAmountInput = ({
currency,
setUsdValue,
hideCurrencyToggle = false,
hideBalance = false,
}: TokenAmountInputProps) => {
const { selectedTokenData } = useContext(tokenSelectorContext)
const inputRef = useRef<HTMLInputElement>(null)
Expand Down Expand Up @@ -229,7 +231,7 @@ const TokenAmountInput = ({
disabled={disabled}
/>
</div>
{walletBalance && (
{walletBalance && !hideBalance && (
<div className="mt-0.5 text-center text-xs text-grey-1">
Your balance: {displayMode === 'FIAT' && currency ? 'US$' : '$'}
{walletBalance}
Expand Down
27 changes: 25 additions & 2 deletions src/utils/__tests__/bridge.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getCurrencySymbol,
getPaymentRailDisplayName,
type BridgeOperationType,
getMinimumAmount,
} from '../bridge.utils'

describe('bridge.utils', () => {
Expand Down Expand Up @@ -114,6 +115,28 @@ describe('bridge.utils', () => {
paymentRail: 'sepa',
})
})

describe('getMinimumAmount', () => {
it('should return 50 for Mexico', () => {
const minimum = getMinimumAmount('MX')
expect(minimum).toBe(50)
})

it('should return 1 for US', () => {
const minimum = getMinimumAmount('US')
expect(minimum).toBe(1)
})

it('should return 1 for other countries', () => {
const minimum = getMinimumAmount('DE')
expect(minimum).toBe(1)
})

it('should return 1 for empty country code', () => {
const minimum = getMinimumAmount('')
expect(minimum).toBe(1)
})
})
})

describe('bridge support', () => {
Expand All @@ -135,8 +158,8 @@ describe('bridge.utils', () => {
expect(getCurrencySymbol('USD')).toBe('$')
expect(getCurrencySymbol('eur')).toBe('€')
expect(getCurrencySymbol('EUR')).toBe('€')
expect(getCurrencySymbol('mxn')).toBe('$')
expect(getCurrencySymbol('MXN')).toBe('$')
expect(getCurrencySymbol('mxn')).toBe('MX$')
expect(getCurrencySymbol('MXN')).toBe('MX$')
})

it('should return uppercase currency code for unsupported currencies', () => {
Expand Down
14 changes: 13 additions & 1 deletion src/utils/bridge.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,23 @@ export const getCurrencySymbol = (currency: string): string => {
const symbols: Record<string, string> = {
usd: '$',
eur: '€',
mxn: '$',
mxn: 'MX$',
}
return symbols[currency.toLowerCase()] || currency.toUpperCase()
}

/**
* Get minimum amount for onramp operations by country
*/
export const getMinimumAmount = (countryId: string): number => {
if (countryId === 'MX') {
return 50
}

// Default minimum for all other countries (including US and EU)
return 1
}

/**
* Get human-readable payment rail name
*/
Expand Down
13 changes: 10 additions & 3 deletions src/utils/currency.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getCurrencySymbol } from './bridge.utils'

// Helper function to get currency symbol based on code
export const getDisplayCurrencySymbol = (code?: string, fallbackSymbol: string = '$'): string => {
if (!code) return fallbackSymbol
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. why remove?
  2. why no uppercase? imo always uppercase on string cmp


const upperCode = code.toUpperCase()

switch (upperCode) {
Expand All @@ -15,14 +16,20 @@ export const getDisplayCurrencySymbol = (code?: string, fallbackSymbol: string =
return '£'
case 'JPY':
return '¥'
case 'MXN':
return 'MX$'
case 'BRL':
return 'R$'
case 'CAD':
return 'CA$'
default:
return upperCode // Return the currency code itself as fallback (e.g., "CAD", "CHF")
return upperCode // Return the currency code itself as fallback (e.g., "CHF")
}
}

// Simple currency amount formatter
export const formatCurrencyAmount = (amount: string | number, currencyCode: string): string => {
const symbol = getDisplayCurrencySymbol(currencyCode)
const symbol = getCurrencySymbol(currencyCode)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function inconsistency may cause validation issues.

Using getCurrencySymbol creates an inconsistency where formatCurrencyAmount doesn't support ARS (Argentine Peso), while getDisplayCurrencySymbol does. This mismatch could cause the validation issues reported by kushagrasarathe where "valid numbers are being shown as invalid."

The functions should use consistent currency support. Consider:

  1. Using getDisplayCurrencySymbol consistently, or
  2. Ensuring both functions support the same currencies
-    const symbol = getCurrencySymbol(currencyCode)
+    const symbol = getDisplayCurrencySymbol(currencyCode)
📝 Committable suggestion

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

Suggested change
const symbol = getCurrencySymbol(currencyCode)
const symbol = getDisplayCurrencySymbol(currencyCode)
🤖 Prompt for AI Agents
In src/utils/currency.ts at line 16, the function uses getCurrencySymbol which
does not support ARS, causing inconsistency with getDisplayCurrencySymbol that
does. To fix this, replace the call to getCurrencySymbol with
getDisplayCurrencySymbol to ensure consistent currency support and prevent
validation issues with ARS and other currencies.

const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount

if (isNaN(numAmount)) return `${symbol}0`
Expand Down
Loading
Loading