Skip to content

Commit 97c9ab7

Browse files
[SDK] feat: Batch approvals and swaps for smart wallets (#6209)
1 parent b73fa4b commit 97c9ab7

File tree

10 files changed

+51
-11
lines changed

10 files changed

+51
-11
lines changed

.changeset/dull-lamps-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Batch approvals and swaps if using smart wallets

packages/thirdweb/src/pay/buyWithCrypto/getStatus.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type BuyWithCryptoQuoteSummary = {
4343
export type BuyWithCryptoTransaction = {
4444
client: ThirdwebClient;
4545
transactionHash: string;
46+
chainId: number; // optional for backwards compatibility
4647
};
4748

4849
type BuyWithCryptoStatuses = "NONE" | "PENDING" | "FAILED" | "COMPLETED";
@@ -133,6 +134,7 @@ export async function getBuyWithCryptoStatus(
133134
}
134135
const queryString = new URLSearchParams({
135136
transactionHash: buyWithCryptoTransaction.transactionHash,
137+
chainId: buyWithCryptoTransaction.chainId.toString(),
136138
}).toString();
137139
const url = `${getPayBuyWithCryptoStatusUrl()}?${queryString}`;
138140

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/SwapDetailsScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export function SwapDetailsScreen(props: {
3434
? {
3535
client: client,
3636
transactionHash: initialStatus.source.transactionHash,
37+
chainId: initialStatus.source.token.chainId,
3738
}
3839
: undefined,
3940
);

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/pay-transactions/useBuyTransactionsToShow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function useBuyTransactionsToShow(client: ThirdwebClient) {
5757
const swapStatus = await getBuyWithCryptoStatus({
5858
client: client,
5959
transactionHash: tx.txHash,
60+
chainId: tx.chainId,
6061
});
6162

6263
if (

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ import type { ThirdwebClient } from "../../../../../../../client/client.js";
66
import { getContract } from "../../../../../../../contract/contract.js";
77
import { approve } from "../../../../../../../extensions/erc20/write/approve.js";
88
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
9+
import { sendBatchTransaction } from "../../../../../../../transaction/actions/send-batch-transaction.js";
910
import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js";
10-
import { waitForReceipt } from "../../../../../../../transaction/actions/wait-for-tx-receipt.js";
11+
import {
12+
type WaitForReceiptOptions,
13+
waitForReceipt,
14+
} from "../../../../../../../transaction/actions/wait-for-tx-receipt.js";
1115
import { shortenAddress } from "../../../../../../../utils/address.js";
1216
import { formatNumber } from "../../../../../../../utils/formatNumber.js";
1317
import { useCustomTheme } from "../../../../../../core/design-system/CustomThemeProvider.js";
@@ -55,11 +59,13 @@ export function SwapConfirmationScreen(props: {
5559
payer: PayerInfo;
5660
preApprovedAmount?: bigint;
5761
}) {
58-
const needsApproval =
62+
const approveTxRequired =
5963
props.quote.approvalData &&
6064
props.preApprovedAmount !== undefined &&
6165
props.preApprovedAmount < BigInt(props.quote.approvalData.amountWei);
62-
const initialStep = needsApproval ? "approval" : "swap";
66+
const needsApprovalStep =
67+
approveTxRequired && !props.payer.account.sendBatchTransaction;
68+
const initialStep = needsApprovalStep ? "approval" : "swap";
6369

6470
const [step, setStep] = useState<"approval" | "swap">(initialStep);
6571
const [status, setStatus] = useState<
@@ -142,7 +148,7 @@ export function SwapConfirmationScreen(props: {
142148
<Spacer y="xl" />
143149

144150
{/* Show 2 steps - Approve and confirm */}
145-
{needsApproval && (
151+
{needsApprovalStep && (
146152
<>
147153
<Spacer y="sm" />
148154
<Container
@@ -251,8 +257,6 @@ export function SwapConfirmationScreen(props: {
251257
if (step === "swap") {
252258
setStatus("pending");
253259
try {
254-
const tx = props.quote.transactionRequest;
255-
256260
trackPayEvent({
257261
event: "prompt_swap_execution",
258262
client: props.client,
@@ -265,11 +269,31 @@ export function SwapConfirmationScreen(props: {
265269
chainId: props.quote.swapDetails.fromToken.chainId,
266270
dstChainId: props.quote.swapDetails.toToken.chainId,
267271
});
272+
const tx = props.quote.transactionRequest;
273+
let _swapTx: WaitForReceiptOptions;
274+
// check if we can batch approval and swap
275+
const canBatch = props.payer.account.sendBatchTransaction;
276+
if (canBatch && props.quote.approvalData && approveTxRequired) {
277+
const approveTx = approve({
278+
contract: getContract({
279+
client: props.client,
280+
address: props.quote.swapDetails.fromToken.tokenAddress,
281+
chain: props.fromChain,
282+
}),
283+
spender: props.quote.approvalData.spenderAddress,
284+
amountWei: BigInt(props.quote.approvalData.amountWei),
285+
});
268286

269-
const _swapTx = await sendTransaction({
270-
account: props.payer.account,
271-
transaction: tx,
272-
});
287+
_swapTx = await sendBatchTransaction({
288+
account: props.payer.account,
289+
transactions: [approveTx, tx],
290+
});
291+
} else {
292+
_swapTx = await sendTransaction({
293+
account: props.payer.account,
294+
transaction: tx,
295+
});
296+
}
273297

274298
await waitForReceipt({ ..._swapTx, maxBlocksWaitTime: 50 });
275299

@@ -291,6 +315,7 @@ export function SwapConfirmationScreen(props: {
291315
addPendingTx({
292316
type: "swap",
293317
txHash: _swapTx.transactionHash,
318+
chainId: _swapTx.chain.id,
294319
});
295320
}
296321

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function FiatValue(
4545
<Text {...props}>
4646
{cryptoToFiatQuery.data?.result
4747
? `$${formatNumber(cryptoToFiatQuery.data.result, 2)}`
48-
: "$0.00"}
48+
: "-"}
4949
</Text>
5050
);
5151
}

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function SwapFlow(props: SwapFlowProps) {
8282
onBack={props.onBack}
8383
onTryAgain={props.onTryAgain}
8484
swapTxHash={swapTxHash}
85+
fromChain={fromChain}
8586
client={props.client}
8687
onDone={props.onDone}
8788
transactionMode={props.transactionMode}

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapStatusScreen.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CheckCircledIcon } from "@radix-ui/react-icons";
22
import { useQueryClient } from "@tanstack/react-query";
33
import { useEffect, useRef } from "react";
4+
import type { Chain } from "../../../../../../../chains/types.js";
45
import type { ThirdwebClient } from "../../../../../../../client/client.js";
56
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
67
import type { BuyWithCryptoStatus } from "../../../../../../../pay/buyWithCrypto/getStatus.js";
@@ -21,6 +22,7 @@ export function SwapStatusScreen(props: {
2122
title: string;
2223
onBack?: () => void;
2324
swapTxHash: string;
25+
fromChain: Chain;
2426
client: ThirdwebClient;
2527
onTryAgain: () => void;
2628
onDone: () => void;
@@ -34,6 +36,7 @@ export function SwapStatusScreen(props: {
3436
const swapStatus = useBuyWithCryptoStatus({
3537
client: props.client,
3638
transactionHash: props.swapTxHash,
39+
chainId: props.fromChain.id,
3740
});
3841

3942
let uiStatus: UIStatus = "pending";

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferFlow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function TransferFlow(props: TransferFlowProps) {
3535
onBack={props.onBack}
3636
onTryAgain={props.onTryAgain}
3737
swapTxHash={transferTxHash}
38+
fromChain={props.chain}
3839
client={props.client}
3940
onDone={props.onDone}
4041
transactionMode={false}

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/pendingSwapTx.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ type PendingTxInfo =
44
| {
55
type: "swap";
66
txHash: string;
7+
chainId: number;
78
}
89
| {
910
type: "fiat";

0 commit comments

Comments
 (0)