Skip to content

Commit aa55fe4

Browse files
abstract wallet support
1 parent 07af647 commit aa55fe4

File tree

34 files changed

+905
-376
lines changed

34 files changed

+905
-376
lines changed
65.3 KB
Loading

packages/thirdweb/scripts/wallets/extra-wallets.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,33 @@
9191
"native": null,
9292
"universal": null
9393
}
94+
},
95+
{
96+
"id": "abstract",
97+
"name": "Abstract Global Wallet",
98+
"homepage": "https://abs.xyz/",
99+
"image_id": "abstract.png",
100+
"app": {
101+
"browser": null,
102+
"ios": null,
103+
"android": null,
104+
"mac": null,
105+
"windows": null,
106+
"linux": null,
107+
"chrome": null,
108+
"firefox": null,
109+
"safari": null,
110+
"edge": null,
111+
"opera": null
112+
},
113+
"rdns": "xyz.abs",
114+
"mobile": {
115+
"native": null,
116+
"universal": null
117+
},
118+
"desktop": {
119+
"native": null,
120+
"universal": null
121+
}
94122
}
95123
]
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import type { EIP1193Provider } from "viem";
2+
import { trackConnect } from "../analytics/track/connect.js";
3+
import type { Chain } from "../chains/types.js";
4+
import { getCachedChainIfExists } from "../chains/utils.js";
5+
import type { ThirdwebClient } from "../client/client.js";
6+
import {
7+
autoConnectEip1193Wallet,
8+
connectEip1193Wallet,
9+
} from "../wallets/injected/index.js";
10+
import type { Account, Wallet } from "../wallets/interfaces/wallet.js";
11+
import { createWalletEmitter } from "../wallets/wallet-emitter.js";
12+
import type { WalletId } from "../wallets/wallet-types.js";
13+
14+
export type Eip1193AdapterOptions = {
15+
client: ThirdwebClient;
16+
provider: EIP1193Provider | (() => Promise<EIP1193Provider>);
17+
walletId?: WalletId;
18+
chain?: Chain;
19+
};
20+
21+
/**
22+
* Converts an EIP1193 provider to a Thirdweb wallet.
23+
* @param options - The options for converting an EIP1193 provider to a Thirdweb wallet.
24+
* @returns A Thirdweb wallet.
25+
* @example
26+
* ```ts
27+
* import { fromEip1193Provider } from "thirdweb/wallets";
28+
* const wallet = fromEip1193Provider({ provider, client, walletId });
29+
* ```
30+
* @walletUtils
31+
*/
32+
export function fromEip1193Provider(options: Eip1193AdapterOptions): Wallet {
33+
const id: WalletId = options.walletId ?? "adapter";
34+
const emitter = createWalletEmitter();
35+
let account: Account | undefined = undefined;
36+
let chain: Chain | undefined = undefined;
37+
let provider: EIP1193Provider | undefined = undefined;
38+
const getProvider = async () => {
39+
if (!provider) {
40+
provider =
41+
typeof options.provider === "function"
42+
? await options.provider()
43+
: options.provider;
44+
}
45+
return provider;
46+
};
47+
48+
const unsubscribeChain = emitter.subscribe("chainChanged", (newChain) => {
49+
chain = newChain;
50+
});
51+
52+
function reset() {
53+
account = undefined;
54+
chain = undefined;
55+
}
56+
57+
let handleDisconnect = async () => {};
58+
59+
const unsubscribeDisconnect = emitter.subscribe("disconnect", () => {
60+
reset();
61+
unsubscribeChain();
62+
unsubscribeDisconnect();
63+
});
64+
65+
emitter.subscribe("accountChanged", (_account) => {
66+
account = _account;
67+
});
68+
69+
let handleSwitchChain: (chain: Chain) => Promise<void> = async () => {
70+
throw new Error("Not implemented");
71+
};
72+
73+
return {
74+
id: options.walletId as WalletId,
75+
subscribe: emitter.subscribe,
76+
getConfig: () => undefined,
77+
getChain() {
78+
if (!chain) {
79+
return undefined;
80+
}
81+
82+
chain = getCachedChainIfExists(chain.id) || chain;
83+
return chain;
84+
},
85+
getAccount: () => account,
86+
connect: async () => {
87+
const [connectedAccount, connectedChain, doDisconnect, doSwitchChain] =
88+
await connectEip1193Wallet({
89+
id,
90+
provider: await getProvider(),
91+
client: options.client,
92+
chain: options.chain,
93+
emitter,
94+
});
95+
// set the states
96+
account = connectedAccount;
97+
chain = connectedChain;
98+
handleDisconnect = doDisconnect;
99+
handleSwitchChain = doSwitchChain;
100+
trackConnect({
101+
client: options.client,
102+
walletType: id,
103+
walletAddress: account.address,
104+
});
105+
// return account
106+
return account;
107+
},
108+
autoConnect: async () => {
109+
const [connectedAccount, connectedChain, doDisconnect, doSwitchChain] =
110+
await autoConnectEip1193Wallet({
111+
id,
112+
provider: await getProvider(),
113+
emitter,
114+
chain: options.chain,
115+
client: options.client,
116+
});
117+
// set the states
118+
account = connectedAccount;
119+
chain = connectedChain;
120+
handleDisconnect = doDisconnect;
121+
handleSwitchChain = doSwitchChain;
122+
trackConnect({
123+
client: options.client,
124+
walletType: id,
125+
walletAddress: account.address,
126+
});
127+
// return account
128+
return account;
129+
},
130+
disconnect: async () => {
131+
reset();
132+
await handleDisconnect();
133+
},
134+
switchChain: (c) => handleSwitchChain(c),
135+
};
136+
}

