@dynamic-labs/ethereum 4.0.0-alpha.3 → 4.0.0-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/package.cjs +8 -0
  3. package/package.js +4 -0
  4. package/package.json +12 -17
  5. package/src/coinbase/coinbase.cjs +1 -3
  6. package/src/coinbase/coinbase.d.ts +2 -5
  7. package/src/coinbase/coinbase.js +2 -4
  8. package/src/coinbase/types.d.ts +2 -2
  9. package/src/ethProviderHelper.cjs +47 -38
  10. package/src/ethProviderHelper.d.ts +6 -6
  11. package/src/ethProviderHelper.js +48 -39
  12. package/src/index.cjs +11 -0
  13. package/src/index.d.ts +1 -1
  14. package/src/index.js +7 -1
  15. package/src/injected/ExodusEvm.cjs +1 -1
  16. package/src/injected/ExodusEvm.d.ts +3 -3
  17. package/src/injected/ExodusEvm.js +1 -1
  18. package/src/injected/FallbackEvmConnector.cjs +1 -1
  19. package/src/injected/FallbackEvmConnector.d.ts +3 -3
  20. package/src/injected/FallbackEvmConnector.js +1 -1
  21. package/src/injected/InjectedWalletBase.cjs +12 -6
  22. package/src/injected/InjectedWalletBase.d.ts +4 -6
  23. package/src/injected/InjectedWalletBase.js +11 -7
  24. package/src/injected/PhantomEvm.cjs +1 -1
  25. package/src/injected/PhantomEvm.d.ts +3 -3
  26. package/src/injected/PhantomEvm.js +1 -1
  27. package/src/injected/UnknownInjected.cjs +1 -1
  28. package/src/injected/UnknownInjected.d.ts +3 -3
  29. package/src/injected/UnknownInjected.js +1 -1
  30. package/src/injected/fetchInjectedWalletConnectors.cjs +4 -2
  31. package/src/injected/fetchInjectedWalletConnectors.js +4 -2
  32. package/src/injected/index.d.ts +1 -0
  33. package/src/metaMask/MetaMaskConnector.cjs +399 -0
  34. package/src/metaMask/MetaMaskConnector.d.ts +58 -0
  35. package/src/metaMask/MetaMaskConnector.js +395 -0
  36. package/src/metaMask/utils/createMetaMaskSDKDisplayUriState.cjs +58 -0
  37. package/src/metaMask/utils/createMetaMaskSDKDisplayUriState.d.ts +14 -0
  38. package/src/metaMask/utils/createMetaMaskSDKDisplayUriState.js +54 -0
  39. package/src/metaMask/utils/isPendingWalletRequestPermissionError.cjs +11 -0
  40. package/src/metaMask/utils/isPendingWalletRequestPermissionError.d.ts +1 -0
  41. package/src/metaMask/utils/isPendingWalletRequestPermissionError.js +7 -0
  42. package/src/metaMask/utils/waitForConnection.cjs +10 -0
  43. package/src/metaMask/utils/waitForConnection.d.ts +2 -0
  44. package/src/metaMask/utils/waitForConnection.js +6 -0
  45. package/src/types.d.ts +1 -3
  46. package/src/utils/logger.cjs +10 -0
  47. package/src/utils/logger.d.ts +2 -0
  48. package/src/utils/logger.js +6 -0
  49. package/src/walletConnect/walletConnect.cjs +1 -3
  50. package/src/walletConnect/walletConnect.d.ts +4 -6
  51. package/src/walletConnect/walletConnect.js +2 -4
