From 4a4d749409e3b393f8824aa8ee473aa27dc394f1 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Sat, 22 Mar 2025 23:42:36 +1300 Subject: [PATCH] [SDK] fix: Handle wallet chain mismatch on post onramp flow --- .changeset/quiet-melons-jump.md | 5 +++ .../src/components/pay/embed.tsx | 10 ----- .../screens/Buy/fiat/OnRampScreen.tsx | 35 ++++++++++++------ .../screens/Buy/swap/ConfirmationScreen.tsx | 37 +++++++++++++------ packages/thirdweb/src/wallets/smart/index.ts | 17 +++++++-- 5 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 .changeset/quiet-melons-jump.md diff --git a/.changeset/quiet-melons-jump.md b/.changeset/quiet-melons-jump.md new file mode 100644 index 00000000000..69b75588455 --- /dev/null +++ b/.changeset/quiet-melons-jump.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Handle wallet chain mismatch on post onramp flow diff --git a/apps/playground-web/src/components/pay/embed.tsx b/apps/playground-web/src/components/pay/embed.tsx index ed85fe3d6b5..18bba36327a 100644 --- a/apps/playground-web/src/components/pay/embed.tsx +++ b/apps/playground-web/src/components/pay/embed.tsx @@ -18,10 +18,6 @@ export function StyledPayEmbedPreview() {
{ const { fromToken, toToken, amount } = input; + const wallet = props.payer.wallet; + + // in case the wallet is not on the same chain as the fromToken, switch to it + if (wallet.getChain()?.id !== fromToken.chainId) { + await wallet.switchChain(getCachedChain(fromToken.chainId)); + } + + const account = wallet.getAccount(); + + if (!account) { + throw new Error("Payer wallet has no account"); + } + // always get a fresh quote before executing const quote = await getBuyWithCryptoQuote({ fromChainId: fromToken.chainId, @@ -641,12 +654,12 @@ function useSwapMutation(props: { toAmount: amount, toChainId: toToken.chainId, toTokenAddress: toToken.tokenAddress, - fromAddress: props.payer.account.address, - toAddress: props.payer.account.address, + fromAddress: account.address, + toAddress: account.address, client: props.client, }); - const canBatch = props.payer.account.sendBatchTransaction; + const canBatch = account.sendBatchTransaction; const tokenContract = getContract({ client: props.client, address: quote.swapDetails.fromToken.tokenAddress, @@ -656,14 +669,14 @@ function useSwapMutation(props: { quote.approvalData && (await allowance({ contract: tokenContract, - owner: props.payer.account.address, + owner: account.address, spender: quote.approvalData.spenderAddress, })) < BigInt(quote.approvalData.amountWei); if (approveTxRequired && quote.approvalData && !canBatch) { trackPayEvent({ event: "prompt_swap_approval", client: props.client, - walletAddress: props.payer.account.address, + walletAddress: account.address, walletType: props.payer.wallet.id, fromToken: quote.swapDetails.fromToken.tokenAddress, fromAmount: quote.swapDetails.fromAmountWei, @@ -680,7 +693,7 @@ function useSwapMutation(props: { }); const tx = await sendTransaction({ - account: props.payer.account, + account, transaction, }); @@ -689,7 +702,7 @@ function useSwapMutation(props: { trackPayEvent({ event: "swap_approval_success", client: props.client, - walletAddress: props.payer.account.address, + walletAddress: account.address, walletType: props.payer.wallet.id, fromToken: quote.swapDetails.fromToken.tokenAddress, fromAmount: quote.swapDetails.fromAmountWei, @@ -703,7 +716,7 @@ function useSwapMutation(props: { trackPayEvent({ event: "prompt_swap_execution", client: props.client, - walletAddress: props.payer.account.address, + walletAddress: account.address, walletType: props.payer.wallet.id, fromToken: quote.swapDetails.fromToken.tokenAddress, fromAmount: quote.swapDetails.fromAmountWei, @@ -723,12 +736,12 @@ function useSwapMutation(props: { }); _swapTx = await sendBatchTransaction({ - account: props.payer.account, + account, transactions: [approveTx, tx], }); } else { _swapTx = await sendTransaction({ - account: props.payer.account, + account, transaction: tx, }); } @@ -738,7 +751,7 @@ function useSwapMutation(props: { trackPayEvent({ event: "swap_execution_success", client: props.client, - walletAddress: props.payer.account.address, + walletAddress: account.address, walletType: props.payer.wallet.id, fromToken: quote.swapDetails.fromToken.tokenAddress, fromAmount: quote.swapDetails.fromAmountWei, diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx index 02fb5cac559..b14c201afee 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx @@ -160,6 +160,19 @@ export function SwapConfirmationScreen(props: { fullWidth disabled={status === "pending"} onClick={async () => { + const wallet = props.payer.wallet; + + // in case the wallet is not on the same chain as the fromToken, switch to it + if (wallet.getChain()?.id !== props.fromChain.id) { + await wallet.switchChain(props.fromChain); + } + + const account = wallet.getAccount(); + + if (!account) { + throw new Error("Payer wallet has no account"); + } + if (step === "approval" && props.quote.approvalData) { try { setStatus("pending"); @@ -167,8 +180,8 @@ export function SwapConfirmationScreen(props: { trackPayEvent({ event: "prompt_swap_approval", client: props.client, - walletAddress: props.payer.account.address, - walletType: props.payer.wallet.id, + walletAddress: account.address, + walletType: wallet.id, fromToken: props.quote.swapDetails.fromToken.tokenAddress, fromAmount: props.quote.swapDetails.fromAmountWei, toToken: props.quote.swapDetails.toToken.tokenAddress, @@ -188,7 +201,7 @@ export function SwapConfirmationScreen(props: { }); const tx = await sendTransaction({ - account: props.payer.account, + account: account, transaction, }); @@ -197,8 +210,8 @@ export function SwapConfirmationScreen(props: { trackPayEvent({ event: "swap_approval_success", client: props.client, - walletAddress: props.payer.account.address, - walletType: props.payer.wallet.id, + walletAddress: account.address, + walletType: wallet.id, fromToken: props.quote.swapDetails.fromToken.tokenAddress, fromAmount: props.quote.swapDetails.fromAmountWei, toToken: props.quote.swapDetails.toToken.tokenAddress, @@ -221,8 +234,8 @@ export function SwapConfirmationScreen(props: { trackPayEvent({ event: "prompt_swap_execution", client: props.client, - walletAddress: props.payer.account.address, - walletType: props.payer.wallet.id, + walletAddress: account.address, + walletType: wallet.id, fromToken: props.quote.swapDetails.fromToken.tokenAddress, fromAmount: props.quote.swapDetails.fromAmountWei, toToken: props.quote.swapDetails.toToken.tokenAddress, @@ -233,7 +246,7 @@ export function SwapConfirmationScreen(props: { const tx = props.quote.transactionRequest; let _swapTx: WaitForReceiptOptions; // check if we can batch approval and swap - const canBatch = props.payer.account.sendBatchTransaction; + const canBatch = account.sendBatchTransaction; if ( canBatch && props.quote.approvalData && @@ -250,12 +263,12 @@ export function SwapConfirmationScreen(props: { }); _swapTx = await sendBatchTransaction({ - account: props.payer.account, + account: account, transactions: [approveTx, tx], }); } else { _swapTx = await sendTransaction({ - account: props.payer.account, + account: account, transaction: tx, }); } @@ -263,8 +276,8 @@ export function SwapConfirmationScreen(props: { trackPayEvent({ event: "swap_execution_success", client: props.client, - walletAddress: props.payer.account.address, - walletType: props.payer.wallet.id, + walletAddress: account.address, + walletType: wallet.id, fromToken: props.quote.swapDetails.fromToken.tokenAddress, fromAmount: props.quote.swapDetails.fromAmountWei, toToken: props.quote.swapDetails.toToken.tokenAddress, diff --git a/packages/thirdweb/src/wallets/smart/index.ts b/packages/thirdweb/src/wallets/smart/index.ts index 0adcc53e5d7..9f26a788fbb 100644 --- a/packages/thirdweb/src/wallets/smart/index.ts +++ b/packages/thirdweb/src/wallets/smart/index.ts @@ -264,11 +264,12 @@ async function createSmartAccount( executeOverride: options.overrides?.execute, }); + const chain = getCachedChain(transaction.chainId); const result = await _sendUserOp({ executeTx, options: { ...options, - chain: getCachedChain(transaction.chainId), + chain, accountContract, overrides: { ...options.overrides, @@ -278,7 +279,7 @@ async function createSmartAccount( }); trackTransaction({ client: options.client, - chainId: options.chain.id, + chainId: chain.id, transactionHash: result.transactionHash, walletAddress: options.accountContract.address, walletType: "smart", @@ -292,17 +293,25 @@ async function createSmartAccount( transactions, executeBatchOverride: options.overrides?.executeBatch, }); + if (transactions.length === 0) { + throw new Error("No transactions to send"); + } + const firstTx = transactions[0]; + if (!firstTx) { + throw new Error("No transactions to send"); + } + const chain = getCachedChain(firstTx.chainId); const result = await _sendUserOp({ executeTx, options: { ...options, - chain: getCachedChain(transactions[0]?.chainId ?? options.chain.id), + chain, accountContract, }, }); trackTransaction({ client: options.client, - chainId: options.chain.id, + chainId: chain.id, transactionHash: result.transactionHash, walletAddress: options.accountContract.address, walletType: "smart",