packages/thirdweb/src/analytics/track/transaction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type TransactionEvent = {
99
ecosystem?: Ecosystem;
1010
transactionHash?: string;
1111
walletAddress?: string;
12-
walletType?: WalletId;
12+
walletType?: WalletId | ({} & string);
1313
chainId?: number;
1414
contractAddress?: string;
1515
functionName?: string;

packages/thirdweb/src/exports/wallets.native.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ export type {
134134
WalletConnectSession,
135135
} from "../wallets/wallet-connect/receiver/types.js";
136136

137+
// eip1193
138+
export { fromEip1193Provider } from "../adapters/eip1193-adapter.js";
139+
137140
// NOT SUPPORTED
138141

139142
export const injectedProvider = () => {

packages/thirdweb/src/exports/wallets.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ export type {
144144
WalletConnectSession,
145145
} from "../wallets/wallet-connect/receiver/types.js";
146146

147+
// eip1193
148+
export { fromEip1193Provider } from "../adapters/eip1193-adapter.js";
149+
147150
// WEB ONLY EXPORTS
148151

149152
// injected

packages/thirdweb/src/react/core/hooks/wallets/useAdminWallet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useConnectedWallets } from "./useConnectedWallets.js";
55
* Get the admin wallet for the active wallet
66
* Useful for smart wallets to get the underlying personal account
77
* @returns The admin wallet for the active wallet, or the active wallet if it doesn't have an admin account
8+
* @walletConnection
89
*/
910
export function useAdminWallet() {
1011
const activeWallet = useActiveWallet();

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

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type {
99
InjectedSupportedWalletIds,
1010
WCSupportedWalletIds,
1111
} from "../../../../../wallets/__generated__/wallet-ids.js";
12-
import { COINBASE } from "../../../../../wallets/constants.js";
1312
import { isEcosystemWallet } from "../../../../../wallets/ecosystem/is-ecosystem-wallet.js";
1413
import { getInstalledWalletProviders } from "../../../../../wallets/injected/mipdStore.js";
1514
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
@@ -197,27 +196,6 @@ export function AnyWalletConnectUI(props: {
197196
);
198197
}
199198

200-
// coinbase wallet sdk
201-
if (props.wallet.id === COINBASE) {
202-
return (
203-
<Suspense fallback={<LoadingScreen />}>
204-
<CoinbaseSDKWalletConnectUI
205-
locale={locale}
206-
onGetStarted={() => {
207-
setScreen("get-started");
208-
}}
209-
onBack={props.onBack}
210-
done={props.done}
211-
wallet={props.wallet as Wallet<typeof COINBASE>}
212-
walletInfo={walletInfo.data}
213-
chain={props.chain}
214-
client={props.client}
215-
size={props.size}
216-
/>
217-
</Suspense>
218-
);
219-
}
220-
221199
// wallet connect
222200
if (walletInfo.data.mobile.native || walletInfo.data.mobile.universal) {
223201
return (
@@ -294,6 +272,27 @@ export function AnyWalletConnectUI(props: {
294272
);
295273
}
296274

275+
// any other known wallet
276+
if (props.wallet.id) {
277+
return (
278+
<Suspense fallback={<LoadingScreen />}>
279+
<CoinbaseSDKWalletConnectUI
280+
locale={locale}
281+
onGetStarted={() => {
282+
setScreen("get-started");
283+
}}
284+
onBack={props.onBack}
285+
done={props.done}
286+
wallet={props.wallet}
287+
walletInfo={walletInfo.data}
288+
chain={props.chain}
289+
client={props.client}
290+
size={props.size}
291+
/>
292+
</Suspense>
293+
);
294+
}
295+
297296
// if can't connect in any way - show get started screen
298297
return (
299298
<GetStartedScreen

packages/thirdweb/src/react/web/wallets/shared/CoinbaseSDKConnection.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
22
import type { Chain } from "../../../../chains/types.js";
33
import type { ThirdwebClient } from "../../../../client/client.js";
4-
import type { COINBASE } from "../../../../wallets/constants.js";
54
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
65
import type { WalletInfo } from "../../../../wallets/wallet-info.js";
76
import type { InjectedWalletLocale } from "../injected/locale/types.js";
@@ -10,12 +9,12 @@ import { ConnectingScreen } from "./ConnectingScreen.js";
109
/**
1110
* @internal
1211
*/
13-
function CoinbaseSDKWalletConnectUI(props: {
12+
function ExternalWalletConnectUI(props: {
1413
onBack?: () => void;
1514
onGetStarted: () => void;
1615
done: () => void;
1716
locale: InjectedWalletLocale;
18-
wallet: Wallet<typeof COINBASE>;
17+
wallet: Wallet;
1918
walletInfo: WalletInfo;
2019
client: ThirdwebClient;
2120
chain: Chain | undefined;
@@ -70,4 +69,4 @@ function CoinbaseSDKWalletConnectUI(props: {
7069
);
7170
}
7271

73-
export default CoinbaseSDKWalletConnectUI;
72+
export default ExternalWalletConnectUI;

packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { TransactionSerializable } from "viem";
22
import { getGasOverridesForTransaction } from "../../gas/fee-data.js";
33
import { getRpcClient } from "../../rpc/rpc.js";
4+
import { getAddress } from "../../utils/address.js";
5+
import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js";
46
import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
57
import type { PreparedTransaction } from "../prepare-transaction.js";
68
import { encode } from "./encode.js";
@@ -45,6 +47,25 @@ export type ToSerializableTransactionOptions = {
4547
export async function toSerializableTransaction(
4648
options: ToSerializableTransactionOptions,
4749
) {
50+
// zk chains require a different rpc method for gas estimation and gas fees
51+
const isZkSync = await isZkSyncChain(options.transaction.chain);
52+
if (isZkSync) {
53+
const { getZkGasFees } = await import(
54+
"./zksync/send-eip712-transaction.js"
55+
);
56+
const { gas, maxFeePerGas, maxPriorityFeePerGas } = await getZkGasFees({
57+
transaction: options.transaction,
58+
from: options.from ? getAddress(options.from) : undefined,
59+
});
60+
// passing these values here will avoid re-fetching them below
61+
options.transaction = {
62+
...options.transaction,
63+
gas,
64+
maxFeePerGas,
65+
maxPriorityFeePerGas,
66+
};
67+
}
68+
4869
const rpcRequest = getRpcClient(options.transaction);
4970
const chainId = options.transaction.chain.id;
5071
const from = options.from;

0 commit comments

Comments
 (0)