From 1405598e7e51e4d415fd57f8169012823db0cedf Mon Sep 17 00:00:00 2001 From: Isaac Dubuque Date: Fri, 23 Aug 2024 16:45:23 -0700 Subject: [PATCH] adding api for pay direct transfers (#4224) Co-authored-by: Joaquim Verges Co-authored-by: Edward Sun Co-authored-by: Edward Sun --- .changeset/nice-fishes-vanish.md | 5 + .../src/components/pay/direct-payment.tsx | 8 +- .../src/components/pay/transaction-button.tsx | 8 +- packages/thirdweb/src/exports/pay.ts | 13 +- packages/thirdweb/src/exports/thirdweb.ts | 13 +- .../src/pay/buyWithCrypto/commonTypes.ts | 40 +++ .../src/pay/buyWithCrypto/getQuote.ts | 50 +-- .../src/pay/buyWithCrypto/getStatus.ts | 2 +- .../src/pay/buyWithCrypto/getTransfer.ts | 185 ++++++++++++ .../thirdweb/src/pay/utils/definitions.ts | 7 + .../ConnectWallet/screens/Buy/BuyScreen.tsx | 9 +- .../pay-transactions/SwapDetailsScreen.tsx | 26 ++ .../screens/Buy/swap/SwapStatusScreen.tsx | 6 +- .../Buy/swap/TransferConfirmationScreen.tsx | 284 ++++++++++++++++++ .../screens/Buy/swap/TransferFlow.tsx | 249 ++------------- 15 files changed, 627 insertions(+), 278 deletions(-) create mode 100644 .changeset/nice-fishes-vanish.md create mode 100644 packages/thirdweb/src/pay/buyWithCrypto/commonTypes.ts create mode 100644 packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts create mode 100644 packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx diff --git a/.changeset/nice-fishes-vanish.md b/.changeset/nice-fishes-vanish.md new file mode 100644 index 00000000000..ee56ae99941 --- /dev/null +++ b/.changeset/nice-fishes-vanish.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Added support for direct transfers in Pay diff --git a/apps/playground-web/src/components/pay/direct-payment.tsx b/apps/playground-web/src/components/pay/direct-payment.tsx index 3a3c359d1b6..9f9c9f03abc 100644 --- a/apps/playground-web/src/components/pay/direct-payment.tsx +++ b/apps/playground-web/src/components/pay/direct-payment.tsx @@ -1,6 +1,6 @@ "use client"; -import { base } from "thirdweb/chains"; +import { sepolia } from "thirdweb/chains"; import { PayEmbed, getDefaultToken } from "thirdweb/react"; import { THIRDWEB_CLIENT } from "../../lib/client"; import { StyledConnectButton } from "../styled-connect-button"; @@ -16,9 +16,9 @@ export function BuyMerchPreview() { payOptions={{ mode: "direct_payment", paymentInfo: { - amount: "15", - chain: base, - token: getDefaultToken(base, "USDC"), + amount: "0.1", + chain: sepolia, + token: getDefaultToken(sepolia, "USDC"), sellerAddress: "0xEb0effdFB4dC5b3d5d3aC6ce29F3ED213E95d675", }, metadata: { diff --git a/apps/playground-web/src/components/pay/transaction-button.tsx b/apps/playground-web/src/components/pay/transaction-button.tsx index 84e027d0889..07f836adcf4 100644 --- a/apps/playground-web/src/components/pay/transaction-button.tsx +++ b/apps/playground-web/src/components/pay/transaction-button.tsx @@ -2,12 +2,13 @@ import { useTheme } from "next-themes"; import { getContract } from "thirdweb"; -import { base, polygon } from "thirdweb/chains"; +import { polygon, sepolia } from "thirdweb/chains"; import { transfer } from "thirdweb/extensions/erc20"; import { claimTo, getNFT } from "thirdweb/extensions/erc1155"; import { PayEmbed, TransactionButton, + getDefaultToken, useActiveAccount, useReadContract, } from "thirdweb/react"; @@ -21,8 +22,9 @@ const nftContract = getContract({ }); const usdcContract = getContract({ - address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - chain: base, + // biome-ignore lint/style/noNonNullAssertion: its there + address: getDefaultToken(sepolia, "USDC")!.address, + chain: sepolia, client: THIRDWEB_CLIENT, }); diff --git a/packages/thirdweb/src/exports/pay.ts b/packages/thirdweb/src/exports/pay.ts index c8b463af4b9..c0fcf3003ef 100644 --- a/packages/thirdweb/src/exports/pay.ts +++ b/packages/thirdweb/src/exports/pay.ts @@ -1,11 +1,20 @@ export { getBuyWithCryptoQuote, type BuyWithCryptoQuote, - type QuoteApprovalParams, - type QuoteTokenInfo, type GetBuyWithCryptoQuoteParams, } from "../pay/buyWithCrypto/getQuote.js"; +export { + getBuyWithCryptoTransfer, + type BuyWithCryptoTransfer, + type GetBuyWithCryptoTransferParams, +} from "../pay/buyWithCrypto/getTransfer.js"; + +export { + type QuoteApprovalParams, + type QuoteTokenInfo, +} from "../pay/buyWithCrypto/commonTypes.js"; + export { getBuyWithCryptoStatus, type BuyWithCryptoStatus, diff --git a/packages/thirdweb/src/exports/thirdweb.ts b/packages/thirdweb/src/exports/thirdweb.ts index ca6c6bc97df..1b18eb9924d 100644 --- a/packages/thirdweb/src/exports/thirdweb.ts +++ b/packages/thirdweb/src/exports/thirdweb.ts @@ -166,11 +166,14 @@ export { toEther, toTokens, toUnits, toWei, fromGwei } from "../utils/units.js"; export { getBuyWithCryptoQuote, type BuyWithCryptoQuote, - type QuoteApprovalParams, - type QuoteTokenInfo, type GetBuyWithCryptoQuoteParams, } from "../pay/buyWithCrypto/getQuote.js"; +export { + type QuoteApprovalParams, + type QuoteTokenInfo, +} from "../pay/buyWithCrypto/commonTypes.js"; + export { getBuyWithCryptoStatus, type BuyWithCryptoStatus, @@ -183,6 +186,12 @@ export { type BuyWithCryptoHistoryParams, } from "../pay/buyWithCrypto/getHistory.js"; +export { + getBuyWithCryptoTransfer, + type GetBuyWithCryptoTransferParams, + type BuyWithCryptoTransfer, +} from "../pay/buyWithCrypto/getTransfer.js"; + export type { PayOnChainTransactionDetails, PayTokenInfo, diff --git a/packages/thirdweb/src/pay/buyWithCrypto/commonTypes.ts b/packages/thirdweb/src/pay/buyWithCrypto/commonTypes.ts new file mode 100644 index 00000000000..80c7895e411 --- /dev/null +++ b/packages/thirdweb/src/pay/buyWithCrypto/commonTypes.ts @@ -0,0 +1,40 @@ +import type { ApproveParams } from "../../extensions/erc20/write/approve.js"; +import type { BaseTransactionOptions } from "../../transaction/types.js"; + +export type QuoteTokenInfo = { + chainId: number; + tokenAddress: string; + decimals: number; + priceUSDCents: number; + name?: string; + symbol?: string; +}; + +export type QuoteTransactionRequest = { + data: string; + to: string; + value: string; + from: string; + chainId: number; + gasPrice: string; + gasLimit: string; +}; + +export type QuoteApprovalInfo = { + chainId: number; + tokenAddress: string; + spenderAddress: string; + amountWei: string; +}; + +export type QuotePaymentToken = { + token: QuoteTokenInfo; + amountWei: string; + amount: string; + amountUSDCents: number; +}; + +/** + * @buyCrypto + */ +export type QuoteApprovalParams = BaseTransactionOptions; diff --git a/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts b/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts index 99a08c83490..29511bcc4e0 100644 --- a/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts +++ b/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts @@ -2,14 +2,16 @@ import type { Hash } from "viem"; import { getCachedChain } from "../../chains/utils.js"; import type { ThirdwebClient } from "../../client/client.js"; import { getContract } from "../../contract/contract.js"; -import { - type ApproveParams, - approve, -} from "../../extensions/erc20/write/approve.js"; +import { approve } from "../../extensions/erc20/write/approve.js"; import type { PrepareTransactionOptions } from "../../transaction/prepare-transaction.js"; -import type { BaseTransactionOptions } from "../../transaction/types.js"; import { getClientFetch } from "../../utils/fetch.js"; import { getPayBuyWithCryptoQuoteEndpoint } from "../utils/definitions.js"; +import type { + QuoteApprovalInfo, + QuotePaymentToken, + QuoteTokenInfo, + QuoteTransactionRequest, +} from "./commonTypes.js"; /** * The parameters for [`getBuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithCryptoQuote) function @@ -107,40 +109,9 @@ export type GetBuyWithCryptoQuoteParams = { /** * @buyCrypto */ -export type QuoteTokenInfo = { - chainId: number; - tokenAddress: string; - decimals: number; - priceUSDCents: number; - name?: string; - symbol?: string; -}; - -type QuotePaymentToken = { - token: QuoteTokenInfo; - amountWei: string; - amount: string; - amountUSDCents: number; -}; - -type QuoteTransactionRequest = { - data: string; - to: string; - value: string; - from: string; - chainId: number; - gasPrice: string; - gasLimit: string; -}; - type BuyWithCryptoQuoteRouteResponse = { transactionRequest: QuoteTransactionRequest; - approval?: { - chainId: number; - tokenAddress: string; - spenderAddress: string; - amountWei: string; - }; + approval?: QuoteApprovalInfo; fromAddress: string; toAddress: string; @@ -173,11 +144,6 @@ type BuyWithCryptoQuoteRouteResponse = { bridge?: string; }; -/** - * @buyCrypto - */ -export type QuoteApprovalParams = BaseTransactionOptions; - /** * @buyCrypto */ diff --git a/packages/thirdweb/src/pay/buyWithCrypto/getStatus.ts b/packages/thirdweb/src/pay/buyWithCrypto/getStatus.ts index 11ff24e77df..1fb04c9515d 100644 --- a/packages/thirdweb/src/pay/buyWithCrypto/getStatus.ts +++ b/packages/thirdweb/src/pay/buyWithCrypto/getStatus.ts @@ -56,7 +56,7 @@ export type BuyWithCryptoSubStatuses = | "UNKNOWN_ERROR" | "REFUNDED"; -export type SwapType = "SAME_CHAIN" | "CROSS_CHAIN"; +export type SwapType = "SAME_CHAIN" | "CROSS_CHAIN" | "TRANSFER"; /** * The object returned by the [`getBuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithCryptoStatus) function to represent the status of a quoted transaction diff --git a/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts b/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts new file mode 100644 index 00000000000..bc4e089de89 --- /dev/null +++ b/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts @@ -0,0 +1,185 @@ +import type { Hash } from "viem"; +import { getCachedChain } from "../../chains/utils.js"; +import type { ThirdwebClient } from "../../client/client.js"; +import { getContract } from "../../contract/contract.js"; +import { approve } from "../../extensions/erc20/write/approve.js"; +import type { PrepareTransactionOptions } from "../../transaction/prepare-transaction.js"; +import type { Address } from "../../utils/address.js"; +import { getClientFetch } from "../../utils/fetch.js"; +import { getPayBuyWithCryptoTransferEndpoint } from "../utils/definitions.js"; +import type { + QuoteApprovalInfo, + QuotePaymentToken, + QuoteTokenInfo, + QuoteTransactionRequest, +} from "./commonTypes.js"; + +/** + * The parameters for [`getBuyWithCryptoTransfer`](https://portal.thirdweb.com/references/typescript/v5/getBuyWithCryptoTransfer) function + * It facilitates a token transfer. + * @buyCrypto + */ +export type GetBuyWithCryptoTransferParams = { + /** + * A client is the entry point to the thirdweb SDK. It is required for all other actions. + * + * You can create a client using the `createThirdwebClient` function. + * Refer to the [Creating a Client](https://portal.thirdweb.com/typescript/v5/client) documentation for more information. + * + */ + client: ThirdwebClient; + + /** + * The address of wallet that pays for the tokens. + */ + fromAddress: string; + + /** + * The address of the wallet where the tokens are sent + */ + toAddress: string; + + /** + * The chain id of the transfer token. + */ + chainId: number; + + /** + * The token address of the transfer token. + */ + tokenAddress: string; + + /** + * The amount of token to be transferred. + */ + amount: string; + + /** + * Extra details to store with the purchase. + * + * This details will be stored with the purchase and can be retrieved later via the status API or Webhook + */ + purchaseData?: object; +}; + +/** + * @buyCrypto + */ +type BuyWithCryptoTransferResponse = { + quoteId: string; + transactionRequest: QuoteTransactionRequest; + approval?: QuoteApprovalInfo; + fromAddress: string; + toAddress: string; + token: QuoteTokenInfo; + paymentToken: QuotePaymentToken; + processingFee: QuotePaymentToken; + estimatedGasCostUSDCents: number; +}; + +/** + * @buyCrypto + */ +export type BuyWithCryptoTransfer = { + transactionRequest: PrepareTransactionOptions; + approval?: PrepareTransactionOptions; + fromAddress: string; + toAddress: string; + paymentToken: QuotePaymentToken; + processingFee: QuotePaymentToken; + estimatedGasCostUSDCents: number; + client: ThirdwebClient; +}; + +/** + * Get a quote of type [`BuyWithCryptoTransfer`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoTransfer) to facilitate a token transfer transaction. + * Using this instead of a native transfer allows you to receive status and webhooks about successful or failed payments. + * + * Once you have the quote, you can use `prepareTransaction` and prepare the transaction for submission. + * @param params - object of type [`GetBuyWithCryptoTransferParams`](https://portal.thirdweb.com/references/typescript/v5/GetBuyWithCryptoTransferParams) + * @returns Object of type [`BuyWithCryptoTransfer`](https://portal.thirdweb.com/references/typescript/v5/BuyWithCryptoTransfer) which contains the information about the transfer + * @example + * + * ```ts + * import { getBuyWithCryptoTransfer } from "thirdweb/pay"; + * + * const transfer = await getBuyWithCryptoTransfer({ + * client, + * fromAddress: "0x...", // wallet address + * toAddress: "0x...", // recipient address - likely to be your wallet + * chainId: 10, // chain id of the token + * tokenAddress: "0x...", // address of the token + * amount: "10", // amount of token to transfer + * purchaseData: { // any metadata for you to attribute this purchase + * "customerId": "yourId" + * } + * }); + * ``` + * @buyCrypto + */ +export async function getBuyWithCryptoTransfer( + params: GetBuyWithCryptoTransferParams, +): Promise { + try { + const clientFetch = getClientFetch(params.client); + + const response = await clientFetch(getPayBuyWithCryptoTransferEndpoint(), { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + fromAddress: params.fromAddress, + toAddress: params.toAddress, + chainId: params.chainId, + tokenAddress: params.tokenAddress, + amount: params.amount, + purchaseData: params.purchaseData, + }), + }); + + if (!response.ok) { + const errorObj = await response.json(); + if (errorObj && "error" in errorObj) { + throw errorObj; + } + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data: BuyWithCryptoTransferResponse = (await response.json()).result; + + const transfer: BuyWithCryptoTransfer = { + transactionRequest: { + chain: getCachedChain(data.transactionRequest.chainId), + client: params.client, + data: data.transactionRequest.data as Hash, + to: data.transactionRequest.to as Address, + value: BigInt(data.transactionRequest.value), + gas: BigInt(data.transactionRequest.gasLimit), + }, + approval: data.approval + ? approve({ + contract: getContract({ + client: params.client, + address: data.approval.tokenAddress, + chain: getCachedChain(data.approval.chainId), + }), + spender: data.approval.spenderAddress as Address, + amountWei: BigInt(data.approval.amountWei), + }) + : undefined, + fromAddress: data.fromAddress, + toAddress: data.toAddress, + paymentToken: data.paymentToken, + processingFee: data.processingFee, + estimatedGasCostUSDCents: data.estimatedGasCostUSDCents, + client: params.client, + }; + + return transfer; + } catch (error) { + console.error("Error getting buy with crypto transfer", error); + throw error; + } +} diff --git a/packages/thirdweb/src/pay/utils/definitions.ts b/packages/thirdweb/src/pay/utils/definitions.ts index 2fa3e5ded02..367af66601b 100644 --- a/packages/thirdweb/src/pay/utils/definitions.ts +++ b/packages/thirdweb/src/pay/utils/definitions.ts @@ -20,6 +20,13 @@ export const getPayBuyWithCryptoStatusUrl = () => export const getPayBuyWithCryptoQuoteEndpoint = () => `${getPayBaseUrl()}/buy-with-crypto/quote/v1`; +/** + * Endpoint to get "Buy with Crypto" transfer. + * @internal + */ +export const getPayBuyWithCryptoTransferEndpoint = () => + `${getPayBaseUrl()}/buy-with-crypto/transfer/v1`; + /** * Endpoint to get a "Buy with Fiat" quote. * @internal diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx index c4185a71b7e..ea6ea61530b 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx @@ -325,12 +325,19 @@ function BuyScreenContent(props: BuyScreenContentProps) { onBack={goBack} payer={payer} client={props.client} - onDone={onDone} chain={toChain} token={toToken} tokenAmount={tokenAmount} receiverAddress={receiverAddress} transactionMode={props.payOptions.mode === "transaction"} + isEmbed={props.isEmbed} + onDone={onDone} + onTryAgain={() => { + setScreen({ + id: "buy-with-crypto", + }); + }} + onSuccess={onSwapSuccess} /> ); } diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/SwapDetailsScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/SwapDetailsScreen.tsx index 700947a9349..68acee5ffde 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/SwapDetailsScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/SwapDetailsScreen.tsx @@ -100,7 +100,9 @@ export function SwapTxDetailsTable( ) { let uiData: SwapTxDetailsData; let showStatusRow = true; + let isTransfer = false; if (props.type === "status") { + isTransfer = props.status.swapType === "TRANSFER"; const status = props.status; if (props.hideStatusRow) { showStatusRow = false; @@ -189,6 +191,30 @@ export function SwapTxDetailsTable( ); + if (isTransfer) { + return ( +
+ {/* source chain Tx hash link */} + {fromChainExplorers.explorers?.[0]?.url && sourceTxHash && ( + + View on {fromChainName.name} Explorer + + + )} +
+ ); + } + return (
{isPartialSuccess && gotToken ? ( diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapStatusScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapStatusScreen.tsx index 7b788e0b98e..839d6832f10 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapStatusScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapStatusScreen.tsx @@ -26,7 +26,7 @@ export function SwapStatusScreen(props: { onDone: () => void; transactionMode: boolean; isEmbed: boolean; - quote: BuyWithCryptoQuote; + quote: BuyWithCryptoQuote | undefined; onSuccess: ((status: BuyWithCryptoStatus) => void) | undefined; }) { const { onSuccess } = props; @@ -82,13 +82,13 @@ export function SwapStatusScreen(props: { hideStatusRow={true} client={props.client} /> - ) : ( + ) : props.quote ? ( - ); + ) : null; return ( diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx new file mode 100644 index 00000000000..459b1cdbff8 --- /dev/null +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx @@ -0,0 +1,284 @@ +import { CheckCircledIcon, CrossCircledIcon } from "@radix-ui/react-icons"; +import { useState } from "react"; +import type { Chain } from "../../../../../../../chains/types.js"; +import type { ThirdwebClient } from "../../../../../../../client/client.js"; +import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js"; +import { getContract } from "../../../../../../../contract/contract.js"; +import { transfer } from "../../../../../../../extensions/erc20/write/transfer.js"; +import { getBuyWithCryptoTransfer } from "../../../../../../../pay/buyWithCrypto/getTransfer.js"; +import { sendAndConfirmTransaction } from "../../../../../../../transaction/actions/send-and-confirm-transaction.js"; +import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js"; +import { prepareTransaction } from "../../../../../../../transaction/prepare-transaction.js"; +import { toWei } from "../../../../../../../utils/units.js"; +import { iconSize } from "../../../../../../core/design-system/index.js"; +import { useChainSymbol } from "../../../../../../core/hooks/others/useChainQuery.js"; +import { Spacer } from "../../../../components/Spacer.js"; +import { Spinner } from "../../../../components/Spinner.js"; +import { StepBar } from "../../../../components/StepBar.js"; +import { SwitchNetworkButton } from "../../../../components/SwitchNetwork.js"; +import { Container, Line, ModalHeader } from "../../../../components/basic.js"; +import { Button } from "../../../../components/buttons.js"; +import { Text } from "../../../../components/text.js"; +import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js"; +import { Step } from "../Stepper.js"; +import { WalletRow } from "../WalletSelectorButton.js"; +import { TokenInfoRow } from "../pay-transactions/TokenInfoRow.js"; +import type { PayerInfo } from "../types.js"; +import { ConnectorLine } from "./ConfirmationScreen.js"; + +type TrasnferConfirmationScreenProps = { + title: string; + onBack?: () => void; + setTransactionHash: (txHash: string) => void; + payer: PayerInfo; + receiverAddress: string; + client: ThirdwebClient; + onDone: () => void; + chain: Chain; + token: ERC20OrNativeToken; + tokenAmount: string; + transactionMode?: boolean; +}; + +export function TransferConfirmationScreen( + props: TrasnferConfirmationScreenProps, +) { + const { + title, + onBack, + receiverAddress, + client, + payer, + onDone, + chain, + token, + tokenAmount, + transactionMode, + setTransactionHash, + } = props; + const [step, setStep] = useState<"approve" | "transfer" | "execute">( + "transfer", + ); + const [status, setStatus] = useState<"idle" | "pending" | "error" | "done">( + "idle", + ); + const { symbol } = useChainSymbol(chain); + + return ( + + + + + {transactionMode && ( + <> + + + + {step === "transfer" + ? "Step 1 of 2 - Transfer funds" + : "Step 2 of 2 - Finalize transaction"} + + + + )} + + {/* Sender Address */} + + From + + + + + + + + {/* Receiver Address */} + + To + + + + + + + + {/* Token Info */} + + + + + {transactionMode && ( + <> + + + + + + + + + )} + + {status === "error" && ( + <> + + + + {step === "transfer" ? "Failed to Transfer" : "Failed to Execute"} + + + + + )} + + {!transactionMode && step === "execute" && status === "done" && ( + <> + + + + {"Payment completed"} + + + + + )} + + {/* Execute */} + {payer.chain.id !== chain.id ? ( + { + await props.payer.wallet.switchChain(chain); + }} + /> + ) : ( + + )} + + ); +} diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferFlow.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferFlow.tsx index fbfc399cc6d..22acfad3362 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferFlow.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferFlow.tsx @@ -1,28 +1,11 @@ -import { CheckCircledIcon, CrossCircledIcon } from "@radix-ui/react-icons"; import { useState } from "react"; import type { Chain } from "../../../../../../../chains/types.js"; import type { ThirdwebClient } from "../../../../../../../client/client.js"; -import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js"; -import { getContract } from "../../../../../../../contract/contract.js"; -import { transfer } from "../../../../../../../extensions/erc20/write/transfer.js"; -import { sendAndConfirmTransaction } from "../../../../../../../transaction/actions/send-and-confirm-transaction.js"; -import { prepareTransaction } from "../../../../../../../transaction/prepare-transaction.js"; -import { toWei } from "../../../../../../../utils/units.js"; -import { iconSize } from "../../../../../../core/design-system/index.js"; -import { useChainSymbol } from "../../../../../../core/hooks/others/useChainQuery.js"; -import { Spacer } from "../../../../components/Spacer.js"; -import { Spinner } from "../../../../components/Spinner.js"; -import { StepBar } from "../../../../components/StepBar.js"; -import { SwitchNetworkButton } from "../../../../components/SwitchNetwork.js"; -import { Container, Line, ModalHeader } from "../../../../components/basic.js"; -import { Button } from "../../../../components/buttons.js"; -import { Text } from "../../../../components/text.js"; -import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js"; -import { Step } from "../Stepper.js"; -import { WalletRow } from "../WalletSelectorButton.js"; -import { TokenInfoRow } from "../pay-transactions/TokenInfoRow.js"; +import type { BuyWithCryptoStatus } from "../../../../../../../pay/buyWithCrypto/getStatus.js"; +import type { ERC20OrNativeToken } from "../../nativeToken.js"; import type { PayerInfo } from "../types.js"; -import { ConnectorLine } from "./ConfirmationScreen.js"; +import { SwapStatusScreen } from "./SwapStatusScreen.js"; +import { TransferConfirmationScreen } from "./TransferConfirmationScreen.js"; type TrasnferFlowProps = { title: string; @@ -31,6 +14,9 @@ type TrasnferFlowProps = { receiverAddress: string; client: ThirdwebClient; onDone: () => void; + onTryAgain: () => void; + isEmbed: boolean; + onSuccess: ((status: BuyWithCryptoStatus) => void) | undefined; chain: Chain; token: ERC20OrNativeToken; tokenAmount: string; @@ -38,206 +24,29 @@ type TrasnferFlowProps = { }; export function TransferFlow(props: TrasnferFlowProps) { - const { - title, - onBack, - receiverAddress, - client, - payer, - onDone, - chain, - token, - tokenAmount, - transactionMode, - } = props; - const [step, setStep] = useState<"transfer" | "execute">("transfer"); - const [status, setStatus] = useState<"idle" | "pending" | "error" | "done">( - "idle", - ); - const { symbol } = useChainSymbol(chain); - - return ( - - - - - {transactionMode && ( - <> - - - - {step === "transfer" - ? "Step 1 of 2 - Transfer funds" - : "Step 2 of 2 - Finalize transaction"} - - - - )} - - {/* Sender Address */} - - From - - - - - - - - {/* Receiver Address */} - - To - - - - - - - - {/* Token Info */} - (); + + if (transferTxHash) { + return ( + + ); + } - - - {transactionMode && ( - <> - - - - - - - - - )} - - {status === "error" && ( - <> - - - - {step === "transfer" ? "Failed to Transfer" : "Failed to Execute"} - - - - - )} - - {!transactionMode && step === "execute" && status === "done" && ( - <> - - - - {"Payment completed"} - - - - - )} - - {/* Execute */} - {payer.chain.id !== chain.id ? ( - { - await props.payer.wallet.switchChain(chain); - }} - /> - ) : ( - - )} - + return ( + ); }