Skip to content

Commit 56c139c

Browse files
joaquim-vergesMananTankjnsdls
authored
feat: Add account abstraction options to useConnect (#2853)
Signed-off-by: Jonas Daniels <jonas.daniels@outlook.com> Co-authored-by: Manan Tank <manantankm@gmail.com> Co-authored-by: Jonas Daniels <jonas.daniels@outlook.com>
1 parent 03d8d6c commit 56c139c

File tree

10 files changed

+146
-189
lines changed

10 files changed

+146
-189
lines changed

.changeset/tricky-clocks-wonder.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Add account abstraction options to `useConnect` and handle smart wallet autoconnection outside of UI components

packages/service-utils/src/core/services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const SERVICE_DEFINITIONS = {
3939
},
4040
embeddedWallets: {
4141
name: "embeddedWallets",
42-
title: "Embedded Wallets",
42+
title: "In-App Wallets",
4343
description: "E-mail and social login wallets for easy web3 onboarding",
4444
// all actions allowed
4545
actions: [],

packages/thirdweb/package.json

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -109,54 +109,22 @@
109109
},
110110
"typesVersions": {
111111
"*": {
112-
"adapters/*": [
113-
"./dist/types/exports/adapters/*.d.ts"
114-
],
115-
"auth": [
116-
"./dist/types/exports/auth.d.ts"
117-
],
118-
"chains": [
119-
"./dist/types/exports/chains.d.ts"
120-
],
121-
"contract": [
122-
"./dist/types/exports/contract.d.ts"
123-
],
124-
"deploys": [
125-
"./dist/types/exports/deploys.d.ts"
126-
],
127-
"event": [
128-
"./dist/types/exports/event.d.ts"
129-
],
130-
"extensions/*": [
131-
"./dist/types/exports/extensions/*.d.ts"
132-
],
133-
"pay": [
134-
"./dist/types/exports/pay.d.ts"
135-
],
136-
"react": [
137-
"./dist/types/exports/react.d.ts"
138-
],
139-
"react-native": [
140-
"./dist/types/exports/react-native.d.ts"
141-
],
142-
"rpc": [
143-
"./dist/types/exports/rpc.d.ts"
144-
],
145-
"storage": [
146-
"./dist/types/exports/storage.d.ts"
147-
],
148-
"transaction": [
149-
"./dist/types/exports/transaction.d.ts"
150-
],
151-
"utils": [
152-
"./dist/types/exports/utils.d.ts"
153-
],
154-
"wallets": [
155-
"./dist/types/exports/wallets.d.ts"
156-
],
157-
"wallets/*": [
158-
"./dist/types/exports/wallets/*.d.ts"
159-
]
112+
"adapters/*": ["./dist/types/exports/adapters/*.d.ts"],
113+
"auth": ["./dist/types/exports/auth.d.ts"],
114+
"chains": ["./dist/types/exports/chains.d.ts"],
115+
"contract": ["./dist/types/exports/contract.d.ts"],
116+
"deploys": ["./dist/types/exports/deploys.d.ts"],
117+
"event": ["./dist/types/exports/event.d.ts"],
118+
"extensions/*": ["./dist/types/exports/extensions/*.d.ts"],
119+
"pay": ["./dist/types/exports/pay.d.ts"],
120+
"react": ["./dist/types/exports/react.d.ts"],
121+
"react-native": ["./dist/types/exports/react-native.d.ts"],
122+
"rpc": ["./dist/types/exports/rpc.d.ts"],
123+
"storage": ["./dist/types/exports/storage.d.ts"],
124+
"transaction": ["./dist/types/exports/transaction.d.ts"],
125+
"utils": ["./dist/types/exports/utils.d.ts"],
126+
"wallets": ["./dist/types/exports/wallets.d.ts"],
127+
"wallets/*": ["./dist/types/exports/wallets/*.d.ts"]
160128
}
161129
},
162130
"browser": {

packages/thirdweb/src/exports/react.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export {
5454
useIsAutoConnecting,
5555
} from "../react/core/hooks/wallets/wallet-hooks.js";
5656

57+
export type { ConnectManagerOptions } from "../wallets/manager/index.js";
58+
5759
// contract related
5860
export { useReadContract } from "../react/core/hooks/contract/useReadContract.js";
5961
export {

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

Lines changed: 21 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
import { useEffect } from "react";
22
import type { ThirdwebClient } from "../../../../client/client.js";
3-
import { createWallet } from "../../../../wallets/create-wallet.js";
4-
// import {
5-
// getSavedConnectParamsFromStorage,
6-
// type WithPersonalWalletConnectionOptions,
7-
// } from "../../../../wallets/storage/walletStorage.js";
8-
import type {
9-
Wallet,
10-
// WalletWithPersonalAccount,
11-
} from "../../../../wallets/interfaces/wallet.js";
3+
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
124
import {
135
getLastConnectedChain,
146
getStoredActiveWalletId,
157
getStoredConnectedWalletIds,
168
} from "../../../../wallets/manager/index.js";
179
import type { SmartWalletOptions } from "../../../../wallets/smart/types.js";
1810
import { asyncLocalStorage } from "../../../../wallets/storage/asyncLocalStorage.js";
19-
import { getSavedConnectParamsFromStorage } from "../../../../wallets/storage/walletStorage.js";
2011
import type { AppMetadata } from "../../../../wallets/types.js";
2112
import { connectionManager } from "../../connectionManager.js";
2213
import { timeoutPromise } from "../../utils/timeoutPromise.js";
23-
// import type { WalletConfig } from "../../types/wallets.js";
2414
import {
2515
useConnect,
2616
useSetActiveWalletConnectionStatus,
@@ -154,9 +144,12 @@ export type AutoConnectProps = {
154144
*/
155145
export function AutoConnect(props: AutoConnectProps) {
156146
const setConnectionStatus = useSetActiveWalletConnectionStatus();
157-
const { connect } = useConnect();
147+
const { connect } = useConnect({
148+
client: props.client,
149+
accountAbstraction: props.accountAbstraction,
150+
});
158151
const { isAutoConnecting } = connectionManager;
159-
const { wallets, accountAbstraction } = props;
152+
const { wallets } = props;
160153
const timeout = props.timeout ?? 15000;
161154
// get the supported wallets from thirdweb provider
162155
// check the storage for last connected wallets and connect them all
@@ -182,74 +175,31 @@ export function AutoConnect(props: AutoConnectProps) {
182175
const lastConnectedChain = await getLastConnectedChain(asyncLocalStorage);
183176

184177
async function handleWalletConnection(wallet: Wallet) {
185-
setConnectionStatus("connecting");
186178
return wallet.autoConnect({
187179
client: props.client,
188180
chain: lastConnectedChain ?? undefined,
189181
});
190182
}
191183

192-
if (lastActiveWalletId === "smart") {
193-
if (!accountAbstraction) {
194-
return;
195-
}
196-
197-
const savedParams = await getSavedConnectParamsFromStorage(
198-
asyncLocalStorage,
199-
"accountAbstraction",
200-
);
201-
202-
const personalWalletId =
203-
savedParams && "personalWalletId" in savedParams
204-
? savedParams.personalWalletId
205-
: null;
184+
const activeWallet =
185+
lastActiveWalletId && wallets.find((w) => w.id === lastActiveWalletId);
206186

207-
if (personalWalletId) {
208-
const personalWallet = wallets.find((w) => w.id === personalWalletId);
209-
210-
if (personalWallet) {
211-
try {
212-
const account = await timeoutPromise(
213-
handleWalletConnection(personalWallet),
214-
{
215-
ms: timeout,
216-
message: `AutoConnect timeout : ${timeout}ms limit exceeded.`,
217-
},
218-
);
219-
220-
const smartWallet = createWallet("smart", accountAbstraction);
221-
await smartWallet.connect({
222-
personalAccount: account,
223-
client: props.client,
224-
});
225-
226-
connect(smartWallet);
227-
} catch (e) {
228-
console.error("Failed to auto connect personal wallet");
229-
console.error(e);
230-
setConnectionStatus("disconnected");
231-
}
232-
}
233-
}
234-
} else {
235-
const activeWallet = wallets.find((w) => w.id === lastActiveWalletId);
236-
237-
if (activeWallet) {
238-
try {
239-
await timeoutPromise(handleWalletConnection(activeWallet), {
240-
ms: timeout,
241-
message: `AutoConnect timeout : ${timeout}ms limit exceeded.`,
242-
});
187+
if (activeWallet) {
188+
try {
189+
setConnectionStatus("connecting"); // only set connecting status if we are connecting the last active EOA
190+
await timeoutPromise(handleWalletConnection(activeWallet), {
191+
ms: timeout,
192+
message: `AutoConnect timeout : ${timeout}ms limit exceeded.`,
193+
});
243194

244-
connect(activeWallet);
245-
} catch (e) {
246-
console.error("Failed to auto connect last active wallet");
247-
console.error(e);
248-
setConnectionStatus("disconnected");
249-
}
250-
} else {
195+
connect(activeWallet);
196+
} catch (e) {
197+
console.error("Failed to auto connect last active wallet");
198+
console.error(e);
251199
setConnectionStatus("disconnected");
252200
}
201+
} else {
202+
setConnectionStatus("disconnected");
253203
}
254204

255205
// then connect wallets that were last connected but were not set as active
@@ -265,7 +215,6 @@ export function AutoConnect(props: AutoConnectProps) {
265215
} catch (e) {
266216
console.error("Failed to auto connect a non-active connected wallet");
267217
console.error(e);
268-
setConnectionStatus("disconnected");
269218
}
270219
}
271220
};

packages/thirdweb/src/react/core/hooks/wallets/wallet-hooks.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useCallback, useState, useSyncExternalStore } from "react";
22
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
3+
import type { ConnectManagerOptions } from "../../../../wallets/manager/index.js";
34
import { connectionManager } from "../../connectionManager.js";
45

56
/**
@@ -95,7 +96,7 @@ export function useConnectedWallets() {
9596
* const setActiveAccount = useSetActiveWallet();
9697
*
9798
* // later in your code
98-
* setActiveAccount(account);
99+
* await setActiveAccount(account);
99100
* ```
100101
* @walletConnection
101102
*/
@@ -133,26 +134,23 @@ export function useSetActiveWallet() {
133134
* ```
134135
* @walletConnection
135136
*/
136-
export function useConnect() {
137-
const { setActiveWallet } = connectionManager;
137+
export function useConnect(options?: ConnectManagerOptions) {
138+
const { connect } = connectionManager;
138139
const [isConnecting, setIsConnecting] = useState(false);
139140
const [error, setError] = useState<Error | null>(null);
140141

141-
const connect = useCallback(
142-
async (options: Wallet | (() => Promise<Wallet>)) => {
142+
const handleConnection = useCallback(
143+
async (walletOrFn: Wallet | (() => Promise<Wallet>)) => {
143144
// reset error state
144145
setError(null);
145-
if (typeof options !== "function") {
146-
setActiveWallet(options);
147-
return options;
146+
if (typeof walletOrFn !== "function") {
147+
return connect(walletOrFn, options);
148148
}
149149

150150
setIsConnecting(true);
151151
try {
152-
const wallet = await options();
153-
// add the uuid for this wallet
154-
setActiveWallet(wallet);
155-
return wallet;
152+
const w = await walletOrFn();
153+
return connect(w, options);
156154
} catch (e) {
157155
console.error(e);
158156
setError(e as Error);
@@ -161,10 +159,10 @@ export function useConnect() {
161159
}
162160
return null;
163161
},
164-
[setActiveWallet],
162+
[connect, options],
165163
);
166164

167-
return { connect, isConnecting, error } as const;
165+
return { connect: handleConnection, isConnecting, error } as const;
168166
}
169167

170168
/**

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Suspense, lazy, useCallback } from "react";
22
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
33
import { useSiweAuth } from "../../../../core/hooks/auth/useSiweAuth.js";
44
import { useConnectUI } from "../../../../core/hooks/others/useWalletConnectionCtx.js";
5-
import { useConnect } from "../../../../core/hooks/wallets/wallet-hooks.js";
5+
import { useSetActiveWallet } from "../../../../core/hooks/wallets/wallet-hooks.js";
66
import { useSetSelectionData } from "../../../providers/wallet-ui-states-provider.js";
77
import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js";
88
import { WalletSelector } from "../WalletSelector.js";
@@ -39,15 +39,15 @@ export const ConnectModalContent = (props: {
3939
connectLocale,
4040
client,
4141
} = useConnectUI();
42-
const { connect } = useConnect();
42+
const setActiveWallet = useSetActiveWallet();
4343
const setSelectionData = useSetSelectionData();
4444

4545
const siweAuth = useSiweAuth(auth);
4646
const showSignatureScreen = siweAuth.requiresAuth && !siweAuth.isLoggedIn;
4747

4848
const handleConnected = useCallback(
4949
(wallet: Wallet) => {
50-
connect(wallet);
50+
setActiveWallet(wallet);
5151

5252
if (onConnect) {
5353
onConnect(wallet);
@@ -69,7 +69,7 @@ export const ConnectModalContent = (props: {
6969
setModalVisibility,
7070
onClose,
7171
onConnect,
72-
connect,
72+
setActiveWallet,
7373
showSignatureScreen,
7474
setScreen,
7575
setSelectionData,

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

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
22
import { useCallback, useEffect, useRef, useState } from "react";
3-
import { createWallet } from "../../../../../wallets/create-wallet.js";
43
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
54
import type { SmartWalletOptions } from "../../../../../wallets/smart/types.js";
6-
import { asyncLocalStorage } from "../../../../../wallets/storage/asyncLocalStorage.js";
7-
import { saveConnectParamsToStorage } from "../../../../../wallets/storage/walletStorage.js";
85
import type { WalletInfo } from "../../../../../wallets/wallet-info.js";
6+
import { connectionManager } from "../../../../core/connectionManager.js";
97
import { useConnectUI } from "../../../../core/hooks/others/useWalletConnectionCtx.js";
108
import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js";
119
import { getSmartWalletLocale } from "../../../wallets/smartWallet/locale/getSmartWalletLocale.js";
@@ -107,31 +105,24 @@ function SmartWalletConnecting(props: {
107105
if (!personalWallet) {
108106
throw new Error("No personal wallet");
109107
}
110-
const personalAccount = personalWallet.getAccount();
111-
if (!personalAccount) {
112-
throw new Error("No personal account");
113-
}
114108

115109
setSmartWalletConnectionStatus("connecting");
116110

117111
try {
118-
const smartWallet = createWallet("smart", props.accountAbstraction);
119-
await smartWallet.connect({
120-
personalAccount,
121-
client,
122-
});
123-
124-
saveConnectParamsToStorage(asyncLocalStorage, "accountAbstraction", {
125-
personalWalletId: personalWallet.id,
126-
});
127-
128-
done(smartWallet);
112+
const connected = await connectionManager.handleConnection(
113+
personalWallet,
114+
{
115+
accountAbstraction: props.accountAbstraction,
116+
client,
117+
},
118+
);
119+
done(connected);
129120
setSmartWalletConnectionStatus("idle");
130121
} catch (e) {
131122
console.error(e);
132123
setSmartWalletConnectionStatus("connect-error");
133124
}
134-
}, [client, done, personalWallet, props.accountAbstraction]);
125+
}, [done, personalWallet, client, props.accountAbstraction]);
135126

136127
const connectStarted = useRef(false);
137128
useEffect(() => {

0 commit comments

Comments
 (0)