"use client"; import { useEffect, useState } from "react"; import { trackPayEvent } from "../../../../../analytics/track/pay.js"; import type { TokenWithPrices } from "../../../../../bridge/types/Token.js"; import { defineChain } from "../../../../../chains/utils.js"; import type { ThirdwebClient } from "../../../../../client/client.js"; import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js"; import type { Address } from "../../../../../utils/address.js"; import { toUnits } from "../../../../../utils/units.js"; import type { Wallet } from "../../../../../wallets/interfaces/wallet.js"; import { usePaymentMethods } from "../../../../core/hooks/usePaymentMethods.js"; import { useActiveWallet } from "../../../../core/hooks/wallets/useActiveWallet.js"; import { useConnectedWallets } from "../../../../core/hooks/wallets/useConnectedWallets.js"; import type { SupportedTokens } from "../../../../core/utils/defaultTokens.js"; import type { ConnectLocale } from "../../ConnectWallet/locale/types.js"; import { WalletSwitcherConnectionScreen } from "../../ConnectWallet/screens/WalletSwitcherConnectionScreen.js"; import { Container, ModalHeader } from "../../components/basic.js"; import { Spacer } from "../../components/Spacer.js"; import type { PayEmbedConnectOptions } from "../../PayEmbed.js"; import type { PaymentMethod } from "../types.js"; import { FiatProviderSelection } from "./FiatProviderSelection.js"; import { TokenSelection } from "./TokenSelection.js"; import { WalletFiatSelection } from "./WalletFiatSelection.js"; type PaymentSelectionProps = { /** * The destination token to bridge to */ destinationToken: TokenWithPrices; /** * The destination amount to bridge */ destinationAmount: string; /** * The receiver address */ receiverAddress: Address; /** * ThirdwebClient for API calls */ client: ThirdwebClient; /** * Called when user selects a payment method */ onPaymentMethodSelected: (paymentMethod: PaymentMethod) => void; /** * Called when an error occurs */ onError: (error: Error) => void; /** * Called when user wants to go back */ onBack: () => void; /** * Connect options for wallet connection */ connectOptions: PayEmbedConnectOptions | undefined; /** * Locale for connect UI */ connectLocale: ConnectLocale; /** * Allowed payment methods */ paymentMethods: ("crypto" | "card")[]; /** * Fee payer */ feePayer: "sender" | "receiver" | undefined; /** * The currency to use for the payment. * @default "USD" */ currency: SupportedFiatCurrency; /** * The user's ISO 3166 alpha-2 country code. This is used to determine onramp provider support. */ country: string | undefined; supportedTokens: SupportedTokens | undefined; }; type Step = | { type: "walletSelection" } | { type: "tokenSelection"; selectedWallet: Wallet } | { type: "fiatProviderSelection" } | { type: "walletConnection" }; export function PaymentSelection({ destinationToken, client, destinationAmount, receiverAddress, onPaymentMethodSelected, onError, onBack, connectOptions, connectLocale, paymentMethods, supportedTokens, feePayer, currency, country, }: PaymentSelectionProps) { const connectedWallets = useConnectedWallets(); const activeWallet = useActiveWallet(); const initialStep = paymentMethods.length === 1 && paymentMethods[0] === "card" ? { type: "fiatProviderSelection" as const, } : { type: "walletSelection" as const, }; const [currentStep, setCurrentStep] = useState(initialStep); const payerWallet = currentStep.type === "tokenSelection" ? currentStep.selectedWallet : activeWallet; const { data: suitableTokenPaymentMethods, isLoading: paymentMethodsLoading, error: paymentMethodsError, } = usePaymentMethods({ client, destinationAmount, destinationToken, payerWallet, supportedTokens, }); // Handle error from usePaymentMethods useEffect(() => { if (paymentMethodsError) { onError(paymentMethodsError as Error); } }, [paymentMethodsError, onError]); const handlePaymentMethodSelected = (paymentMethod: PaymentMethod) => { try { onPaymentMethodSelected(paymentMethod); } catch (error) { onError(error as Error); } }; const handleWalletSelected = (wallet: Wallet) => { trackPayEvent({ client, event: "ub:ui:token_selection", }); setCurrentStep({ selectedWallet: wallet, type: "tokenSelection" }); }; const handleConnectWallet = async () => { setCurrentStep({ type: "walletConnection" }); }; const handleFiatSelected = () => { trackPayEvent({ client, event: "ub:ui:fiat_provider_selection", }); setCurrentStep({ type: "fiatProviderSelection" }); }; const handleBackToWalletSelection = () => { setCurrentStep({ type: "walletSelection" }); }; const handleOnrampProviderSelected = ( provider: "coinbase" | "stripe" | "transak", ) => { const recipientAddress = receiverAddress || payerWallet?.getAccount()?.address; if (!recipientAddress) { onError(new Error("No recipient address available for fiat payment")); return; } const fiatPaymentMethod: PaymentMethod = { currency: currency || "USD", onramp: provider, payerWallet, type: "fiat", }; handlePaymentMethodSelected(fiatPaymentMethod); }; const getStepTitle = () => { switch (currentStep.type) { case "walletSelection": return "Choose Payment Method"; case "tokenSelection": return "Select Token"; case "fiatProviderSelection": return "Select Payment Provider"; case "walletConnection": return "Connect Wallet"; } }; const getBackHandler = () => { if (paymentMethods.length === 1 && paymentMethods[0] === "card") { return onBack; } switch (currentStep.type) { case "walletSelection": return onBack; case "tokenSelection": case "fiatProviderSelection": case "walletConnection": return handleBackToWalletSelection; } }; // Handle rendering WalletSwitcherConnectionScreen if (currentStep.type === "walletConnection") { const destinationChain = destinationToken ? defineChain(destinationToken.chainId) : undefined; const chains = destinationChain ? [destinationChain, ...(connectOptions?.chains || [])] : connectOptions?.chains; return ( w.id !== "inApp")} /> ); } return ( {currentStep.type === "walletSelection" && ( )} {currentStep.type === "tokenSelection" && ( )} {currentStep.type === "fiatProviderSelection" && ( )} ); }