Skip to content

Commit 0006e54

Browse files
fix: chain switching with in-app smart accounts (#3005)
1 parent 1ed1255 commit 0006e54

File tree

8 files changed

+59
-23
lines changed

8 files changed

+59
-23
lines changed

.changeset/sweet-spoons-move.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
- useSendTransaction now switches chain if needed
6+
- Fix chain switching with in-app smart accounts
7+
- Fix tx simulation for pay modal

packages/thirdweb/src/react/core/hooks/contract/useSendTransaction.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import { sendTransaction } from "../../../../transaction/actions/send-transactio
55
import type { WaitForReceiptOptions } from "../../../../transaction/actions/wait-for-tx-receipt.js";
66
import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js";
77
import { resolvePromisedValue } from "../../../../utils/promise/resolve-promised-value.js";
8+
import type { Account } from "../../../../wallets/interfaces/wallet.js";
89
import {
910
type GetWalletBalanceResult,
1011
getWalletBalance,
1112
} from "../../../../wallets/utils/getWalletBalance.js";
1213
import { fetchBuySupportedDestinations } from "../../../web/ui/ConnectWallet/screens/Buy/swap/useSwapSupportedChains.js";
13-
import { useActiveAccount } from "../wallets/wallet-hooks.js";
14+
import {
15+
useActiveAccount,
16+
useActiveWallet,
17+
useSwitchActiveWalletChain,
18+
} from "../wallets/wallet-hooks.js";
1419

1520
type ShowModalData = {
1621
tx: PreparedTransaction;
@@ -37,10 +42,21 @@ export function useSendTransactionCore(
3742
showPayModal?: (data: ShowModalData) => void,
3843
gasless?: GaslessOptions,
3944
): UseMutationResult<WaitForReceiptOptions, Error, PreparedTransaction> {
40-
const account = useActiveAccount();
45+
let _account = useActiveAccount();
46+
const wallet = useActiveWallet();
47+
const switchChain = useSwitchActiveWalletChain();
4148

4249
return useMutation({
4350
mutationFn: async (tx) => {
51+
// switch chain if needed
52+
if (wallet && tx.chain.id !== wallet.getChain()?.id) {
53+
await switchChain(tx.chain);
54+
// in smart wallet case, account may change after chain switch
55+
_account = wallet.getAccount();
56+
}
57+
58+
const account = _account;
59+
4460
if (!account) {
4561
throw new Error("No active account");
4662
}
@@ -90,7 +106,7 @@ export function useSendTransactionCore(
90106
chain: tx.chain,
91107
client: tx.client,
92108
}),
93-
getTotalTxCostForBuy(tx),
109+
getTotalTxCostForBuy(tx, account),
94110
]);
95111

96112
const walletBalanceWei = walletBalance.value;
@@ -122,9 +138,13 @@ export function useSendTransactionCore(
122138
});
123139
}
124140

125-
export async function getTotalTxCostForBuy(tx: PreparedTransaction) {
141+
export async function getTotalTxCostForBuy(
142+
tx: PreparedTransaction,
143+
account?: Account,
144+
) {
126145
const gasCost = await estimateGasCost({
127146
transaction: tx,
147+
account,
128148
});
129149

130150
const bufferCost = gasCost.wei / 10n;

packages/thirdweb/src/react/web/ui/ConnectWallet/ConnectButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function ConnectButton(props: ConnectButtonProps) {
102102
wallets: wallets,
103103
locale: props.locale || "en_US",
104104
connectLocale: localeQuery.data,
105-
chain: props.chain,
105+
chain: props.chain || props.accountAbstraction?.chain,
106106
chains: props.chains,
107107
walletConnect: props.walletConnect,
108108
accountAbstraction: props.accountAbstraction,

packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ export function ConnectEmbed(props: ConnectEmbedProps) {
389389
wallets: wallets,
390390
locale: localeId,
391391
connectLocale: localeQuery.data,
392-
chain: props.chain,
392+
chain: props.chain || props.accountAbstraction?.chain,
393393
chains: props.chains,
394394
walletConnect: props.walletConnect,
395395
accountAbstraction: props.accountAbstraction,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ function BuyScreenContent(props: BuyScreenContentProps) {
217217
buyForTx,
218218
hasEditedAmount,
219219
isMainScreen: screen.type === "main",
220+
account,
220221
});
221222

222223
// check if the screen is expanded or not

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useState } from "react";
22
import { formatNumber } from "../../../../../../../utils/formatNumber.js";
33
import { toEther } from "../../../../../../../utils/units.js";
4+
import type { Account } from "../../../../../../../wallets/interfaces/wallet.js";
45
import { getTotalTxCostForBuy } from "../../../../../../core/hooks/contract/useSendTransaction.js";
56
import { wait } from "../../../../../../core/utils/wait.js";
67
import type { BuyForTx } from "./types.js";
@@ -10,8 +11,10 @@ export function useBuyTxStates(options: {
1011
buyForTx?: BuyForTx;
1112
hasEditedAmount: boolean;
1213
isMainScreen: boolean;
14+
account?: Account;
1315
}) {
14-
const { buyForTx, hasEditedAmount, isMainScreen, setTokenAmount } = options;
16+
const { buyForTx, hasEditedAmount, isMainScreen, setTokenAmount, account } =
17+
options;
1518
const shouldRefreshTokenAmount = !hasEditedAmount && isMainScreen;
1619
const stopUpdatingAll = !isMainScreen;
1720

@@ -35,7 +38,7 @@ export function useBuyTxStates(options: {
3538
}
3639

3740
try {
38-
const totalCost = await getTotalTxCostForBuy(buyForTx.tx);
41+
const totalCost = await getTotalTxCostForBuy(buyForTx.tx, account);
3942

4043
if (!mounted) {
4144
return;
@@ -64,7 +67,13 @@ export function useBuyTxStates(options: {
6467
return () => {
6568
mounted = false;
6669
};
67-
}, [buyForTx, shouldRefreshTokenAmount, setTokenAmount, stopUpdatingAll]);
70+
}, [
71+
buyForTx,
72+
shouldRefreshTokenAmount,
73+
setTokenAmount,
74+
stopUpdatingAll,
75+
account,
76+
]);
6877

6978
return {
7079
amountNeeded,

packages/thirdweb/src/react/web/ui/TransactionButton/index.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ import {
88
import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js";
99
import type { TransactionReceipt } from "../../../../transaction/types.js";
1010
import { stringify } from "../../../../utils/json.js";
11-
import {
12-
useActiveAccount,
13-
useActiveWallet,
14-
useSwitchActiveWalletChain,
15-
} from "../../../core/hooks/wallets/wallet-hooks.js";
11+
import { useActiveAccount } from "../../../core/hooks/wallets/wallet-hooks.js";
1612
import {
1713
type SendTransactionPayModalConfig,
1814
useSendTransaction,
@@ -131,9 +127,7 @@ export function TransactionButton(props: TransactionButtonProps) {
131127
...buttonProps
132128
} = props;
133129
const account = useActiveAccount();
134-
const wallet = useActiveWallet();
135130
const [isPending, setIsPending] = useState(false);
136-
const switchChain = useSwitchActiveWalletChain();
137131

138132
const sendTransaction = useSendTransaction({
139133
gasless,
@@ -153,11 +147,6 @@ export function TransactionButton(props: TransactionButtonProps) {
153147
try {
154148
setIsPending(true);
155149
const resolvedTx = await transaction();
156-
157-
if (wallet && wallet.getChain()?.id !== resolvedTx.chain.id) {
158-
await switchChain(resolvedTx.chain);
159-
}
160-
161150
const result = await sendTransaction.mutateAsync(resolvedTx);
162151

163152
if (onTransactionSent) {

packages/thirdweb/src/wallets/in-app/core/wallet/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ export async function connectInAppWallet(
2525
client: options.client,
2626
authAccount,
2727
smartAccountOptions: createOptions.smartAccount,
28+
chain: options.chain,
2829
});
2930
}
3031

31-
return [authAccount, options.chain || ethereum] as const;
32+
return [
33+
authAccount,
34+
options.chain || createOptions?.smartAccount?.chain || ethereum,
35+
] as const;
3236
}
3337

3438
/**
@@ -51,16 +55,21 @@ export async function autoConnectInAppWallet(
5155
client: options.client,
5256
authAccount,
5357
smartAccountOptions: createOptions.smartAccount,
58+
chain: options.chain,
5459
});
5560
}
5661

57-
return [authAccount, options.chain || ethereum] as const;
62+
return [
63+
authAccount,
64+
options.chain || createOptions?.smartAccount?.chain || ethereum,
65+
] as const;
5866
}
5967

6068
async function convertToSmartAccount(options: {
6169
client: ThirdwebClient;
6270
authAccount: Account;
6371
smartAccountOptions: CreateWalletArgs<"smart">[1];
72+
chain?: Chain;
6473
}) {
6574
const [{ smartWallet }, { connectSmartWallet }] = await Promise.all([
6675
import("../../../create-wallet.js"),
@@ -73,6 +82,7 @@ async function convertToSmartAccount(options: {
7382
{
7483
client: options.client,
7584
personalAccount: options.authAccount,
85+
chain: options.chain,
7686
},
7787
options.smartAccountOptions,
7888
);

0 commit comments

Comments
 (0)