@dynamic-labs/ethereum 4.0.0-alpha.9 → 4.0.0

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