"use client"; import { useQuery } from "@tanstack/react-query"; import type { Token } from "../../../../../bridge/types/Token.js"; import type { ChainMetadata } from "../../../../../chains/types.js"; import { defineChain, getCachedChain, getChainMetadata, } from "../../../../../chains/utils.js"; import { shortenHex } from "../../../../../utils/address.js"; import type { WindowAdapter } from "../../../../core/adapters/WindowAdapter.js"; import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js"; import { radius, spacing } from "../../../../core/design-system/index.js"; import type { BridgePrepareResult } from "../../../../core/hooks/useBridgePrepare.js"; import type { CompletedStatusResult } from "../../../../core/hooks/useStepExecutor.js"; import { formatTokenAmount } from "../../ConnectWallet/screens/formatTokenBalance.js"; import { Container, Line, ModalHeader } from "../../components/basic.js"; import { shorterChainName } from "../../components/ChainName.js"; import { CopyIcon } from "../../components/CopyIcon.js"; import { Skeleton } from "../../components/Skeleton.js"; import { Spacer } from "../../components/Spacer.js"; import { Link, Text } from "../../components/text.js"; interface TransactionInfo { type: "paymentId" | "transactionHash"; id: string; label: string; chain: ChainMetadata; destinationToken?: Token; destinationChain?: ChainMetadata; originToken?: Token; originChain?: ChainMetadata; amountPaid?: string; amountReceived?: string; } function getPaymentId( preparedQuote: BridgePrepareResult, status: CompletedStatusResult, ) { if (preparedQuote.type === "onramp") { return preparedQuote.id; } return status.transactions[status.transactions.length - 1]?.transactionHash; } /** * Hook to fetch transaction info for a completed status */ function useTransactionInfo( status: CompletedStatusResult, preparedQuote: BridgePrepareResult, ) { return useQuery({ enabled: true, queryFn: async (): Promise => { const isOnramp = status.type === "onramp"; if (isOnramp && preparedQuote.type === "onramp") { // For onramp, create a display ID since OnrampStatus doesn't have paymentId return { amountPaid: `${preparedQuote.currencyAmount} ${preparedQuote.currency}`, amountReceived: `${formatTokenAmount( preparedQuote.destinationAmount, preparedQuote.destinationToken.decimals, )} ${preparedQuote.destinationToken.symbol}`, chain: await getChainMetadata( defineChain(preparedQuote.destinationToken.chainId), ), destinationToken: preparedQuote.destinationToken, id: preparedQuote.id, label: "Onramp Payment", type: "paymentId" as const, }; } else if ( status.type === "buy" || status.type === "sell" || status.type === "transfer" ) { if (status.transactions.length > 0) { // get the last transaction hash const tx = status.transactions[status.transactions.length - 1]; if (tx) { const [destinationChain, originChain] = await Promise.all([ getChainMetadata(getCachedChain(status.destinationToken.chainId)), getChainMetadata(getCachedChain(status.originToken.chainId)), ]); return { amountPaid: `${formatTokenAmount( status.originAmount, status.originToken.decimals, )} ${status.originToken.symbol}`, amountReceived: `${formatTokenAmount( status.destinationAmount, status.destinationToken.decimals, )} ${status.destinationToken.symbol}`, chain: destinationChain, destinationChain, destinationToken: status.destinationToken, id: tx.transactionHash, label: "Transaction", originChain, originToken: status.originToken, type: "transactionHash" as const, }; } } } return null; }, queryKey: [ "transaction-info", status.type, getPaymentId(preparedQuote, status), ], staleTime: 5 * 60 * 1000, // 5 minutes }); } interface CompletedStepDetailCardProps { status: CompletedStatusResult; preparedQuote: BridgePrepareResult; windowAdapter: WindowAdapter; } /** * Component to display details for a completed transaction step */ function CompletedStepDetailCard({ status, preparedQuote, }: CompletedStepDetailCardProps) { const theme = useCustomTheme(); const { data: txInfo, isPending } = useTransactionInfo(status, preparedQuote); if (isPending) { return ; } if (!txInfo) { return null; } return ( {/* Status Badge */} {txInfo.label} COMPLETED {/* Amount Paid */} {txInfo.amountPaid && ( Amount Paid {txInfo.amountPaid} )} {/* Amount Received */} {txInfo.amountReceived && ( Amount Received {txInfo.amountReceived} )} {/* Origin Chain */} {txInfo.originChain && ( Origin Chain {shorterChainName(txInfo.originChain.name)} )} {/* Destination Chain */} Destination Chain {shorterChainName(txInfo.chain.name)} {/* Transaction Info */} {txInfo.type === "paymentId" && ( Payment ID {shortenHex(txInfo.id)} )} {status.transactions.map((tx) => { const explorerLink = `https://thirdweb.com/${tx.chainId}/tx/${tx.transactionHash}`; return ( Transaction Hash {shortenHex(tx.transactionHash)} ); })} ); } interface PaymentReceitProps { /** * Prepared quote from Bridge.prepare */ preparedQuote: BridgePrepareResult; /** * Completed status results from step execution */ completedStatuses: CompletedStatusResult[]; /** * Called when user goes back to success screen */ onBack: () => void; /** * Window adapter for opening URLs */ windowAdapter: WindowAdapter; } export function PaymentReceipt({ preparedQuote, completedStatuses, onBack, windowAdapter, }: PaymentReceitProps) { return ( {/* Status Results */} {completedStatuses.map((status, index) => ( ))} ); }