"use client"; import { useQuery } from "@tanstack/react-query"; import type { TokenWithPrices } from "../../../../bridge/types/Token.js"; import type { ThirdwebClient } from "../../../../client/client.js"; import { NATIVE_TOKEN_ADDRESS } from "../../../../constants/addresses.js"; import type { SupportedFiatCurrency } from "../../../../pay/convert/type.js"; import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js"; import { type Address, getAddress, shortenAddress, } from "../../../../utils/address.js"; import { resolvePromisedValue } from "../../../../utils/promise/resolve-promised-value.js"; import { getDefaultWalletsForBridgeComponents } from "../../../../wallets/defaultWallets.js"; import { getWalletBalance } from "../../../../wallets/utils/getWalletBalance.js"; import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js"; import { fontSize, radius, spacing, type Theme, } from "../../../core/design-system/index.js"; import { useChainMetadata } from "../../../core/hooks/others/useChainQuery.js"; import { useTransactionDetails } from "../../../core/hooks/useTransactionDetails.js"; import { useActiveAccount } from "../../../core/hooks/wallets/useActiveAccount.js"; import { useActiveWallet } from "../../../core/hooks/wallets/useActiveWallet.js"; import { ConnectButton } from "../ConnectWallet/ConnectButton.js"; import { PoweredByThirdweb } from "../ConnectWallet/PoweredByTW.js"; import { formatCurrencyAmount } from "../ConnectWallet/screens/formatTokenBalance.js"; import { Container, Line } from "../components/basic.js"; import { Button } from "../components/buttons.js"; import { ChainName } from "../components/ChainName.js"; import { Skeleton } from "../components/Skeleton.js"; import { Spacer } from "../components/Spacer.js"; import { Spinner } from "../components/Spinner.js"; import { Text } from "../components/text.js"; import type { PayEmbedConnectOptions } from "../PayEmbed.js"; import { ChainIcon } from "./common/TokenAndChain.js"; import { WithHeader } from "./common/WithHeader.js"; type TransactionPaymentProps = { /** * UI configuration and mode */ transaction: PreparedTransaction; currency: SupportedFiatCurrency; buttonLabel: string | undefined; metadata: { title: string | undefined; description: string | undefined; image: string | undefined; }; /** * ThirdwebClient for blockchain interactions */ client: ThirdwebClient; /** * Called when user confirms transaction execution */ onContinue: ( amount: string, token: TokenWithPrices, receiverAddress: Address, ) => void; /** * Request to execute the transaction immediately (skips funding flow) */ onExecuteTransaction: () => void; /** * Connect options for wallet connection */ connectOptions?: PayEmbedConnectOptions; /** * Whether to show thirdweb branding in the widget. * @default true */ showThirdwebBranding?: boolean; }; export function TransactionPayment({ transaction, client, onContinue, onExecuteTransaction, connectOptions, currency, showThirdwebBranding = true, buttonLabel: _buttonLabel, metadata, }: TransactionPaymentProps) { const theme = useCustomTheme(); const activeAccount = useActiveAccount(); const wallet = useActiveWallet(); // Get chain metadata for native currency symbol const chainMetadata = useChainMetadata(transaction.chain); // Use the extracted hook for transaction details const transactionDataQuery = useTransactionDetails({ client, transaction: transaction, wallet, }); // We can't use useWalletBalance here because erc20Value is a possibly async value const { data: userBalance } = useQuery({ enabled: !!activeAccount?.address, queryFn: async (): Promise => { if (!activeAccount?.address) { return "0"; } const erc20Value = await resolvePromisedValue(transaction.erc20Value); const walletBalance = await getWalletBalance({ address: activeAccount?.address, chain: transaction.chain, tokenAddress: erc20Value?.tokenAddress.toLowerCase() !== NATIVE_TOKEN_ADDRESS ? erc20Value?.tokenAddress : undefined, client, }); return walletBalance.displayValue; }, queryKey: ["user-balance", activeAccount?.address], }); const contractName = transactionDataQuery.data?.contractMetadata?.name || "Unknown Contract"; const functionName = transactionDataQuery.data?.functionInfo?.functionName || "Contract Call"; const isLoading = transactionDataQuery.isLoading || chainMetadata.isLoading; const buttonLabel = _buttonLabel || `Execute ${functionName}`; const tokenFiatPricePerToken = transactionDataQuery.data?.tokenInfo?.prices[currency] || undefined; const totalFiatCost = tokenFiatPricePerToken && transactionDataQuery.data ? tokenFiatPricePerToken * Number(transactionDataQuery.data.totalCost) : undefined; const costToDisplay = totalFiatCost !== undefined ? formatCurrencyAmount(currency, totalFiatCost) : transactionDataQuery.data?.txCostDisplay; if (isLoading) { return ( {/* Loading Header */} {/* Loading Rows */} {/* Loading Button */} {showThirdwebBranding ? (
) : null}
); } return ( {/* Cost and Function Name section */} {/* USD Value */} {costToDisplay} {/* Function Name */} {functionName} {/* Contract Info */} {contractName !== "UnknownContract" && contractName !== undefined && contractName !== "Unknown Contract" && ( Contract {contractName} )} {/* Address */} Address {shortenAddress(transaction.to as string)} {/* Network */} Network {/* Cost */} {transactionDataQuery.data?.txCostDisplay && ( Cost {transactionDataQuery.data?.txCostDisplay} )} {/* Network Fees */} {transactionDataQuery.data?.gasCostDisplay && ( Network fees {transactionDataQuery.data?.gasCostDisplay} )} {/* Action Button */} {activeAccount ? ( ) : ( )} {showThirdwebBranding ? (
) : null}
); } const SkeletonHeader = (_props: { theme: Theme }) => ( ); function SkeletonRow(props: { labelWidth?: string; valueWidth?: string }) { return ( ); }