From c2556bbdf800ba99d5054f6c696a5ddd77b08a90 Mon Sep 17 00:00:00 2001 From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:19:03 -0300 Subject: [PATCH 1/4] feat: enable crosschain payments using token selector --- src/components/Payment/PaymentForm/index.tsx | 90 +++++++++++++------- 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/src/components/Payment/PaymentForm/index.tsx b/src/components/Payment/PaymentForm/index.tsx index c9f3c7180..eb09cc5f1 100644 --- a/src/components/Payment/PaymentForm/index.tsx +++ b/src/components/Payment/PaymentForm/index.tsx @@ -10,8 +10,9 @@ import FileUploadInput from '@/components/Global/FileUploadInput' import { type IconName } from '@/components/Global/Icons/Icon' import NavHeader from '@/components/Global/NavHeader' import TokenAmountInput from '@/components/Global/TokenAmountInput' +import TokenSelector from '@/components/Global/TokenSelector/TokenSelector' import UserCard from '@/components/User/UserCard' -import { PEANUT_WALLET_TOKEN, PEANUT_WALLET_TOKEN_DECIMALS } from '@/constants' +import { PEANUT_WALLET_TOKEN, PEANUT_WALLET_TOKEN_DECIMALS, PEANUT_WALLET_CHAIN } from '@/constants' import { tokenSelectorContext } from '@/context' import { useAuth } from '@/context/authContext' import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' @@ -182,6 +183,9 @@ export const PaymentForm = ({ setInputTokenAmount(amount) } + // for ADDRESS/ENS recipients, initialize token/chain from URL or defaults + const isExternalRecipient = recipient?.recipientType === 'ADDRESS' || recipient?.recipientType === 'ENS' + if (chain) { setSelectedChainID((chain.chainId || requestDetails?.chainId) ?? '') if (!token && !requestDetails?.tokenAddress) { @@ -191,15 +195,37 @@ export const PaymentForm = ({ // Note: decimals automatically derived by useTokenPrice hook } } + } else if (isExternalRecipient && !selectedChainID) { + // default to arbitrum for external recipients if no chain specified + setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString()) } if (token) { setSelectedTokenAddress((token.address || requestDetails?.tokenAddress) ?? '') // Note: decimals automatically derived by useTokenPrice hook + } else if (isExternalRecipient && !selectedTokenAddress && selectedChainID) { + // default to USDC for external recipients if no token specified + const chainData = supportedSquidChainsAndTokens[selectedChainID] + const defaultToken = chainData?.tokens.find((t) => t.symbol.toLowerCase() === 'usdc') + if (defaultToken) { + setSelectedTokenAddress(defaultToken.address) + } } setInitialSetupDone(true) - }, [chain, token, amount, initialSetupDone, requestDetails, showRequestPotInitialView, isRequestPotLink]) + }, [ + chain, + token, + amount, + initialSetupDone, + requestDetails, + showRequestPotInitialView, + isRequestPotLink, + recipient?.recipientType, + selectedChainID, + selectedTokenAddress, + supportedSquidChainsAndTokens, + ]) // reset error when component mounts or recipient changes useEffect(() => { @@ -244,12 +270,14 @@ export const PaymentForm = ({ } } else { // regular send/pay + const isExternalRecipient = recipient?.recipientType === 'ADDRESS' || recipient?.recipientType === 'ENS' + if ( !showRequestPotInitialView && // don't apply balance check on request pot payment initial view isActivePeanutWallet && - areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN) + (areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN) || !isExternalRecipient) ) { - // peanut wallet payment + // peanut wallet payment (for USERNAME or default token) const walletNumeric = parseFloat(String(peanutWalletBalance).replace(/,/g, '')) if (walletNumeric < parsedInputAmount) { dispatch(paymentActions.setError('Insufficient balance')) @@ -274,6 +302,9 @@ export const PaymentForm = ({ } else { dispatch(paymentActions.setError(null)) } + } else if (isExternalRecipient && isActivePeanutWallet) { + // for external recipients with peanut wallet, balance will be checked via cross-chain route + dispatch(paymentActions.setError(null)) } else { dispatch(paymentActions.setError(null)) } @@ -304,6 +335,7 @@ export const PaymentForm = ({ currentView, isProcessing, hasPendingTransactions, + recipient?.recipientType, ]) // Calculate USD value when requested token price is available @@ -339,7 +371,12 @@ export const PaymentForm = ({ (!!inputTokenAmount && parseFloat(inputTokenAmount) > 0) || (!!usdValue && parseFloat(usdValue) > 0) } - const tokenSelected = !!selectedTokenAddress && !!selectedChainID + const isExternalRecipient = recipient?.recipientType === 'ADDRESS' || recipient?.recipientType === 'ENS' + // for external recipients, token selection is required + // for USERNAME recipients, token is always PEANUT_WALLET_TOKEN + const tokenSelected = isExternalRecipient + ? !!selectedTokenAddress && !!selectedChainID + : !!selectedTokenAddress && !!selectedChainID const recipientExists = !!recipient const walletConnected = isConnected @@ -809,30 +846,25 @@ export const PaymentForm = ({ defaultSliderSuggestedAmount={defaultSliderValue.suggestedAmount} /> - {/* - Url request flow (peanut.me/
) - If we are paying from peanut wallet we only need to - select a token if it's not included in the url - From other wallets we always need to select a token - */} - {/* we dont need this as daimo will handle token selection */} - {/* {!(chain && isPeanutWalletConnected) && isConnected && !isAddMoneyFlow && ( -
- {!isPeanutWalletUSDC && !selectedTokenAddress && !selectedChainID && ( -
Select token and chain to receive
- )} - - {!isPeanutWalletUSDC && selectedTokenAddress && selectedChainID && ( -
- Use USDC on Arbitrum for free transactions! -
- )} -
- )} */} - - {/* {isExternalWalletConnected && isAddMoneyFlow && ( - - )} */} + {/* Token selector for external ADDRESS/ENS recipients */} + {!isExternalWalletFlow && + !showRequestPotInitialView && + (recipient?.recipientType === 'ADDRESS' || recipient?.recipientType === 'ENS') && + isConnected && ( +
+ + {selectedTokenAddress && + selectedChainID && + !( + areEvmAddressesEqual(selectedTokenAddress, PEANUT_WALLET_TOKEN) && + selectedChainID === PEANUT_WALLET_CHAIN.id.toString() + ) && ( +
+ Use USDC on Arbitrum for free transactions! +
+ )} +
+ )} {isDirectUsdPayment && ( Date: Wed, 12 Nov 2025 13:26:16 -0300 Subject: [PATCH 2/4] fix: use daimo for external wallet payments --- src/app/[...recipient]/client.tsx | 7 -- src/components/Common/ActionList.tsx | 21 ++++-- .../Common/ActionListDaimoPayButton.tsx | 6 ++ src/components/Payment/PaymentForm/index.tsx | 16 +---- .../views/ExternalWalletFulfilManager.tsx | 67 ------------------- .../views/ExternalWalletFulfilMethods.tsx | 45 ------------- src/context/RequestFulfillmentFlowContext.tsx | 18 ----- 7 files changed, 24 insertions(+), 156 deletions(-) delete mode 100644 src/components/Request/views/ExternalWalletFulfilManager.tsx delete mode 100644 src/components/Request/views/ExternalWalletFulfilMethods.tsx diff --git a/src/app/[...recipient]/client.tsx b/src/app/[...recipient]/client.tsx index 007190da8..bbb5fd15c 100644 --- a/src/app/[...recipient]/client.tsx +++ b/src/app/[...recipient]/client.tsx @@ -26,7 +26,6 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { twMerge } from 'tailwind-merge' import { fetchTokenPrice } from '@/app/actions/tokens' import { RequestFulfillmentBankFlowStep, useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' -import ExternalWalletFulfilManager from '@/components/Request/views/ExternalWalletFulfilManager' import ActionList from '@/components/Common/ActionList' import NavHeader from '@/components/Global/NavHeader' import { ReqFulfillBankFlowManager } from '@/components/Request/views/ReqFulfillBankFlowManager' @@ -67,7 +66,6 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) const { isDrawerOpen, selectedTransaction, openTransactionDetails } = useTransactionDetailsDrawer() const [isLinkCancelling, setisLinkCancelling] = useState(false) const { - showExternalWalletFulfillMethods, showRequestFulfilmentBankFlowManager, setShowRequestFulfilmentBankFlowManager, setFlowStep: setRequestFulfilmentBankFlowStep, @@ -524,11 +522,6 @@ export default function PaymentPage({ recipient, flow = 'request_pay' }: Props) ) } - // render external wallet fulfilment methods - if (showExternalWalletFulfillMethods) { - return - } - // render request fulfilment bank flow manager if (showRequestFulfilmentBankFlowManager) { return diff --git a/src/components/Common/ActionList.tsx b/src/components/Common/ActionList.tsx index 657e439ee..1aecc768e 100644 --- a/src/components/Common/ActionList.tsx +++ b/src/components/Common/ActionList.tsx @@ -5,7 +5,7 @@ import IconStack from '../Global/IconStack' import { ClaimBankFlowStep, useClaimBankFlow } from '@/context/ClaimBankFlowContext' import { type ClaimLinkData } from '@/services/sendLinks' import { formatUnits } from 'viem' -import { useContext, useMemo, useState } from 'react' +import { useContext, useMemo, useState, useRef } from 'react' import ActionModal from '@/components/Global/ActionModal' import Divider from '../0_Bruddle/Divider' import { Button } from '../0_Bruddle' @@ -86,7 +86,6 @@ export default function ActionList({ const { addParamStep } = useClaimLink() const { setShowRequestFulfilmentBankFlowManager, - setShowExternalWalletFulfillMethods, setFlowStep: setRequestFulfilmentBankFlowStep, setFulfillUsingManteca, setRegionalMethodType: setRequestFulfillmentRegionalMethodType, @@ -109,6 +108,8 @@ export default function ActionList({ const { initiatePayment, loadingStep } = usePaymentInitiator() const { isUserMantecaKycApproved } = useKycStatus() const isPaymentInProgress = loadingStep !== 'Idle' && loadingStep !== 'Error' && loadingStep !== 'Success' + // ref to store daimo button click handler for triggering from balance modal + const daimoButtonClickRef = useRef<(() => void) | null>(null) const dispatch = useAppDispatch() @@ -255,9 +256,7 @@ export default function ActionList({ setRequestFulfillmentRegionalMethodType(method.id) setFulfillUsingManteca(true) break - case 'exchange-or-wallet': - setShowExternalWalletFulfillMethods(true) - break + // 'exchange-or-wallet' case removed - handled by ActionListDaimoPayButton } } } @@ -329,6 +328,7 @@ export default function ActionList({ return true // Proceed with Daimo }} isDisabled={!isAmountEntered} + clickHandlerRef={daimoButtonClickRef} /> ) @@ -427,7 +427,16 @@ export default function ActionList({ setIsUsePeanutBalanceModalShown(true) // Proceed with the method the user originally selected if (selectedPaymentMethod) { - handleMethodClick(selectedPaymentMethod, true) // true = bypass modal check + // for exchange-or-wallet, trigger daimo button after state updates + if (selectedPaymentMethod.id === 'exchange-or-wallet' && daimoButtonClickRef.current) { + // use setTimeout to ensure state updates are processed before triggering daimo + setTimeout(() => { + daimoButtonClickRef.current?.() + }, 0) + } else { + // for other methods, use handleMethodClick + handleMethodClick(selectedPaymentMethod, true) // true = bypass modal check + } } setSelectedPaymentMethod(null) }, diff --git a/src/components/Common/ActionListDaimoPayButton.tsx b/src/components/Common/ActionListDaimoPayButton.tsx index a2683aeea..2c96d3142 100644 --- a/src/components/Common/ActionListDaimoPayButton.tsx +++ b/src/components/Common/ActionListDaimoPayButton.tsx @@ -17,6 +17,7 @@ interface ActionListDaimoPayButtonProps { showConfirmModal: boolean onBeforeShow?: () => boolean | Promise isDisabled?: boolean + clickHandlerRef?: React.MutableRefObject<(() => void) | null> } const ActionListDaimoPayButton = ({ @@ -24,6 +25,7 @@ const ActionListDaimoPayButton = ({ showConfirmModal, onBeforeShow, isDisabled, + clickHandlerRef, }: ActionListDaimoPayButtonProps) => { const dispatch = useAppDispatch() const searchParams = useSearchParams() @@ -178,6 +180,10 @@ const ActionListDaimoPayButton = ({ {({ onClick, loading }) => { // Store the onClick function so we can trigger it from elsewhere daimoPayButtonClickRef.current = onClick + // also store in parent ref if provided (for balance modal in ActionList) + if (clickHandlerRef) { + clickHandlerRef.current = onClick + } return ( { - if (isExternalWalletFlow) { - setShowExternalWalletFulfillMethods(true) - setExternalWalletFulfillMethod(null) - return - } else if (window.history.length > 1) { + if (window.history.length > 1) { router.back() } else { router.push('/') diff --git a/src/components/Request/views/ExternalWalletFulfilManager.tsx b/src/components/Request/views/ExternalWalletFulfilManager.tsx deleted file mode 100644 index 389ed9005..000000000 --- a/src/components/Request/views/ExternalWalletFulfilManager.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' -import ExternalWalletFulfilMethods from './ExternalWalletFulfilMethods' -import AddMoneyCryptoPage from '@/app/(mobile-ui)/add-money/crypto/page' -import { type ParsedURL } from '@/lib/url-parser/types/payment' -import { usePaymentStore } from '@/redux/hooks' -import ConfirmPaymentView from '@/components/Payment/Views/Confirm.payment.view' -import DirectSuccessView from '@/components/Payment/Views/Status.payment.view' -import { PaymentForm } from '@/components/Payment/PaymentForm' - -export default function ExternalWalletFulfilManager({ parsedPaymentData }: { parsedPaymentData: ParsedURL }) { - const { - showExternalWalletFulfillMethods, - externalWalletFulfillMethod, - setExternalWalletFulfillMethod, - setShowExternalWalletFulfillMethods, - } = useRequestFulfillmentFlow() - const { currentView } = usePaymentStore() - - if (externalWalletFulfillMethod === 'wallet') { - switch (currentView) { - case 'INITIAL': - return ( - - ) - case 'CONFIRM': - return - case 'STATUS': - return ( - - ) - default: - break - } - } - - if (externalWalletFulfillMethod === 'exchange') { - return ( - { - setExternalWalletFulfillMethod(null) - setShowExternalWalletFulfillMethods(true) - }} - /> - ) - } - - if (showExternalWalletFulfillMethods) { - return setShowExternalWalletFulfillMethods(false)} /> - } - - return null -} diff --git a/src/components/Request/views/ExternalWalletFulfilMethods.tsx b/src/components/Request/views/ExternalWalletFulfilMethods.tsx deleted file mode 100644 index 94acb7fd8..000000000 --- a/src/components/Request/views/ExternalWalletFulfilMethods.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { BINANCE_LOGO, LEMON_LOGO, RIPIO_LOGO } from '@/assets' -import { METAMASK_LOGO, RAINBOW_LOGO, TRUST_WALLET_SMALL_LOGO } from '@/assets/wallets' -import { MethodCard } from '@/components/Common/ActionList' -import NavHeader from '@/components/Global/NavHeader' -import { type PaymentMethod } from '@/constants/actionlist.consts' -import { type ExternalWalletFulfilMethod, useRequestFulfillmentFlow } from '@/context/RequestFulfillmentFlowContext' - -const methods: PaymentMethod[] = [ - { - id: 'exchange', - title: 'Exchange', - description: 'Lemon, Binance, Ripio and more', - icons: [RIPIO_LOGO, BINANCE_LOGO, LEMON_LOGO], - soon: false, - }, - { - id: 'wallet', - title: 'Crypto Wallet', - description: 'Metamask, Trustwallet and more', - icons: [RAINBOW_LOGO, TRUST_WALLET_SMALL_LOGO, METAMASK_LOGO], - soon: false, - }, -] - -export default function ExternalWalletFulfilMethods({ onBack }: { onBack: () => void }) { - const { setExternalWalletFulfillMethod } = useRequestFulfillmentFlow() - - return ( -
- -
-
Where will you send from?
- {methods.map((method) => ( - { - setExternalWalletFulfillMethod(method.id as ExternalWalletFulfilMethod) - }} - /> - ))} -
-
- ) -} diff --git a/src/context/RequestFulfillmentFlowContext.tsx b/src/context/RequestFulfillmentFlowContext.tsx index 82ee98a12..d9400aa24 100644 --- a/src/context/RequestFulfillmentFlowContext.tsx +++ b/src/context/RequestFulfillmentFlowContext.tsx @@ -5,8 +5,6 @@ import { type CountryData } from '@/components/AddMoney/consts' import { type IOnrampData } from './OnrampFlowContext' import { type User } from '@/interfaces' -export type ExternalWalletFulfilMethod = 'exchange' | 'wallet' - export enum RequestFulfillmentBankFlowStep { BankCountryList = 'bank-country-list', DepositBankDetails = 'deposit-bank-details', @@ -16,12 +14,8 @@ export enum RequestFulfillmentBankFlowStep { interface RequestFulfillmentFlowContextType { resetFlow: () => void - showExternalWalletFulfillMethods: boolean - setShowExternalWalletFulfillMethods: (showExternalWalletFulfillMethods: boolean) => void showRequestFulfilmentBankFlowManager: boolean setShowRequestFulfilmentBankFlowManager: (showRequestFulfilmentBankFlowManager: boolean) => void - externalWalletFulfillMethod: ExternalWalletFulfilMethod | null - setExternalWalletFulfillMethod: (externalWalletFulfillMethod: ExternalWalletFulfilMethod | null) => void flowStep: RequestFulfillmentBankFlowStep | null setFlowStep: (step: RequestFulfillmentBankFlowStep | null) => void selectedCountry: CountryData | null @@ -43,10 +37,6 @@ interface RequestFulfillmentFlowContextType { const RequestFulfillmentFlowContext = createContext(undefined) export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [showExternalWalletFulfillMethods, setShowExternalWalletFulfillMethods] = useState(false) - const [externalWalletFulfillMethod, setExternalWalletFulfillMethod] = useState( - null - ) const [showRequestFulfilmentBankFlowManager, setShowRequestFulfilmentBankFlowManager] = useState(false) const [flowStep, setFlowStep] = useState(null) const [selectedCountry, setSelectedCountry] = useState(null) @@ -58,8 +48,6 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod const [triggerPayWithPeanut, setTriggerPayWithPeanut] = useState(false) // To trigger the pay with peanut from Action List const resetFlow = useCallback(() => { - setExternalWalletFulfillMethod(null) - setShowExternalWalletFulfillMethods(false) setFlowStep(null) setShowRequestFulfilmentBankFlowManager(false) setSelectedCountry(null) @@ -73,10 +61,6 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod const value = useMemo( () => ({ resetFlow, - externalWalletFulfillMethod, - setExternalWalletFulfillMethod, - showExternalWalletFulfillMethods, - setShowExternalWalletFulfillMethods, flowStep, setFlowStep, showRequestFulfilmentBankFlowManager, @@ -98,8 +82,6 @@ export const RequestFulfilmentFlowContextProvider: React.FC<{ children: ReactNod }), [ resetFlow, - externalWalletFulfillMethod, - showExternalWalletFulfillMethods, flowStep, showRequestFulfilmentBankFlowManager, selectedCountry, From 710a7e608906d5746bddbed7f878691af9c5f45b Mon Sep 17 00:00:00 2001 From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com> Date: Wed, 12 Nov 2025 13:27:04 -0300 Subject: [PATCH 3/4] fix: daimo cross chain payments destination chain --- src/components/Common/ActionListDaimoPayButton.tsx | 7 ++++++- src/components/Global/DaimoPayButton/index.tsx | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/Common/ActionListDaimoPayButton.tsx b/src/components/Common/ActionListDaimoPayButton.tsx index 2c96d3142..95551cbed 100644 --- a/src/components/Common/ActionListDaimoPayButton.tsx +++ b/src/components/Common/ActionListDaimoPayButton.tsx @@ -117,7 +117,10 @@ const ActionListDaimoPayButton = ({ const result = await completeDaimoPayment({ chargeDetails: chargeDetails, txHash: daimoPaymentResponse.txHash as string, - destinationchainId: daimoPaymentResponse.payment.destination.chainId, + // use destination chain from chargeDetails (not from daimo response) + // chargeDetails has the correct chain from URL/explicit overrides + destinationchainId: + Number(chargeDetails.chainId) ?? Number(daimoPaymentResponse.payment.destination.chainId), payerAddress: peanutWalletAddress ?? daimoPaymentResponse.payment.source.payerAddress, sourceChainId: daimoPaymentResponse.payment.source.chainId, sourceTokenAddress: daimoPaymentResponse.payment.source.tokenAddress, @@ -150,6 +153,8 @@ const ActionListDaimoPayButton = ({ { // First check if parent wants to intercept (e.g. show balance modal) diff --git a/src/components/Global/DaimoPayButton/index.tsx b/src/components/Global/DaimoPayButton/index.tsx index 2702efa0c..cdb6b6732 100644 --- a/src/components/Global/DaimoPayButton/index.tsx +++ b/src/components/Global/DaimoPayButton/index.tsx @@ -13,6 +13,10 @@ export interface DaimoPayButtonProps { amount: string /** The recipient address */ toAddress: string + /** Target chain ID (defaults to Arbitrum if not specified) */ + toChainId?: number + /** Target token address (defaults to USDC on Arbitrum if not specified) */ + toTokenAddress?: string /** * Render function that receives click handler and other props * OR React node for backwards compatibility @@ -51,6 +55,8 @@ export interface DaimoPayButtonProps { export const DaimoPayButton = ({ amount, toAddress, + toChainId, + toTokenAddress, children, variant = 'purple', icon, @@ -139,10 +145,10 @@ export const DaimoPayButton = ({ resetOnSuccess // resets the daimo payment state after payment is successfully completed appId={daimoAppId} intent="Deposit" - toChain={arbitrum.id} + toChain={toChainId ?? arbitrum.id} // use provided chain or default to arbitrum toUnits={amount.replace(/,/g, '')} toAddress={getAddress(toAddress)} - toToken={getAddress(PEANUT_WALLET_TOKEN)} // USDC on arbitrum + toToken={getAddress(toTokenAddress ?? PEANUT_WALLET_TOKEN)} // use provided token or default to usdc on arbitrum onPaymentCompleted={onPaymentCompleted} closeOnSuccess onClose={onClose} From 6f4e0cc2c9f29ad354fd50c4bb75380aca473426 Mon Sep 17 00:00:00 2001 From: kushagrasarathe <76868364+kushagrasarathe@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:39:18 -0300 Subject: [PATCH 4/4] fix: cr comments --- src/components/Common/ActionListDaimoPayButton.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Common/ActionListDaimoPayButton.tsx b/src/components/Common/ActionListDaimoPayButton.tsx index 95551cbed..609978550 100644 --- a/src/components/Common/ActionListDaimoPayButton.tsx +++ b/src/components/Common/ActionListDaimoPayButton.tsx @@ -114,13 +114,18 @@ const ActionListDaimoPayButton = ({ if (chargeDetails) { dispatch(paymentActions.setIsDaimoPaymentProcessing(true)) try { + // validate and parse destination chain id with proper fallback + // use chargeDetails chainId if it's a valid non-negative integer, otherwise use daimo response + const parsedChainId = Number(chargeDetails.chainId) + const destinationChainId = + Number.isInteger(parsedChainId) && parsedChainId >= 0 + ? parsedChainId + : Number(daimoPaymentResponse.payment.destination.chainId) + const result = await completeDaimoPayment({ chargeDetails: chargeDetails, txHash: daimoPaymentResponse.txHash as string, - // use destination chain from chargeDetails (not from daimo response) - // chargeDetails has the correct chain from URL/explicit overrides - destinationchainId: - Number(chargeDetails.chainId) ?? Number(daimoPaymentResponse.payment.destination.chainId), + destinationchainId: destinationChainId, payerAddress: peanutWalletAddress ?? daimoPaymentResponse.payment.source.payerAddress, sourceChainId: daimoPaymentResponse.payment.source.chainId, sourceTokenAddress: daimoPaymentResponse.payment.source.tokenAddress,