@dynamic-labs/ethereum 4.0.0-alpha.50 → 4.0.0-alpha.52

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.
@@ -4,52 +4,159 @@ import EthereumProvider from '@walletconnect/ethereum-provider';
4
4
  import EventEmitter from 'eventemitter3';
5
5
  import { createWalletClient, custom } from 'viem';
6
6
  import { logger, performPlatformSpecificConnectionMethod, getDeepLink } from '@dynamic-labs/wallet-connector-core';
7
- import { parseIntSafe, DynamicError, sleep, isMobile } from '@dynamic-labs/utils';
7
+ import { parseIntSafe, sleep, DynamicError, isMobile } from '@dynamic-labs/utils';
8
8
  import { EthereumWalletConnector, chainsMap } from '@dynamic-labs/ethereum-core';
9
9
 
10
10
  const activeAccountKey = (walletName) => `dynamic-wc2-active-account-${walletName}`;
11
11
  const sessionTopicKey = (walletName) => `dynamic-wc2-session-topic-${walletName}`;
12
- const swicthedNetworkKey = (walletName) => `dynamic-wc2-switched-network-${walletName}`;
13
12
  const currentChainKey = (walletName) => `dynamic-wc2-current-chain-${walletName}`;
14
13
  const ee = new EventEmitter();