@@ -0,0 +1,399 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../../_virtual/_tslib.cjs');
7
+ var sdk = require('@metamask/sdk');
8
+ var viem = require('viem');
9
+ var ethereumCore = require('@dynamic-labs/ethereum-core');
10
+ var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
11
+ var utils = require('@dynamic-labs/utils');
12
+ var walletBook = require('@dynamic-labs/wallet-book');
13
+ var logger = require('../utils/logger.cjs');
14
+ var ethProviderHelper = require('../ethProviderHelper.cjs');
15
+ var createMetaMaskSDKDisplayUriState = require('./utils/createMetaMaskSDKDisplayUriState.cjs');
16
+ var waitForConnection = require('./utils/waitForConnection.cjs');
17
+ var isPendingWalletRequestPermissionError = require('./utils/isPendingWalletRequestPermissionError.cjs');
18
+
19
+ /**
20
+ * The MetaMask SDK must be initialized only once, so we store the instance
21
+ * in these variables to avoid initializing it multiple times
22
+ */
23
+ let _metaMaskSDK = null;
24
+ let _metaMaskSDKDisplayUriState = null;
25
+ const eventTimeline = utils.createEventTimeline();
26
+ class MetaMaskConnector extends ethereumCore.EthereumWalletConnector {
27
+ constructor(props) {
28
+ super(props);
29
+ this.name = 'MetaMask';
30
+ this.overrideKey = 'metamask';
31
+ this.canConnectViaQrCode = true;
32
+ this.isInAppBrowser = false;
33
+ this.appName = props.appName;
34
+ this.appLogoUrl = props.appLogoUrl;
35
+ /**
36
+ * The isInAppBrowser must be calculated before initializing the MetaMask SDK.
37
+ *
38
+ * The isInAppBrowser is calculated by checking if the window provider is installed
39
+ * in the browser and if it is running on a mobile device.
40
+ *
41
+ * But the MetaMask SDK will inject its own provider to the window if not provider is injected.
42
+ * This means the MetaMask SDK can interfere with the isInAppBrowser calculation.
43
+ *
44
+ * So we need to calculate the isInAppBrowser before initializing the MetaMask SDK
45
+ * to prevent a false negative
46
+ */
47
+ this.isInAppBrowser = this.getIsInAppBrowser();
48
+ if (!_metaMaskSDK) {
49
+ this.createMetaMaskSDK();
50
+ }
51
+ }
52
+ getSupportedNetworks() {
53
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
54
+ return this.evmNetworks.map((network) => network.chainId.toString());
55
+ });
56
+ }
57
+ get ethProviderHelper() {
58
+ return new ethProviderHelper.EthProviderHelper(walletBook.getWalletBookWallet(this.walletBook, this.key));
59
+ }
60
+ get metaMaskSDK() {
61
+ if (!_metaMaskSDK)
62
+ throw new Error('MetaMaskSDK not initialized');
63
+ return _metaMaskSDK;
64
+ }
65
+ set metaMaskSDK(metaMaskSDK) {
66
+ _metaMaskSDK = metaMaskSDK;
67
+ }
68
+ get metaMaskSDKDisplayUriState() {
69
+ if (!_metaMaskSDKDisplayUriState)
70
+ throw new Error('MetaMaskSDKDisplayUriState not initialized');
71
+ return _metaMaskSDKDisplayUriState;
72
+ }
73
+ createMetaMaskSDK() {
74
+ const dappMetadata = {
75
+ iconUrl: this.appLogoUrl,
76
+ name: this.appName,
77
+ url: utils.PlatformService.getOrigin(),
78
+ };
79
+ _metaMaskSDK = new sdk.MetaMaskSDK({
80
+ _source: 'dynamic-labs',
81
+ checkInstallationImmediately: true,
82
+ dappMetadata,
83
+ enableAnalytics: true,
84
+ extensionOnly: this.isInstalledOnBrowser(),
85
+ headless: true,
86
+ openDeeplink: utils.PlatformService.openURL,
87
+ preferDesktop: !utils.isMobile(),
88
+ readonlyRPCMap: getReadonlyRPCMap(this.evmNetworkRpcMap()),
89
+ useDeeplink: true,
90
+ });
91
+ _metaMaskSDKDisplayUriState =
92
+ createMetaMaskSDKDisplayUriState.createMetaMaskSDKDisplayUriState(_metaMaskSDK);
93
+ }
94
+ endSession() {
95
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
96
+ eventTimeline.postEvent('disconnect');
97
+ /**
98
+ * The MetaMask SDK must be terminated and reinitialized on mobile
99
+ * to prevent deeplinks not working
100
+ */
101
+ if (utils.isMobile()) {
102
+ return this.metaMaskSDK.terminate().then(() => {
103
+ _metaMaskSDK = null;
104
+ _metaMaskSDKDisplayUriState = null;
105
+ return this.createMetaMaskSDK();
106
+ });
107
+ }
108
+ /**
109
+ * Just terminate the SDK on desktop
110
+ */
111
+ return this.metaMaskSDK.terminate();
112
+ });
113
+ }
114
+ getAddress(opts) {
115
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
116
+ return new Promise((resolve, reject) => {
117
+ var _a, _b, _c, _d;
118
+ // QR Code flow
119
+ if (!utils.isMobile() && Boolean(opts === null || opts === void 0 ? void 0 : opts.onDisplayUri)) {
120
+ this.metaMaskSDKDisplayUriState
121
+ .consumeDisplayUri()
122
+ .then((displayUri) => {
123
+ var _a;
124
+ if (!displayUri) {
125
+ reject(new Error('MetaMask display uri not found'));
126
+ return;
127
+ }
128
+ (_a = opts === null || opts === void 0 ? void 0 : opts.onDisplayUri) === null || _a === void 0 ? void 0 : _a.call(opts, displayUri);
129
+ });
130
+ }
131
+ // Deep link to MetaMask app in-app browser
132
+ if (this.shouldDeepLinkToMetaMaskInAppBrowser() &&
133
+ ((_b = (_a = this.wallet) === null || _a === void 0 ? void 0 : _a.mobile) === null || _b === void 0 ? void 0 : _b.inAppBrowser)) {
134
+ // Redirect to the in-app browser and append the current url
135
+ window.location.href = `${(_d = (_c = this.wallet) === null || _c === void 0 ? void 0 : _c.mobile) === null || _d === void 0 ? void 0 : _d.inAppBrowser}/${window.location.href}`;
136
+ resolve(undefined);
137
+ return;
138
+ }
139
+ // Connect to MetaMask
140
+ this.getConnectedAccountsSafely().then((initialConnectedAccounts) => _tslib.__awaiter(this, void 0, void 0, function* () {
141
+ if (initialConnectedAccounts.length) {
142
+ resolve(initialConnectedAccounts[0]);
143
+ return;
144
+ }
145
+ try {
146
+ yield this.metaMaskSDK.connect();
147
+ }
148
+ catch (error) {
149
+ const isRequestPendingError = isPendingWalletRequestPermissionError.isPendingWalletRequestPermissionError(error);
150
+ if (!isRequestPendingError) {
151
+ throw error;
152
+ }
153
+ else {
154
+ yield waitForConnection.waitForConnection(this.getProvider());
155
+ }
156
+ }
157
+ const accounts = yield this.getConnectedAccounts();
158
+ resolve(accounts[0]);
159
+ }));
160
+ });
161
+ });
162
+ }
163
+ getConnectedAccountsSafely() {
164
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
165
+ try {
166
+ const connectedAccounts = yield this.getConnectedAccounts();
167
+ return connectedAccounts;
168
+ }
169
+ catch (err) {
170
+ logger.logger.error(err);
171
+ return [];
172
+ }
173
+ });
174
+ }
175
+ getConnectedAccounts() {
176
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
177
+ // Wait for for MetaMask SDK to initialize
178
+ yield this.metaMaskSDK.sdkInitPromise;
179
+ const provider = this.getProvider();
180
+ if (!provider) {
181
+ return [];
182
+ }
183
+ /**
184
+ * The eth_accounts method can hang on mobile devices when
185
+ * the MetaMask SDK has not connected yet. So we use a retryable
186
+ * to ensure the timeout will be respected
187
+ */
188
+ const accounts = yield utils.retryableFn(() => provider.request({
189
+ method: 'eth_accounts',
190
+ params: [],
191
+ }), {
192
+ fallbackValue: [],
193
+ timeoutMs: 1000,
194
+ });
195
+ if (!(accounts === null || accounts === void 0 ? void 0 : accounts.length)) {
196
+ return [];
197
+ }
198
+ return accounts;
199
+ });
200
+ }
201
+ signMessage(messageToSign, withAddress) {
202
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
203
+ yield this.metaMaskSDK.sdkInitPromise;
204
+ /**
205
+ * Should wait for the window to be focused on mobile
206
+ * to account for the user moving between the MetaMaskApp
207
+ * and the browser
208
+ */
209
+ const windowFocusPromiseForMobile = !this.isInAppBrowser && utils.isMobile()
210
+ ? waitForFocusWindowEvent()
211
+ : Promise.resolve();
212
+ const provider = this.getProvider();
213
+ if (!provider) {
214
+ return undefined;
215
+ }
216
+ const [selectedAddress] = yield this.getConnectedAccounts();
217
+ const effectiveAddress = withAddress !== null && withAddress !== void 0 ? withAddress : selectedAddress;
218
+ if (!effectiveAddress) {
219
+ return undefined;
220
+ }
221
+ const walletClient = this.getWalletClientForAddress(effectiveAddress);
222
+ if (!walletClient)
223
+ return undefined;
224
+ const signature = yield walletClient.signMessage({
225
+ message: messageToSign,
226
+ });
227
+ yield windowFocusPromiseForMobile;
228
+ return signature;
229
+ });
230
+ }
231
+ chooseAccountsToConnect() {
232
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
233
+ return [];
234
+ });
235
+ }
236
+ getWalletClient(chainId) {
237
+ const provider = this.getProvider();
238
+ if (!provider) {
239
+ return undefined;
240
+ }
241
+ const selectedAddress = provider.getSelectedAddress();
242
+ return this.getWalletClientForAddress(selectedAddress || undefined, chainId);
243
+ }
244
+ get rdns() {
245
+ const { rdns } = this.metadata;
246
+ if (!rdns) {
247
+ throw new Error('rdns not found in metadata');
248
+ }
249
+ return rdns;
250
+ }
251
+ isInstalledOnBrowser() {
252
+ var _a;
253
+ const metaMaskEip6963Provider = (_a = this.ethProviderHelper) === null || _a === void 0 ? void 0 : _a.eip6963ProviderLookup(this.rdns);
254
+ const isInstalled = Boolean(metaMaskEip6963Provider);
255
+ return isInstalled;
256
+ }
257
+ setupEventListeners() {
258
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
259
+ yield this.metaMaskSDK.sdkInitPromise;
260
+ const metaMaskProvider = this.getProvider();
261
+ if (!metaMaskProvider) {
262
+ return;
263
+ }
264
+ const { handleAccountChange, handleChainChange, handleDisconnect } = walletConnectorCore.eventListenerHandlers(this);
265
+ const handleAccountsChangedFromMetaMask = (accounts) => {
266
+ /**
267
+ * MetaMask emits an account changed event when the wallet is disconnected
268
+ * so we ignore the accountsChanged event if the disconnect event was recent
269
+ */
270
+ if (eventTimeline.isEventRecent('disconnect', 1000)) {
271
+ return;
272
+ }
273
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
274
+ // @ts-ignore
275
+ handleAccountChange(accounts);
276
+ };
277
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
278
+ // @ts-ignore
279
+ metaMaskProvider.on('accountsChanged', handleAccountsChangedFromMetaMask);
280
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
281
+ // @ts-ignore
282
+ metaMaskProvider.on('chainChanged', handleChainChange);
283
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
284
+ // @ts-ignore
285
+ metaMaskProvider.on('disconnect', handleDisconnect);
286
+ this.teardownEventListeners = () => {
287
+ metaMaskProvider.off('accountsChanged', handleAccountsChangedFromMetaMask);
288
+ metaMaskProvider.off('chainChanged', handleChainChange);
289
+ metaMaskProvider.off('disconnect', handleDisconnect);
290
+ };
291
+ });
292
+ }
293
+ /**
294
+ * This override is necessary to wait for the MetaMask SDK to initialize
295
+ * before calling the super method. Otherwise, the super method may fail
296
+ * to fetch the provider
297
+ */
298
+ getNetwork() {
299
+ const _super = Object.create(null, {
300
+ getNetwork: { get: () => super.getNetwork }
301
+ });
302
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
303
+ yield this.metaMaskSDK.sdkInitPromise;
304
+ const net = yield _super.getNetwork.call(this);
305
+ return net;
306
+ });
307
+ }
308
+ // Utils
309
+ getProvider() {
310
+ var _a;
311
+ return ((_a = this.metaMaskSDK.getProvider()) !== null && _a !== void 0 ? _a : this.metaMaskSDK.getMobileProvider());
312
+ }
313
+ evmNetworkByChainId(chainId) {
314
+ return this.evmNetworks.find((network) => network.chainId === chainId);
315
+ }
316
+ getWalletClientForAddress(address, chainId) {
317
+ var _a, _b;
318
+ const provider = this.getProvider();
319
+ if (!provider) {
320
+ return undefined;
321
+ }
322
+ const effectiveChainId = (_b = (_a = this.toInt(chainId)) !== null && _a !== void 0 ? _a : this.getCurrentChainId()) !== null && _b !== void 0 ? _b : '1';
323
+ const network = this.evmNetworkByChainId(effectiveChainId);
324
+ return viem.createWalletClient({
325
+ account: address,
326
+ chain: network ? ethereumCore.getOrMapViemChain(network) : this.getActiveChain(),
327
+ transport: viem.custom(provider),
328
+ });
329
+ }
330
+ toInt(chainId) {
331
+ if (!chainId)
332
+ return undefined;
333
+ try {
334
+ return parseInt(chainId);
335
+ }
336
+ catch (err) {
337
+ logger.logger.debug(err);
338
+ return undefined;
339
+ }
340
+ }
341
+ getCurrentChainId() {
342
+ const provider = this.getProvider();
343
+ if (!provider) {
344
+ return undefined;
345
+ }
346
+ const chainId = provider.getChainId();
347
+ if (viem.isHex(chainId)) {
348
+ return parseInt(chainId);
349
+ }
350
+ return chainId;
351
+ }
352
+ /**
353
+ * Checks if the current environment is the MetaMask in-app browser
354
+ * by checking if the MetaMask provider is installed in the window object
355
+ * on a mobile device
356
+ */
357
+ getIsInAppBrowser() {
358
+ var _a, _b;
359
+ if (!utils.isMobile())
360
+ return false;
361
+ const provider = ((_a = this.ethProviderHelper) === null || _a === void 0 ? void 0 : _a.eip6963ProviderLookup(this.rdns)) ||
362
+ ((_b = this.ethProviderHelper) === null || _b === void 0 ? void 0 : _b.getInjectedProvider());
363
+ return Boolean(provider);
364
+ }
365
+ shouldDeepLinkToMetaMaskInAppBrowser() {
366
+ var _a;
367
+ // Not in an in-app browser
368
+ if (this.isInAppBrowser)
369
+ return false;
370
+ // Not a mobile device
371
+ if (!utils.isMobile())
372
+ return false;
373
+ // SDK is configured to use the in-app browser
374
+ if (this.mobileExperience !== 'in-app-browser')
375
+ return false;
376
+ const isAppBrowserLinkPresent = ((_a = this.wallet) === null || _a === void 0 ? void 0 : _a.mobile) && this.wallet.mobile.inAppBrowser;
377
+ // Wallet has an in-app browser link
378
+ if (!isAppBrowserLinkPresent)
379
+ return false;
380
+ return true;
381
+ }
382
+ /**
383
+ * Get wallet book wallet information
384
+ */
385
+ get wallet() {
386
+ return walletBook.findWalletBookWallet(this.walletBook, this.key);
387
+ }
388
+ }
389
+ // Utils
390
+ const getReadonlyRPCMap = (evmNetworkRpcMap) => Object.keys(evmNetworkRpcMap).reduce((acc, chainId) => (Object.assign(Object.assign({}, acc), { [viem.toHex(parseInt(chainId))]: evmNetworkRpcMap[chainId] })), {});
391
+ /**
392
+ * Waits for the focus page event and await for an extra second
393
+ * This is necessary to ensure the verify call will succeed
394
+ */
395
+ const waitForFocusWindowEvent = () => new Promise((resolve) => {
396
+ window.addEventListener('focus', resolve);
397
+ }).then(() => new Promise((resolve) => setTimeout(resolve, 1000)));
398
+
399
+ exports.MetaMaskConnector = MetaMaskConnector;
@@ -0,0 +1,58 @@
1
+ import { MetaMaskSDK } from '@metamask/sdk';
2
+ import { Account, Transport, WalletClient, Chain as ViemChain } from 'viem';
3
+ import { EthereumWalletConnector, EthereumWalletConnectorOpts } from '@dynamic-labs/ethereum-core';
4
+ import { GetAddressOpts, IWalletConnectConnector } from '@dynamic-labs/wallet-connector-core';
5
+ import { EthProviderHelper } from '../ethProviderHelper';
6
+ import { MetaMaskSDKDisplayUriState } from './utils/createMetaMaskSDKDisplayUriState';
7
+ export type MetaMaskConnectorProps = EthereumWalletConnectorOpts & {
8
+ appName: string;
9
+ appLogoUrl: string;
10
+ };
11
+ export declare const clearMetaMaskSdk: () => void;
12
+ export declare class MetaMaskConnector extends EthereumWalletConnector implements IWalletConnectConnector {
13
+ name: string;
14
+ overrideKey: string;
15
+ canConnectViaQrCode: boolean;
16
+ private appName;
17
+ private appLogoUrl;
18
+ private isInAppBrowser;
19
+ constructor(props: MetaMaskConnectorProps);
20
+ getSupportedNetworks(): Promise<string[]>;
21
+ get ethProviderHelper(): EthProviderHelper | undefined;
22
+ get metaMaskSDK(): MetaMaskSDK;
23
+ set metaMaskSDK(metaMaskSDK: MetaMaskSDK);
24
+ get metaMaskSDKDisplayUriState(): MetaMaskSDKDisplayUriState;
25
+ createMetaMaskSDK(): void;
26
+ endSession(): Promise<void>;
27
+ getAddress(opts?: GetAddressOpts): Promise<string | undefined>;
28
+ private getConnectedAccountsSafely;
29
+ getConnectedAccounts(): Promise<string[]>;
30
+ signMessage(messageToSign: string, withAddress?: string): Promise<string | undefined>;
31
+ chooseAccountsToConnect(): Promise<never[]>;
32
+ getWalletClient(chainId?: string): WalletClient<Transport, ViemChain, Account> | undefined;
33
+ get rdns(): string;
34
+ isInstalledOnBrowser(): boolean;
35
+ setupEventListeners(): Promise<void>;
36
+ /**
37
+ * This override is necessary to wait for the MetaMask SDK to initialize
38
+ * before calling the super method. Otherwise, the super method may fail
39
+ * to fetch the provider
40
+ */
41
+ getNetwork(): Promise<number | undefined>;
42
+ private getProvider;
43
+ private evmNetworkByChainId;
44
+ private getWalletClientForAddress;
45
+ private toInt;
46
+ private getCurrentChainId;
47
+ /**
48
+ * Checks if the current environment is the MetaMask in-app browser
49
+ * by checking if the MetaMask provider is installed in the window object
50
+ * on a mobile device
51
+ */
52
+ private getIsInAppBrowser;
53
+ private shouldDeepLinkToMetaMaskInAppBrowser;
54
+ /**
55
+ * Get wallet book wallet information
56
+ */
57
+ private get wallet();
58
+ }