Skip to content

Commit 889a1a0

Browse files
authored
smart wallet autoconnect and other fixes (#2558)
1 parent 6d7eedc commit 889a1a0

File tree

7 files changed

+137
-109
lines changed

7 files changed

+137
-109
lines changed

packages/thirdweb/src/react/core/hooks/connection/useAutoConnect.tsx

Lines changed: 79 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import { asyncLocalStorage } from "../../utils/asyncLocalStorage.js";
2121
import type { ThirdwebClient } from "../../../../client/client.js";
2222
import type { AppMetadata } from "../../../../wallets/types.js";
2323
import { timeoutPromise } from "../../utils/timeoutPromise.js";
24+
import type { SmartWalletOptions } from "../../../../wallets/smart/types.js";
25+
import { createWallet } from "../../../../wallets/create-wallet.js";
26+
import { getSavedConnectParamsFromStorage } from "../../../../wallets/storage/walletStorage.js";
2427

2528
let autoConnectAttempted = false;
2629

@@ -94,6 +97,22 @@ export type AutoConnectProps = {
9497
* ```
9598
*/
9699
timeout?: number;
100+
101+
/**
102+
* Enable Account abstraction for all wallets. This will connect to the users's smart account based on the connected personal wallet and the given options.
103+
*
104+
* If you are connecting to smart wallet using personal wallet - setting this configuration will autoConnect the personal wallet and then connect to the smart wallet.
105+
*
106+
* ```tsx
107+
* <AutoConnect
108+
* accountAbstraction={{
109+
* factoryAddress: "0x123...",
110+
* chain: sepolia,
111+
* gasless: true;
112+
* }}
113+
* />
114+
*/
115+
accountAbstraction?: SmartWalletOptions;
97116
};
98117

99118
/**
@@ -128,7 +147,7 @@ export function AutoConnect(props: AutoConnectProps) {
128147
const setConnectionStatus = useSetActiveWalletConnectionStatus();
129148
const { connect } = useConnect();
130149
const { isAutoConnecting } = connectionManager;
131-
const { wallets } = props;
150+
const { wallets, accountAbstraction } = props;
132151
const timeout = props.timeout ?? 15000;
133152
// get the supported wallets from thirdweb provider
134153
// check the storage for last connected wallets and connect them all
@@ -153,92 +172,74 @@ export function AutoConnect(props: AutoConnectProps) {
153172

154173
async function handleWalletConnection(wallet: Wallet) {
155174
setConnectionStatus("connecting");
175+
return wallet.autoConnect({
176+
client: props.client,
177+
});
178+
}
156179

157-
// if this wallet requires a personal wallet to be connected
158-
// if (walletConfig.personalWalletConfigs) {
159-
// // get saved connection params for this wallet
160-
// const savedParams = await getSavedConnectParamsFromStorage(
161-
// asyncLocalStorage,
162-
// walletConfig.metadata.id,
163-
// );
164-
165-
// // if must be an object with `personalWalletId` property
166-
// if (!isValidWithPersonalWalletConnectionOptions(savedParams)) {
167-
// throw new Error("Invalid connection params");
168-
// }
169-
170-
// // find the personal wallet config
171-
// const personalWalletConfig = walletConfig.personalWalletConfigs.find(
172-
// (w) => w.metadata.id === savedParams.personalWalletId,
173-
// );
174-
175-
// if (!personalWalletConfig) {
176-
// throw new Error("Personal wallet not found");
177-
// }
178-
179-
// // create and auto connect the personal wallet to get personal account
180-
// const personalWallet = personalWalletConfig.create({
181-
// client,
182-
// appMetadata,
183-
// });
180+
if (lastActiveWalletId === "smart") {
181+
if (!accountAbstraction) {
182+
return;
183+
}
184184

185-
// const account = await personalWallet.autoConnect();
185+
const savedParams = await getSavedConnectParamsFromStorage(
186+
asyncLocalStorage,
187+
"accountAbstraction",
188+
);
186189

187-
// // create wallet
188-
// const wallet = walletConfig.create({
189-
// client,
190-
// appMetadata,
191-
// }) as WalletWithPersonalAccount;
190+
const personalWalletId =
191+
savedParams && "personalWalletId" in savedParams
192+
? savedParams.personalWalletId
193+
: null;
192194

193-
// // auto connect the wallet using the personal account
194-
// await wallet.autoConnect({
195-
// personalAccount: account,
196-
// });
195+
if (personalWalletId) {
196+
const personalWallet = wallets.find((w) => w.id === personalWalletId);
197197

198-
// return wallet;
199-
// }
198+
if (personalWallet) {
199+
try {
200+
const account = await timeoutPromise(
201+
handleWalletConnection(personalWallet),
202+
{
203+
ms: timeout,
204+
message:
205+
"AutoConnect timeout : " + timeout + "ms limit exceeded.",
206+
},
207+
);
200208

201-
if (false) {
202-
// do nothing
203-
}
209+
const smartWallet = createWallet("smart", accountAbstraction);
210+
await smartWallet.connect({
211+
personalAccount: account,
212+
client: props.client,
213+
});
204214

205-
// if this wallet does not require a personal wallet to be connected
206-
else {
207-
await wallet.autoConnect({
208-
client: props.client,
209-
});
210-
return wallet;
215+
connect(smartWallet);
216+
} catch (e) {
217+
console.error("Failed to auto connect personal wallet");
218+
console.error(e);
219+
setConnectionStatus("disconnected");
220+
}
221+
}
211222
}
212-
}
213-
214-
// connect the last active wallet and set it as active
215-
const activeWalletConfig = wallets.find(
216-
(w) => w.id === lastActiveWalletId,
217-
);
223+
} else {
224+
const activeWallet = wallets.find((w) => w.id === lastActiveWalletId);
218225

219-
if (activeWalletConfig) {
220-
try {
221-
const wallet = await timeoutPromise(
222-
handleWalletConnection(activeWalletConfig),
223-
{
226+
if (activeWallet) {
227+
try {
228+
await timeoutPromise(handleWalletConnection(activeWallet), {
224229
ms: timeout,
225230
message:
226231
"AutoConnect timeout : " + timeout + "ms limit exceeded.",
227-
},
228-
);
232+
});
229233

230-
if (wallet) {
231-
connect(wallet);
232-
} else {
234+
connect(activeWallet);
235+
} catch (e) {
236+
console.error("Failed to auto connect last active wallet");
237+
console.error(e);
233238
setConnectionStatus("disconnected");
234239
}
235-
} catch (e) {
236-
console.error("Failed to auto connect last active wallet");
237-
console.error(e);
240+
} else {
238241
setConnectionStatus("disconnected");
239242
}
240-
} else {
241-
setConnectionStatus("disconnected");
242243
}
243244

244245
// then connect wallets that were last connected but were not set as active
@@ -247,10 +248,14 @@ export function AutoConnect(props: AutoConnectProps) {
247248
w.id !== lastActiveWalletId && lastConnectedWalletIds.includes(w.id),
248249
);
249250

250-
otherWallets.forEach(async (config) => {
251-
const account = await handleWalletConnection(config);
252-
if (account) {
253-
connectionManager.addConnectedWallet(account);
251+
otherWallets.forEach(async (wallet) => {
252+
try {
253+
await handleWalletConnection(wallet);
254+
connectionManager.addConnectedWallet(wallet);
255+
} catch (e) {
256+
console.error("Failed to auto connect a non-active connected wallet");
257+
console.error(e);
258+
setConnectionStatus("disconnected");
254259
}
255260
});
256261
};

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export function ConnectButton(props: ConnectButtonProps) {
7777
? undefined
7878
: props.autoConnect?.timeout
7979
}
80+
accountAbstraction={props.accountAbstraction}
8081
/>
8182
);
8283

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,9 +820,11 @@ function EmbeddedWalletEmail() {
820820
const emailQuery = useQuery({
821821
queryKey: ["embedded-wallet-user", client],
822822
queryFn: async () => {
823-
return getUserEmail({
823+
const data = await getUserEmail({
824824
client: client,
825825
});
826+
827+
return data || null;
826828
},
827829
});
828830

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { ModalConfigCtx } from "../../../providers/wallet-ui-states-provider.js"
1717
import { createWallet } from "../../../../../wallets/create-wallet.js";
1818
import { useWalletInfo } from "../../hooks/useWalletInfo.js";
1919
import type { WalletInfo } from "../../../../../wallets/wallet-info.js";
20+
import { saveConnectParamsToStorage } from "../../../../../wallets/storage/walletStorage.js";
21+
import { asyncLocalStorage } from "../../../../core/utils/asyncLocalStorage.js";
2022

2123
/**
2224
* @internal
@@ -118,6 +120,10 @@ function SmartWalletConnecting(props: {
118120
client,
119121
});
120122

123+
saveConnectParamsToStorage(asyncLocalStorage, "accountAbstraction", {
124+
personalWalletId: personalWallet.id,
125+
});
126+
121127
done(smartWallet);
122128
setSmartWalletConnectionStatus("idle");
123129
} catch (e) {

scripts/hotlink/changes.mjs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import fs from "fs";
33
import path from "path";
44

5-
export const changes = [
5+
export const _legacyChanges = [
66
// react
77
{
8-
path: "./packages/react/package.json",
8+
path: "./legacy_packages/react/package.json",
99
entry: "./src/index.ts",
1010
exports: {
1111
".": "./src/index.ts",
@@ -14,7 +14,7 @@ export const changes = [
1414
},
1515
// react-core
1616
{
17-
path: "./packages/react-core/package.json",
17+
path: "./legacy_packages/react-core/package.json",
1818
entry: "./src/index.ts",
1919
exports: {
2020
".": "./src/index.ts",
@@ -23,7 +23,7 @@ export const changes = [
2323
},
2424
// wallets
2525
{
26-
path: "./packages/wallets/package.json",
26+
path: "./legacy_packages/wallets/package.json",
2727
entry: "./src/index.ts",
2828
exports: {
2929
".": "./src/index.ts",
@@ -58,6 +58,9 @@ export const changes = [
5858
"./evm/wallets/smart-wallet": "./src/evm/wallets/smart-wallet.ts",
5959
},
6060
},
61+
];
62+
63+
export const _changes = [
6164
// thirdweb v5 sdk
6265
{
6366
path: "./packages/thirdweb/package.json",
@@ -91,8 +94,10 @@ const absPath = (relativePath) => path.join(process.cwd(), relativePath);
9194
/**
9295
*
9396
* @param {"original" | 'hotlink'} changeKey
97+
* @param {boolean} isLegacy
9498
*/
95-
export function updatePackages(changeKey) {
99+
export function updatePackages(changeKey, isLegacy) {
100+
const changes = isLegacy ? _legacyChanges : _changes;
96101
changes.forEach((change) => {
97102
// read the package json file
98103
const pkg = JSON.parse(fs.readFileSync(absPath(change.path), "utf8"));

scripts/hotlink/hotlink-init.mjs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,34 @@ import { updatePackages } from "./changes.mjs";
44
import { execute } from "./execute.mjs";
55

66
async function script() {
7+
const isLegacy = process.argv[2] === "legacy";
78
console.log(banner);
89

910
console.log("\x1b[34m%s\x1b[0m", "Updating package json files");
10-
updatePackages("hotlink");
11+
updatePackages("hotlink", isLegacy);
1112

1213
console.log("\x1b[34m%s\x1b[0m", "Creating Symlinks");
13-
await execute([
14-
"cd packages/thirdweb",
15-
"pnpm link",
16-
"cd ../react",
17-
"pnpm link",
18-
"cd ../react-core",
19-
"pnpm link",
20-
"cd ../wallets",
21-
"pnpm link",
22-
"cd ../..",
23-
]);
14+
if (isLegacy) {
15+
await execute([
16+
"cd legacy_packages/react",
17+
"pnpm link",
18+
"cd ../react-core",
19+
"pnpm link",
20+
"cd ../wallets",
21+
"pnpm link",
22+
"cd ../..",
23+
]);
2424

25-
// use react-core and wallets link in react package
26-
await execute([
27-
"cd packages/react",
28-
"pnpm link @thirdweb-dev/react-core",
29-
"pnpm link @thirdweb-dev/wallets",
30-
"cd ../..",
31-
]);
25+
// use react-core and wallets link in react package
26+
await execute([
27+
"cd packages/react",
28+
"pnpm link @thirdweb-dev/react-core",
29+
"pnpm link @thirdweb-dev/wallets",
30+
"cd ../..",
31+
]);
32+
} else {
33+
await execute(["cd packages/thirdweb", "pnpm link", "cd ../../"]);
34+
}
3235

3336
// done
3437
console.log("\x1b[32m%s\x1b[0m", "\nHotlink Complete ✨\n\n");

scripts/hotlink/hotlink-revert.mjs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@ import { execute } from "./execute.mjs";
55

66
async function script() {
77
console.log(banner);
8+
const isLegacy = process.argv[2] === "legacy";
89

910
console.log("\x1b[34m%s\x1b[0m", "Reverting package json files");
10-
updatePackages("original");
11+
updatePackages("original", isLegacy);
1112

1213
console.log("\x1b[34m%s\x1b[0m", "Removing Symlinks");
13-
await execute([
14-
"cd packages/react",
15-
"pnpm unlink",
16-
"cd ../react-core",
17-
"pnpm unlink",
18-
"cd ../wallets",
19-
"pnpm unlink",
20-
"cd ../..",
21-
]);
14+
15+
if (isLegacy) {
16+
await execute([
17+
"cd legacy_packages/react",
18+
"pnpm unlink",
19+
"cd ../react-core",
20+
"pnpm unlink",
21+
"cd ../wallets",
22+
"pnpm unlink",
23+
"cd ../..",
24+
]);
25+
} else {
26+
await execute(["cd packages/thirdweb", "pnpm unlink"]);
27+
}
2228

2329
console.log("\x1b[32m%s\x1b[0m", "\nHotlink Reverted\n\n");
2430
}

0 commit comments

Comments
 (0)