15
14
  class WalletConnect extends EthereumWalletConnector {
15
+ supportsNetworkSwitching() {
16
+ return true;
17
+ }
16
18
  constructor(opts) {
17
- var _a;
18
19
  super(opts);
19
20
  this.isInitialized = false;
20
21
  this.canConnectViaQrCode = true;
21
22
  this.isWalletConnect = true;
22
23
  this.preferredChains = [];
23
- // When trying to switch network for MetaMask, the switch promise gets stuck
24
- // if the switch got trigged once already, so we need to keep track of that
25
- this._hasSwitchedNetwork = false;
26
24
  this.sessionEventHandler = () => { };
27
25
  this.sessionDeleteHandler = () => { };
28
26
  this.name = opts.walletName;
29
27
  this.projectId = opts.projectId;
30
28
  this.deepLinkPreference = opts.deepLinkPreference || 'native';
31
29
  this.preferredChains = opts.walletConnectPreferredChains || [];
32
- this.hasSwitchedNetwork =
33
- (_a = Boolean(localStorage.getItem(this.swicthedNetworkKey))) !== null && _a !== void 0 ? _a : false;
34
30
  const lsCurrentChain = localStorage.getItem(this.currentChainKey);
35
31
  this.currentChainId = lsCurrentChain
36
32
  ? parseIntSafe(lsCurrentChain)
37
33
  : undefined;
38
34
  }
39
- getMappedChains() {
40
- return (this.evmNetworks
41
- // Filters out palm that crashes Trust Wallet
42
- .filter((network) => network.chainId !== 11297108109)
43
- .map((network) => `eip155:${network.chainId}`));
35
+ /**
36
+ * This method is used to get the address of the active account.
37
+ * It will re-initialize the provider if the provider is not found.
38
+ * @param opts
39
+ * @returns
40
+ */
41
+ getAddress(opts) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ var _a, _b;
44
+ const activeAccount = this.getActiveAccount();
45
+ if (activeAccount === null || activeAccount === void 0 ? void 0 : activeAccount.address) {
46
+ return activeAccount.address;
47
+ }
48
+ if (!WalletConnect.provider || !((_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.signer.uri)) {
49
+ logger.debug('No WC2 provider found, re-initializing...');
50
+ yield this.endSession();
51
+ yield this.init();
52
+ // sleep 1 s to wait for connect call to finish
53
+ // the connect call isn't await-ed because it only resolves once
54
+ // the connection is established, but we need to wait for it to
55
+ // finish setting up the connection URI and making it available
56
+ // on the provider
57
+ yield sleep(1000);
58
+ if (!WalletConnect.provider || !((_b = WalletConnect.provider) === null || _b === void 0 ? void 0 : _b.signer.uri)) {
59
+ logger.debug('No WC2 provider was found after init attempt, aborting');
60
+ throw new DynamicError('No provider found');
61
+ }
62
+ else {
63
+ logger.debug('provider was successfully initialized, continuing');
64
+ }
65
+ }
66
+ // Set Deep links
67
+ performPlatformSpecificConnectionMethod(WalletConnect.provider.signer.uri, this.metadata.deepLinks, {
68
+ onDesktopUri: opts === null || opts === void 0 ? void 0 : opts.onDesktopUri,
69
+ onDisplayUri: opts === null || opts === void 0 ? void 0 : opts.onDisplayUri,
70
+ }, this.deepLinkPreference);
71
+ return new Promise((resolve, reject) => {
72
+ if (!WalletConnect.provider) {
73
+ reject(new DynamicError('No provider found'));
74
+ return;
75
+ }
76
+ const onFail = () => {
77
+ logger.debug('onConnection faile re-initializing provider');
78
+ const error = new DynamicError('Connection rejected. Please try again.');
79
+ error.code = 'connection_rejected';
80
+ if (WalletConnect.provider) {
81
+ WalletConnect.provider.signer.uri = undefined;
82
+ // this is needed for mobile to work when using universal links.
83
+ // if the user cancels the connection, we need to re-initialize the provider
84
+ // so that the async work is done ahead of time, before the user tries to connect again,
85
+ // otherwise they will trigger the iOS bug where they are redirected to the app store
86
+ this.init();
87
+ logger.debug('provider was re-initialized in onConnection fail, continuing');
88
+ }
89
+ reject(error);
90
+ // We must clean up the onConnect and onFail listeners
91
+ // whenever the connection attempt either succeeds or fails
92
+ cleanupListeners();
93
+ };
94
+ const onConnect = () => {
95
+ var _a;
96
+ logger.debug('onConnection success, setting session');
97
+ const session = (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.session;
98
+ if (!session) {
99
+ logger.debug('onConnection success, but no session found rejecting');
100
+ reject(new DynamicError('No session found'));
101
+ return;
102
+ }
103
+ this.setSession(session);
104
+ this.setWCActiveAccount(session.namespaces.eip155.accounts[0].split(':')[2]);
105
+ this.getNetwork().then((chainId) => {
106
+ var _a, _b;
107
+ logger.debug('onConnection success, resolving, chainId:', chainId);
108
+ this.currentChainId = chainId;
109
+ logger.debug('onConnection success, resolving, activeAccountAddress:', (_a = this.getActiveAccount()) === null || _a === void 0 ? void 0 : _a.address);
110
+ resolve((_b = this.getActiveAccount()) === null || _b === void 0 ? void 0 : _b.address);
111
+ });
112
+ // We must clean up the onConnect and onFail listeners
113
+ // whenever the connection attempt either succeeds or fails
114
+ cleanupListeners();
115
+ };
116
+ const cleanupListeners = () => {
117
+ var _a;
118
+ ee.off('walletconnect_connection_failed', onFail);
119
+ (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.off('connect', onConnect);
120
+ };
121
+ ee.on('walletconnect_connection_failed', onFail);
122
+ WalletConnect.provider.on('connect', onConnect);
123
+ });
124
+ });
125
+ }
126
+ init() {
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ logger.debug('init was called', this.name);
129
+ yield this.initProvider();
130
+ yield this.initConnection();
131
+ this.isInitialized = true;
132
+ });
44
133
  }
