Skip to content

Commit 97b1a7d

Browse files
committed
fix(sdk): disconnect smart wallet when signer is disconnected
1 parent 9d8ecce commit 97b1a7d

File tree

9 files changed

+70
-9
lines changed

9 files changed

+70
-9
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,13 @@ function DetailsModal(props: {
343343
props.onDisconnect(info);
344344
}
345345

346+
useEffect(() => {
347+
if (!activeAccount) {
348+
setIsOpen(false);
349+
props.closeModal();
350+
}
351+
}, [activeAccount, props.closeModal]);
352+
346353
const networkSwitcherButton = (
347354
<MenuButton
348355
type="button"

packages/thirdweb/src/react/web/ui/prebuilt/Account/address.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export function AccountAddress({
5959
...restProps
6060
}: AccountAddressProps) {
6161
const { address } = useAccountContext();
62+
if (!address) {
63+
return null;
64+
}
6265
const value = formatFn ? formatFn(address) : address;
6366
return <span {...restProps}>{value}</span>;
6467
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/avatar.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ export function AccountAvatar({
166166
const avatarQuery = useQuery({
167167
queryKey: ["account-avatar", address],
168168
queryFn: async (): Promise<string> => {
169+
if (!address) {
170+
throw new Error(
171+
"No address found, please make sure you only use AccountAvatar when an account is active",
172+
);
173+
}
169174
const [socialData, ensName] = await Promise.all([
170175
getSocialProfiles({ address, client }),
171176
resolveName({

packages/thirdweb/src/react/web/ui/prebuilt/Account/balance.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ export function AccountBalance({
172172
if (!client) {
173173
throw new Error("client is required");
174174
}
175+
if (!address) {
176+
throw new Error(
177+
"Can not fetch balance without an address, please ensure AccountBalance is only rendered when an address exists",
178+
);
179+
}
175180
return getWalletBalance({
176181
chain: chainToLoad,
177182
client,

packages/thirdweb/src/react/web/ui/prebuilt/Account/blobbie.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ import { useAccountContext } from "./provider.js";
99
*/
1010
export function AccountBlobbie(props: Omit<BlobbieProps, "address">) {
1111
const { address } = useAccountContext();
12+
if (!address) {
13+
throw new Error(
14+
"Can not render AccountBlobbie without an address, please ensure AccountBlobbie is only rendered when an address exists",
15+
);
16+
}
1217
return <Blobbie {...props} address={address} />;
1318
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/name.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ export function AccountName({
140140
const nameQuery = useQuery({
141141
queryKey: ["account-name", address],
142142
queryFn: async () => {
143+
if (!address) {
144+
throw new Error(
145+
"Can not resolve account name without an address, please ensure AccountName is only rendered when an address exists",
146+
);
147+
}
148+
143149
const [socialData, ensName] = await Promise.all([
144150
getSocialProfiles({ address, client }),
145151
resolveName({

packages/thirdweb/src/react/web/ui/prebuilt/Account/provider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type AccountProviderProps = {
1313
/**
1414
* The user's wallet address
1515
*/
16-
address: Address;
16+
address: Address | undefined;
1717
/**
1818
* thirdweb Client
1919
*/

packages/thirdweb/src/wallets/manager/connection-manager.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
4444
await manager.connect(wallet, { client, onConnect });
4545

4646
expect(onConnect).toHaveBeenCalledWith(wallet);
47-
expect(storage.setItem).toHaveBeenCalledWith(expect.any(String), wallet.id);
47+
expect(storage.setItem).toHaveBeenCalled();
4848
});
4949

5050
it("handleConnection should connect smart wallet", async () => {
@@ -65,4 +65,22 @@ describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
6565

6666
expect(manager.connectedWallets.getValue()).toContain(wallet);
6767
});
68+
69+
it("should disconnect the active wallet when the EOA disconnects", async () => {
70+
const manager = createConnectionManager(storage);
71+
72+
const smartWallet = await manager.handleConnection(wallet, {
73+
client,
74+
accountAbstraction: smartWalletOptions,
75+
});
76+
77+
expect(manager.activeWalletStore.getValue()).toBe(smartWallet);
78+
79+
await wallet.disconnect();
80+
81+
expect(wallet.subscribe).toHaveBeenCalledWith(
82+
"disconnect",
83+
expect.any(Function),
84+
);
85+
});
6886
});

packages/thirdweb/src/wallets/manager/index.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export function createConnectionManager(storage: AsyncStorage) {
131131
const activeWallet = await (async () => {
132132
if (options?.accountAbstraction && !hasSmartAccount(wallet)) {
133133
return await handleSmartWalletConnection(
134-
account,
134+
wallet,
135135
options.client,
136136
options.accountAbstraction,
137137
);
@@ -140,13 +140,9 @@ export function createConnectionManager(storage: AsyncStorage) {
140140
}
141141
})();
142142

143-
// add personal wallet to connected wallets list
143+
// add personal wallet to connected wallets list even if it's not the active one
144144
addConnectedWallet(wallet);
145145

146-
if (wallet.id !== "smart") {
147-
await storage.setItem(LAST_ACTIVE_EOA_ID, wallet.id);
148-
}
149-
150146
handleSetActiveWallet(activeWallet);
151147

152148
wallet.subscribe("accountChanged", async () => {
@@ -159,10 +155,15 @@ export function createConnectionManager(storage: AsyncStorage) {
159155
};
160156

161157
const handleSmartWalletConnection = async (
162-
signer: Account,
158+
eoaWallet: Wallet,
163159
client: ThirdwebClient,
164160
options: SmartWalletOptions,
165161
) => {
162+
const signer = eoaWallet.getAccount();
163+
if (!signer) {
164+
throw new Error("Can not set a wallet without an account as active");
165+
}
166+
166167
const wallet = smartWallet(options);
167168

168169
await wallet.connect({
@@ -171,6 +172,17 @@ export function createConnectionManager(storage: AsyncStorage) {
171172
chain: options.chain,
172173
});
173174

175+
await storage.setItem(LAST_ACTIVE_EOA_ID, eoaWallet.id);
176+
177+
// Disconnect the active wallet when the EOA disconnects if it the active wallet is a smart wallet
178+
const disconnectUnsub = eoaWallet.subscribe("disconnect", () => {
179+
handleDisconnect();
180+
});
181+
const handleDisconnect = () => {
182+
disconnectUnsub();
183+
onWalletDisconnect(wallet);
184+
};
185+
174186
return wallet;
175187
};
176188

0 commit comments

Comments
 (0)