45
- getMappedChainsByPreferredOrder() {
46
- const allChains = this.getMappedChains();
47
- const reorderedChains = this.preferredChains.filter((chain) => allChains.includes(chain));
48
- const remainingChains = allChains.filter((chain) => !this.preferredChains.includes(chain));
49
- return [...reorderedChains, ...remainingChains].map((chain) => Number(chain.split(':')[1]));
134
+ // We need to add a gate to this method since we will be calling it asynchronously
135
+ // from different places (such as setShowAuthFlow), which means there's a chance for
136
+ // a race condition to happen where createInitProviderPromise is called multiple times
137
+ initProvider() {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ logger.debug('initProvider was called', this.name);
140
+ const { provider } = WalletConnect;
141
+ if (!provider) {
142
+ logger.debug('provider is undefined', this.name);
143
+ if (this.initializePromise === undefined) {
144
+ this.initializePromise = this.createInitProviderPromise();
145
+ }
146
+ yield this.initializePromise;
147
+ }
148
+ });
149
+ }
150
+ createInitProviderPromise() {
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ WalletConnect.provider = yield this.createProvider();
153
+ this.teardownEventListeners();
154
+ this.setupEventListeners();
155
+ });
50
156
  }
51
157
  initConnection() {
52
158
  return __awaiter(this, void 0, void 0, function* () {
159
+ logger.debug('initConnection was called', this.name);
53
160
  const { provider } = WalletConnect;
54
161
  if (!provider) {
55
162
  throw new DynamicError('No provider found (init connection)');
@@ -66,6 +173,7 @@ class WalletConnect extends EthereumWalletConnector {
66
173
  }
67
174
  createProvider() {
68
175
  return __awaiter(this, void 0, void 0, function* () {
176
+ logger.debug('createProvider was called', this.name);
69
177
  return EthereumProvider.init({
70
178
  events: ['chainChanged', 'accountsChanged'],
71
179
  methods: [],
@@ -87,65 +195,49 @@ class WalletConnect extends EthereumWalletConnector {
87
195
  });
88
196
  });
89
197
  }
90
- getWalletClientFromInitializedProvider() {
91
- return __awaiter(this, void 0, void 0, function* () {
92
- const walletConnect = this.createProvider();
93
- const walletClient = createWalletClient({
94
- account: this.getActiveAccount(),
95
- transport: custom(yield walletConnect),
96
- });
97
- return walletClient;
98
- });
99
- }
100
- createInitProviderPromise() {
101
- return __awaiter(this, void 0, void 0, function* () {
102
- WalletConnect.provider = yield this.createProvider();
103
- this.teardownEventListeners();
104
- this.setupEventListeners();
105
- });
106
- }
107
- // We need to add a gate to this method since we will be calling it asynchronously
108
- // from different places (such as setShowAuthFlow), which means there's a chance for
109
- // a race condition to happen where createInitProviderPromise is called multiple times
110
- initProvider() {
198
+ refreshSession() {
111
199
  return __awaiter(this, void 0, void 0, function* () {
112
- const { provider } = WalletConnect;
113
- if (!provider) {
114
- if (this.initializePromise === undefined) {
115
- this.initializePromise = this.createInitProviderPromise();
200
+ var _a, _b, _c, _d, _e;
201
+ logger.debug('refreshSession was called', this.name);
202
+ if ((_b = (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.topic) {
203
+ if (localStorage.getItem(this.sessionTopicKey) ===
204
+ ((_d = (_c = WalletConnect.provider) === null || _c === void 0 ? void 0 : _c.session) === null || _d === void 0 ? void 0 : _d.topic)) {
205
+ this.session = WalletConnect.provider.session;
206
+ this.setActiveAccount(((_e = localStorage.getItem(this.activeAccountKey)) !== null && _e !== void 0 ? _e : undefined));
116
207
  }
117
- yield this.initializePromise;
118
208
  }
119
- });
120
- }
121
- refreshSession() {
122
- var _a, _b, _c, _d, _e;
123
- if ((_b = (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.topic) {
124
- if (localStorage.getItem(this.sessionTopicKey) ===
125
- ((_d = (_c = WalletConnect.provider) === null || _c === void 0 ? void 0 : _c.session) === null || _d === void 0 ? void 0 : _d.topic)) {
126
- this.session = WalletConnect.provider.session;
127
- this.setActiveAccount(((_e = localStorage.getItem(this.activeAccountKey)) !== null && _e !== void 0 ? _e : undefined));
209
+ const walletClient = this.getWalletClient();
210
+ const walletChainId = yield walletClient.getChainId();
211
+ logger.debug('checking selected chain in refreshSession', this.name, walletChainId, this.currentChainId);
212
+ if (this.currentChainId && this.currentChainId !== walletChainId) {
213
+ logger.debug('switching to selected chain', this.currentChainId);
214
+ yield walletClient.switchChain({ id: this.currentChainId });
128
215
  }
129
- }
130
- }
131
- init() {
132
- return __awaiter(this, void 0, void 0, function* () {
133
- yield this.initProvider();
134
- yield this.initConnection();
135
- this.isInitialized = true;
136
216
  });
137
217
  }
138
- get sessionTopicKey() {
139
- return sessionTopicKey(this.key);
140
- }
141
- get activeAccountKey() {
142
- return activeAccountKey(this.key);
218
+ getMappedChainsByPreferredOrder() {
219
+ const allChains = this.evmNetworks.map((network) => `eip155:${network.chainId}`);
220
+ const reorderedChains = this.preferredChains.filter((chain) => allChains.includes(chain));
221
+ const remainingChains = allChains.filter((chain) => !this.preferredChains.includes(chain));
222
+ return [...reorderedChains, ...remainingChains].map((chain) => Number(chain.split(':')[1]));
143
223
  }
144
- get swicthedNetworkKey() {
145
- return swicthedNetworkKey(this.key);
224
+ getWalletClient(chainId) {
225
+ logger.debug('getWalletClient was called', this.name);
226
+ const walletConnect = WalletConnect.provider || this.createProvider();
227
+ const walletClient = createWalletClient({
228
+ account: this.getActiveAccount(),
229
+ chain: chainsMap[chainId !== null && chainId !== void 0 ? chainId : String(this.currentChainId)],
230
+ transport: custom({
231
+ request: (args) => __awaiter(this, void 0, void 0, function* () {
232
+ const provider = yield walletConnect;
233
+ return provider.request(args);
234
+ }),
235
+ }),
236
+ });
237
+ return walletClient;
146
238
  }
147
- get currentChainKey() {
148
- return currentChainKey(this.key);
239
+ get currentChainId() {
240
+ return this._currentChainId;
149
241
  }
150
242
  set currentChainId(value) {
151
243
  this._currentChainId = value;
@@ -156,23 +248,14 @@ class WalletConnect extends EthereumWalletConnector {
156
248
  localStorage.removeItem(this.currentChainKey);
157
249
  }
158
250
  }
159
- get currentChainId() {
160
- return this._currentChainId;
161
- }
162
- set hasSwitchedNetwork(value) {
163
- this._hasSwitchedNetwork = value;
164
- if (value) {
165
- localStorage.setItem(this.swicthedNetworkKey, value.toString());
166
- }
167
- else {
168
- localStorage.removeItem(this.swicthedNetworkKey);
169
- }
251
+ get sessionTopicKey() {
252
+ return sessionTopicKey(this.key);
170
253
  }
171
- get hasSwitchedNetwork() {
172
- return this._hasSwitchedNetwork;
254
+ get activeAccountKey() {
255
+ return activeAccountKey(this.key);
173
256
  }
174
- supportsNetworkSwitching() {
175
- return true;
257
+ get currentChainKey() {
258
+ return currentChainKey(this.key);
176
259
  }
177
260
  setupEventListeners() {
178
261
  if (!WalletConnect.provider) {
@@ -197,7 +280,6 @@ class WalletConnect extends EthereumWalletConnector {
197
280
  }
198
281
  this.currentChainId = chainId;
199
282
  this.emit('chainChange', { chain: String(chainId) });
200
- this.hasSwitchedNetwork = true;
201
283
  // When a user switches network from their wallet, we need the provider to change network
202
284
  // such that any future calls to `getNetwork` will return the correct network
203
285
  this.switchNetwork({ networkChainId: chainId });
@@ -226,91 +308,6 @@ class WalletConnect extends EthereumWalletConnector {
226
308
  WalletConnect.provider.off('session_event', this.sessionEventHandler);
227
309
  WalletConnect.provider.off('session_delete', this.sessionDeleteHandler);
228
310
  }
229
- getWalletClient(chainId) {
230
- if (!WalletConnect.provider) {
231
- return;
232
- }
233
- return createWalletClient({
234
- account: this.getActiveAccount(),
235
- chain: chainsMap[chainId !== null && chainId !== void 0 ? chainId : String(this.currentChainId)],
236
- transport: custom(WalletConnect.provider),
237
- });
238
- }
239
- getAddress(opts) {
240
- return __awaiter(this, void 0, void 0, function* () {
241
- var _a, _b;
242
- const activeAccount = this.getActiveAccount();
243
- if (activeAccount === null || activeAccount === void 0 ? void 0 : activeAccount.address) {
244
- return activeAccount.address;
245
- }
246
- if (!WalletConnect.provider || !((_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.signer.uri)) {
247
- logger.debug('No WC2 provider found, re-initializing...');
248
- yield this.endSession();
249
- yield this.init();
250
- // sleep 1 s to wait for connect call to finish
251
- // the connect call isn't await-ed because it only resolves once
252
- // the connection is established, but we need to wait for it to
253
- // finish setting up the connection URI and making it available
254
- // on the provider
255
- yield sleep(1000);
256
- if (!WalletConnect.provider || !((_b = WalletConnect.provider) === null || _b === void 0 ? void 0 : _b.signer.uri)) {
257
- logger.debug('No WC2 provider found, escaping and throwing error');
258
- throw new DynamicError('No provider found');
259
- }
260
- }
261
- performPlatformSpecificConnectionMethod(WalletConnect.provider.signer.uri, this.metadata.deepLinks, {
262
- onDesktopUri: opts === null || opts === void 0 ? void 0 : opts.onDesktopUri,
263
- onDisplayUri: opts === null || opts === void 0 ? void 0 : opts.onDisplayUri,
264
- }, this.deepLinkPreference);
265
- return new Promise((resolve, reject) => {
266
- if (!WalletConnect.provider) {
267
- reject(new DynamicError('No provider found'));
268
- return;
269
- }
270
- const onFail = () => {
271
- const error = new DynamicError('Connection rejected. Please try again.');
272
- error.code = 'connection_rejected';
273
- if (WalletConnect.provider) {
274
- WalletConnect.provider.signer.uri = undefined;
275
- // this is needed for mobile to work when using universal links.
276
- // if the user cancels the connection, we need to re-initialize the provider
277
- // so that the async work is done ahead of time, before the user tries to connect again,
278
- // otherwise they will trigger the iOS bug where they are redirected to the app store
279
- this.init();
280
- }
281
- reject(error);
282
- // We must clean up the onConnect and onFail listeners
283
- // whenever the connection attempt either succeeds or fails
284
- cleanupListeners();
285
- };
286
- const onConnect = () => {
287
- var _a;
288
- const session = (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.session;
289
- if (!session) {
290
- reject(new DynamicError('No session found'));
291
- return;
292
- }
293
- this.setSession(session);
294
- this.setWCActiveAccount(session.namespaces.eip155.accounts[0].split(':')[2]);
295
- this.getNetwork().then((chainId) => {
296
- var _a;
297
- this.currentChainId = chainId;
298
- resolve((_a = this.getActiveAccount()) === null || _a === void 0 ? void 0 : _a.address);
299
- });
300
- // We must clean up the onConnect and onFail listeners
301
- // whenever the connection attempt either succeeds or fails
302
- cleanupListeners();
303
- };
304
- const cleanupListeners = () => {
305
- var _a;
306
- ee.off('walletconnect_connection_failed', onFail);
307
- (_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.off('connect', onConnect);
308
- };
309
- ee.on('walletconnect_connection_failed', onFail);
310
- WalletConnect.provider.on('connect', onConnect);
311
- });
312
- });
313
- }
314
311
  /**
315
312
  * WalletConnect V2 will fail to send the sign message request if the chainId
316
313
  * is not the same as the one in the session. This method will wait for the
@@ -370,7 +367,7 @@ class WalletConnect extends EthereumWalletConnector {
370
367
  if (!activeAccount) {
371
368
  return;
372
369
  }
373
- const walletClient = yield this.getWalletClientFromInitializedProvider();
370
+ const walletClient = yield this.getWalletClient();
374
371
  return walletClient.signMessage({
375
372
  account: activeAccount,
376
373
  message: messageToSign,
@@ -402,7 +399,6 @@ class WalletConnect extends EthereumWalletConnector {
402
399
  var _a;
403
400
  this.clearActiveAccount();
404
401
  this.clearSession();
405
- this.hasSwitchedNetwork = false;
406
402
  this.currentChainId = undefined;
407
403
  if (!((_a = WalletConnect.provider) === null || _a === void 0 ? void 0 : _a.session)) {
408
404
  return;
@@ -448,27 +444,18 @@ class WalletConnect extends EthereumWalletConnector {
448
444
  if (this.switchNetworkOnlyFromWallet) {
449
445
  throw new DynamicError('Network switching is only supported through the wallet');
450
446
  }
451
- if (!this.supportsNetworkSwitching()) {
452
- throw new DynamicError('Network switching not supported');
453
- }
454
- const walletClient = yield this.getWalletClientFromInitializedProvider();
455
- if (this.isMetaMask()) {
456
- const deepLink = this.getDeepLink();
457
- if (deepLink) {
458
- window.location.href = deepLink;
459
- }
460
- }
447
+ const walletClient = yield this.getWalletClient();
461
448
  yield _super.providerSwitchNetwork.call(this, { network, provider: walletClient });
462
449
  this.currentChainId = network.chainId;
463
- this.hasSwitchedNetwork = true;
464
450
  this.emit('chainChange', { chain: String(network.chainId) });
465
451
  });
466
452
  }
467
453
  getConnectedAccounts() {
468
454
  return __awaiter(this, void 0, void 0, function* () {
469
455
  if (this.isInitialized === false) {
456
+ // TODO why not just call init()?
470
457
  yield this.initProvider();
471
- this.refreshSession();
458
+ yield this.refreshSession();
472
459
  this.isInitialized = true;
473
460
  }
474
461
  const activeAccount = this.getActiveAccount();
@@ -478,21 +465,11 @@ class WalletConnect extends EthereumWalletConnector {
478
465
  return [activeAccount.address];
479
466
  });
480
467
  }
481
- isMetaMask() {
482
- var _a, _b, _c, _d, _e;
483
- return ((_e = (_d = (_c = (_b = (_a = this.session) === null || _a === void 0 ? void 0 : _a.peer) === null || _b === void 0 ? void 0 : _b.metadata) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.toLowerCase().startsWith('metamask')) !== null && _e !== void 0 ? _e : false);
484
- }
485
468
  getSupportedNetworks() {
486
469
  return __awaiter(this, void 0, void 0, function* () {
487
470
  var _a;
488
471
  yield this.initProvider();
489
- this.refreshSession();
490
- if (this.isMetaMask()) {
491
- if (this.hasSwitchedNetwork) {
492
- return [String(this.currentChainId)];
493
- }
494
- return this.evmNetworks.map((network) => network.chainId.toString());
495
- }
472
+ yield this.refreshSession();
496
473
  if (!this.session) {
497
474
  return [];
498
475
  }