@dynamic-labs/ethereum 0.17.10 → 0.17.12

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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,18 @@
1
1
 
2
+ ### [0.17.12](https://github.com/dynamic-labs/DynamicAuth/compare/v0.17.11...v0.17.12) (2023-06-26)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * more wc2 improvements ([#2484](https://github.com/dynamic-labs/DynamicAuth/issues/2484)) ([482ed5d](https://github.com/dynamic-labs/DynamicAuth/commit/482ed5d699985ff4a96f4af2ce2a3f7565a6cc1f))
8
+
9
+ ### [0.17.11](https://github.com/dynamic-labs/DynamicAuth/compare/v0.17.10...v0.17.11) (2023-06-26)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * multiple improvements for WC2 ([#2465](https://github.com/dynamic-labs/DynamicAuth/issues/2465)) ([7d5d7bd](https://github.com/dynamic-labs/DynamicAuth/commit/7d5d7bdcf2dd01ef3810e2fddd46d56a40effa95)), closes [#2458](https://github.com/dynamic-labs/DynamicAuth/issues/2458)
15
+
2
16
  ### [0.17.10](https://github.com/dynamic-labs/DynamicAuth/compare/v0.17.9...v0.17.10) (2023-06-23)
3
17
 
4
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/ethereum",
3
- "version": "0.17.10",
3
+ "version": "0.17.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/dynamic-labs/DynamicAuth.git",
@@ -28,14 +28,15 @@
28
28
  "dependencies": {
29
29
  "@walletconnect/client": "1.8.0",
30
30
  "@walletconnect/ethereum-provider": "1.8.0",
31
- "@walletconnect/universal-provider": "2.8.0",
31
+ "@walletconnect/universal-provider": "2.8.3",
32
32
  "ethers": "5.7.2",
33
+ "eventemitter3": "5.0.1",
33
34
  "buffer": "6.0.3",
34
- "@dynamic-labs/rpc-providers": "0.17.10",
35
- "@dynamic-labs/types": "0.17.10",
36
- "@dynamic-labs/utils": "0.17.10",
37
- "@dynamic-labs/wallet-book": "0.17.10",
38
- "@dynamic-labs/wallet-connector-core": "0.17.10"
35
+ "@dynamic-labs/rpc-providers": "0.17.12",
36
+ "@dynamic-labs/types": "0.17.12",
37
+ "@dynamic-labs/utils": "0.17.12",
38
+ "@dynamic-labs/wallet-book": "0.17.12",
39
+ "@dynamic-labs/wallet-connector-core": "0.17.12"
39
40
  },
40
41
  "peerDependencies": {}
41
42
  }
package/src/index.cjs CHANGED
@@ -4,13 +4,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  require('./polyfills.cjs');
6
6
  var index = require('./injected/index.cjs');
7
- require('@walletconnect/universal-provider');
7
+ require('./walletConnect/walletConnectV2.cjs');
8
+ require('@walletconnect/ethereum-provider');
8
9
  require('ethers');
9
10
  require('@dynamic-labs/wallet-connector-core');
10
11
  require('@dynamic-labs/wallet-book');
11
12
  require('@dynamic-labs/utils');
12
13
  var EthWalletConnector = require('./EthWalletConnector.cjs');
13
- require('@walletconnect/ethereum-provider');
14
14
  var constants = require('./constants.cjs');
15
15
  require('@walletconnect/client');
16
16
  var fetchWalletConnectWallets = require('./walletConnect/fetchWalletConnectWallets.cjs');
package/src/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import './polyfills.js';
2
2
  import { injectedWallets } from './injected/index.js';
3
3
  export { injectedWallets } from './injected/index.js';
4
- import '@walletconnect/universal-provider';
4
+ import './walletConnect/walletConnectV2.js';
5
+ import '@walletconnect/ethereum-provider';
5
6
  import 'ethers';
6
7
  import '@dynamic-labs/wallet-connector-core';
7
8
  import '@dynamic-labs/wallet-book';
8
9
  import '@dynamic-labs/utils';
9
10
  export { EthWalletConnector } from './EthWalletConnector.js';
10
- import '@walletconnect/ethereum-provider';
11
11
  export { INFURA_ID } from './constants.js';
12
12
  import '@walletconnect/client';
13
13
  import { fetchWalletConnectWallets, getWalletConnectConnector } from './walletConnect/fetchWalletConnectWallets.js';
@@ -0,0 +1 @@
1
+ export declare const isString: (value: unknown) => value is string;
@@ -0,0 +1 @@
1
+ export declare const last: <T = unknown>(array: T[]) => T | undefined;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
6
+
7
+ const parseIntSafe = (value, radix = 10) => {
8
+ try {
9
+ const int = parseInt(String(value), radix);
10
+ if (isNaN(int)) {
11
+ walletConnectorCore.logger.error(`Error parsing ${value}`);
12
+ return undefined;
13
+ }
14
+ return int;
15
+ }
16
+ catch (e) {
17
+ walletConnectorCore.logger.error(`Error parsing ${value} to int: ${e}`);
18
+ }
19
+ return undefined;
20
+ };
21
+
22
+ exports.parseIntSafe = parseIntSafe;
@@ -0,0 +1 @@
1
+ export declare const parseIntSafe: (value: string | number, radix?: number) => number | undefined;
@@ -0,0 +1,18 @@
1
+ import { logger } from '@dynamic-labs/wallet-connector-core';
2
+
3
+ const parseIntSafe = (value, radix = 10) => {
4
+ try {
5
+ const int = parseInt(String(value), radix);
6
+ if (isNaN(int)) {
7
+ logger.error(`Error parsing ${value}`);
8
+ return undefined;
9
+ }
10
+ return int;
11
+ }
12
+ catch (e) {
13
+ logger.error(`Error parsing ${value} to int: ${e}`);
14
+ }
15
+ return undefined;
16
+ };
17
+
18
+ export { parseIntSafe };
@@ -5,21 +5,27 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var _tslib = require('../../_virtual/_tslib.cjs');
6
6
  var Provider = require('@walletconnect/universal-provider');
7
7
  var ethers = require('ethers');
8
+ var EventEmitter = require('eventemitter3');
8
9
  var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
9
10
  var walletBook = require('@dynamic-labs/wallet-book');
10
11
  var utils = require('@dynamic-labs/utils');
11
12
  var EthWalletConnector = require('../EthWalletConnector.cjs');
13
+ var parseIntSafe = require('../utils/parseIntSafe.cjs');
12
14
 
13
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
16
 
15
17
  var Provider__default = /*#__PURE__*/_interopDefaultLegacy(Provider);
18
+ var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
16
19
 
20
+ const activeAccountKey = (walletName) => `dynamic-wc2-active-account-${walletName}`;
17
21
  const sessionTopicKey = (walletName) => `dynamic-wc2-session-topic-${walletName}`;
22
+ const ee = new EventEmitter__default["default"]();
18
23
  class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
19
24
  constructor(opts) {
20
25
  super(opts);
21
26
  this.supportedChains = ['EVM', 'ETH'];
22
27
  this.connectedChain = 'EVM';
28
+ this.isInitialized = false;
23
29
  this.canConnectViaQrCode = true;
24
30
  this.isWalletConnect = true;
25
31
  this.name = opts.walletName;
@@ -35,24 +41,39 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
35
41
  if (provider === null || provider === void 0 ? void 0 : provider.uri) {
36
42
  return;
37
43
  }
38
- yield provider.connect({
44
+ const defaultChain = 1;
45
+ provider
46
+ .connect({
39
47
  namespaces: {
48
+ eip155: {
49
+ chains: [`eip155:${defaultChain}`],
50
+ events: ['chainChanged', 'accountsChanged'],
51
+ methods: ['personal_sign', 'eth_sendTransaction'],
52
+ rpcMap: this.evmNetworkRpcMap(),
53
+ },
54
+ },
55
+ optionalNamespaces: {
40
56
  eip155: {
41
57
  chains: this.evmNetworks
42
- // filtering out Palm for now since it causes Trust to crash
43
- .filter((network) => network.chainId !== 11297108109)
58
+ // Filters out mainnet which is required and palm that crashes Trust Wallet
59
+ .filter((network) => network.chainId !== 11297108109 &&
60
+ network.chainId !== defaultChain)
44
61
  .map((network) => `eip155:${network.chainId}`),
45
- events: ['chainChanged', 'accountsChanged'],
62
+ events: [],
46
63
  methods: [
47
- 'eth_sendTransaction',
64
+ 'eth_signTypedData',
48
65
  'eth_signTransaction',
49
66
  'eth_sign',
50
67
  'personal_sign',
51
- 'eth_signTypedData',
68
+ 'eth_sendTransaction',
52
69
  ],
53
70
  rpcMap: this.evmNetworkRpcMap(),
54
71
  },
55
72
  },
73
+ })
74
+ .catch((e) => {
75
+ walletConnectorCore.logger.error(e);
76
+ ee.emit('walletconnect_connection_failed');
56
77
  });
57
78
  });
58
79
  }
@@ -68,11 +89,13 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
68
89
  });
69
90
  }
70
91
  refreshSession() {
71
- var _a, _b, _c, _d;
92
+ var _a, _b, _c, _d, _e;
72
93
  if ((_b = (_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.topic) {
73
94
  if (localStorage.getItem(this.sessionTopicKey) ===
74
95
  ((_d = (_c = WalletConnectV2.provider) === null || _c === void 0 ? void 0 : _c.session) === null || _d === void 0 ? void 0 : _d.topic)) {
75
96
  this.session = WalletConnectV2.provider.session;
97
+ this.activeAccount =
98
+ (_e = localStorage.getItem(this.activeAccountKey)) !== null && _e !== void 0 ? _e : undefined;
76
99
  }
77
100
  }
78
101
  }
@@ -80,11 +103,15 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
80
103
  return _tslib.__awaiter(this, void 0, void 0, function* () {
81
104
  yield this.initProvider();
82
105
  yield this.initConnection();
106
+ this.isInitialized = true;
83
107
  });
84
108
  }
85
109
  get sessionTopicKey() {
86
110
  return sessionTopicKey(this.key);
87
111
  }
112
+ get activeAccountKey() {
113
+ return activeAccountKey(this.key);
114
+ }
88
115
  supportsNetworkSwitching() {
89
116
  return true;
90
117
  }
@@ -100,18 +127,22 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
100
127
  }
101
128
  const { name, data } = params.event;
102
129
  if (name === 'chainChanged') {
103
- if (typeof data !== 'string') {
130
+ const chainId = parseIntSafe.parseIntSafe(data);
131
+ if (chainId === undefined) {
104
132
  walletConnectorCore.logger.debug(`received unexpected data for chainChanged: ${data} with type ${typeof data}}`);
105
133
  return;
106
134
  }
107
- (_a = listeners.onChainChange) === null || _a === void 0 ? void 0 : _a.call(listeners, data);
135
+ (_a = listeners.onChainChange) === null || _a === void 0 ? void 0 : _a.call(listeners, String(chainId));
108
136
  }
109
137
  else if (name === 'accountsChanged') {
110
- if (typeof data !== 'string' || !Array.isArray(data)) {
138
+ if (!Array.isArray(data)) {
111
139
  walletConnectorCore.logger.debug(`received unexpected data for accountsChanged: ${data} with type ${typeof data}}`);
112
140
  return;
113
141
  }
114
- (_b = listeners.onAccountChange) === null || _b === void 0 ? void 0 : _b.call(listeners, data);
142
+ // eslint-disable-next-line prefer-destructuring
143
+ const account = data[0].split(':')[2];
144
+ this.setActiveAccount(account);
145
+ (_b = listeners.onAccountChange) === null || _b === void 0 ? void 0 : _b.call(listeners, [account]);
115
146
  }
116
147
  });
117
148
  WalletConnectV2.provider.client.on('session_delete', () => _tslib.__awaiter(this, void 0, void 0, function* () {
@@ -148,15 +179,25 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
148
179
  return new ethers.ethers.providers.Web3Provider(WalletConnectV2.provider, 'any');
149
180
  }
150
181
  fetchPublicAddress(opts) {
182
+ var _a, _b;
151
183
  return _tslib.__awaiter(this, void 0, void 0, function* () {
152
- if (this.session) {
153
- return this.session.namespaces.eip155.accounts[0].split(':')[2];
184
+ if (this.activeAccount) {
185
+ return this.activeAccount;
154
186
  }
155
- if (!WalletConnectV2.provider) {
156
- throw new utils.DynamicError('No provider found');
157
- }
158
- if (!WalletConnectV2.provider.uri) {
159
- throw new utils.DynamicError('No uri found');
187
+ if (!WalletConnectV2.provider || !((_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.uri)) {
188
+ walletConnectorCore.logger.debug('No WC2 provider found, re-initializing...');
189
+ yield this.endSession();
190
+ yield this.init();
191
+ // sleep 1 s to wait for connect call to finish
192
+ // the connect call isn't await-ed because it only resolves once
193
+ // the connection is established, but we need to wait for it to
194
+ // finish setting up the connection URI and making it available
195
+ // on the provider
196
+ yield new Promise((resolve) => setTimeout(resolve, 1000));
197
+ if (!WalletConnectV2.provider || !((_b = WalletConnectV2.provider) === null || _b === void 0 ? void 0 : _b.uri)) {
198
+ walletConnectorCore.logger.debug('No WC2 provider found, escaping and throwing error');
199
+ throw new utils.DynamicError('No provider found');
200
+ }
160
201
  }
161
202
  const metadata = walletBook.getWalletBookWallet(this.name);
162
203
  walletConnectorCore.performPlatformSpecificConnectionMethod(WalletConnectV2.provider.uri, metadata, {
@@ -168,13 +209,21 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
168
209
  reject(new utils.DynamicError('No provider found'));
169
210
  return;
170
211
  }
212
+ ee.on('walletconnect_connection_failed', () => {
213
+ const error = new utils.DynamicError('Connection rejected. Please try again.');
214
+ error.code = 'connection_rejected';
215
+ if (WalletConnectV2.provider) {
216
+ WalletConnectV2.provider.uri = undefined;
217
+ }
218
+ reject(error);
219
+ });
171
220
  WalletConnectV2.provider.on('connect', ({ session }) => {
172
221
  if (!session) {
173
222
  reject(new utils.DynamicError('No session found'));
174
223
  }
175
- this.session = session;
176
- localStorage.setItem(this.sessionTopicKey, session.topic);
177
- resolve(this.session.namespaces.eip155.accounts[0].split(':')[2]);
224
+ this.setSession(session);
225
+ this.setActiveAccount(session.namespaces.eip155.accounts[0].split(':')[2]);
226
+ resolve(this.activeAccount);
178
227
  });
179
228
  });
180
229
  });
@@ -194,34 +243,69 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
194
243
  const deepLink = walletConnectorCore.getDeepLink({ metadata, mode: 'regular' });
195
244
  window.location.href = deepLink;
196
245
  }
197
- // eslint-disable-next-line prefer-destructuring
198
- const address = this.session.namespaces.eip155.accounts[0].split(':')[2];
199
246
  const web3Provider = this.getWeb3Provider();
200
- return web3Provider.send('personal_sign', [
247
+ const response = web3Provider.send('personal_sign', [
201
248
  walletConnectorCore.utf8ToHex(messageToSign, true),
202
- address,
249
+ this.activeAccount,
203
250
  ]);
251
+ try {
252
+ // A terrible hack to get MM to sign the message when it is on the wrong chain
253
+ // Since MM supports only one chain at a time, it just hangs when we call
254
+ // to sign, and it is not on Mainnet. For some magical reason,
255
+ // when we attempt to call again it just works.
256
+ if (this.key === 'metamask') {
257
+ yield this.promiseWithTimeout(7000, response);
258
+ }
259
+ }
260
+ catch (e) {
261
+ walletConnectorCore.logger.debug('metmask failed to sign message');
262
+ throw e;
263
+ }
264
+ return response;
204
265
  });
205
266
  }
267
+ clearActiveAccount() {
268
+ localStorage.removeItem(this.activeAccountKey);
269
+ this.activeAccount = undefined;
270
+ }
271
+ clearSession() {
272
+ localStorage.removeItem(this.sessionTopicKey);
273
+ this.session = undefined;
274
+ }
275
+ setActiveAccount(account) {
276
+ localStorage.setItem(this.activeAccountKey, account);
277
+ this.activeAccount = account;
278
+ }
279
+ setSession(session) {
280
+ localStorage.setItem(this.sessionTopicKey, session.topic);
281
+ this.session = session;
282
+ }
206
283
  endSession() {
284
+ var _a;
207
285
  return _tslib.__awaiter(this, void 0, void 0, function* () {
208
- if (!this.session) {
209
- return;
210
- }
211
- if (!WalletConnectV2.provider) {
286
+ this.clearActiveAccount();
287
+ this.clearSession();
288
+ if (!((_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.session)) {
212
289
  return;
213
290
  }
214
291
  try {
215
292
  yield WalletConnectV2.provider.disconnect();
216
293
  WalletConnectV2.provider.uri = undefined;
217
- localStorage.removeItem(this.sessionTopicKey);
218
- this.session = undefined;
219
294
  }
220
295
  catch (e) {
221
296
  walletConnectorCore.logger.debug(e);
222
297
  }
223
298
  });
224
299
  }
300
+ getNetwork() {
301
+ const _super = Object.create(null, {
302
+ getNetwork: { get: () => super.getNetwork }
303
+ });
304
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
305
+ yield this.initProvider();
306
+ return _super.getNetwork.call(this);
307
+ });
308
+ }
225
309
  providerSwitchNetwork({ network, provider, }) {
226
310
  const _super = Object.create(null, {
227
311
  providerSwitchNetwork: { get: () => super.providerSwitchNetwork }
@@ -244,14 +328,46 @@ class WalletConnectV2 extends EthWalletConnector.EthWalletConnector {
244
328
  });
245
329
  }
246
330
  getConnectedAccounts() {
331
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
332
+ yield this.initProvider();
333
+ this.refreshSession();
334
+ this.isInitialized = true;
335
+ if (!this.activeAccount) {
336
+ return [];
337
+ }
338
+ return [this.activeAccount];
339
+ });
340
+ }
341
+ getSupportedNetworks() {
247
342
  return _tslib.__awaiter(this, void 0, void 0, function* () {
248
343
  yield this.initProvider();
249
344
  this.refreshSession();
250
345
  if (!this.session) {
251
346
  return [];
252
347
  }
253
- return [this.session.namespaces.eip155.accounts[0].split(':')[2]];
348
+ const chains = [];
349
+ // Some wallet (i.e ZenGo) use namespaces.account to list supported chains
350
+ // while others use keys within the namespaces object
351
+ Object.keys(this.session.namespaces).forEach((key) => {
352
+ if (key.startsWith('eip155:')) {
353
+ chains.push(key.split(':')[1]);
354
+ }
355
+ });
356
+ this.session.namespaces.eip155.accounts.forEach((account) => chains.push(account.split(':')[1]));
357
+ return chains.length ? chains : undefined;
358
+ });
359
+ }
360
+ // a HACK for MM to sign message when the selected chain is not mainnet
361
+ promiseWithTimeout(ms, promise) {
362
+ // Create a promise that rejects in <ms> milliseconds
363
+ const timeout = new Promise((_, reject) => {
364
+ const id = setTimeout(() => {
365
+ clearTimeout(id);
366
+ reject(new utils.DynamicError(undefined, 'metamask_timeout'));
367
+ }, ms);
254
368
  });
369
+ // Returns a race between our timeout and the passed in promise
370
+ return Promise.race([promise, timeout]);
255
371
  }
256
372
  }
257
373
 
@@ -12,6 +12,8 @@ export declare class WalletConnectV2 extends EthWalletConnector {
12
12
  connectedChain: Chain;
13
13
  name: string;
14
14
  session: SessionTypes.Struct | undefined;
15
+ activeAccount: string | undefined;
16
+ isInitialized: boolean;
15
17
  canConnectViaQrCode: boolean;
16
18
  isWalletConnect: boolean;
17
19
  private static provider;
@@ -22,6 +24,7 @@ export declare class WalletConnectV2 extends EthWalletConnector {
22
24
  private refreshSession;
23
25
  init(): Promise<void>;
24
26
  get sessionTopicKey(): string;
27
+ get activeAccountKey(): string;
25
28
  supportsNetworkSwitching(): boolean;
26
29
  setupEventListeners(listeners: WalletEventListeners): void;
27
30
  teardownEventListeners(): void;
@@ -29,10 +32,17 @@ export declare class WalletConnectV2 extends EthWalletConnector {
29
32
  getWeb3Provider(): ethers.providers.Web3Provider;
30
33
  fetchPublicAddress(opts?: FetchPublicAddressOpts): Promise<string | undefined>;
31
34
  signMessage(messageToSign: string): Promise<string | undefined>;
35
+ private clearActiveAccount;
36
+ private clearSession;
37
+ private setActiveAccount;
38
+ private setSession;
32
39
  endSession(): Promise<void>;
40
+ getNetwork(): Promise<number | undefined>;
33
41
  providerSwitchNetwork({ network, provider, }: {
34
42
  network: EvmNetwork;
35
43
  provider: ethers.providers.Web3Provider;
36
44
  }): Promise<void>;
37
45
  getConnectedAccounts(): Promise<string[]>;
46
+ getSupportedNetworks(): Promise<string[] | undefined>;
47
+ private promiseWithTimeout;
38
48
  }
@@ -1,17 +1,22 @@
1
1
  import { __awaiter } from '../../_virtual/_tslib.js';
2
2
  import Provider from '@walletconnect/universal-provider';
3
3
  import { ethers } from 'ethers';
4
+ import EventEmitter from 'eventemitter3';
4
5
  import { logger, getDeepLink, performPlatformSpecificConnectionMethod, utf8ToHex } from '@dynamic-labs/wallet-connector-core';
5
6
  import { getWalletBookWallet } from '@dynamic-labs/wallet-book';
6
7
  import { DynamicError, isMobile } from '@dynamic-labs/utils';
7
8
  import { EthWalletConnector } from '../EthWalletConnector.js';
9
+ import { parseIntSafe } from '../utils/parseIntSafe.js';
8
10
 
11
+ const activeAccountKey = (walletName) => `dynamic-wc2-active-account-${walletName}`;
9
12
  const sessionTopicKey = (walletName) => `dynamic-wc2-session-topic-${walletName}`;
13
+ const ee = new EventEmitter();
10
14
  class WalletConnectV2 extends EthWalletConnector {
11
15
  constructor(opts) {
12
16
  super(opts);
13
17
  this.supportedChains = ['EVM', 'ETH'];
14
18
  this.connectedChain = 'EVM';
19
+ this.isInitialized = false;
15
20
  this.canConnectViaQrCode = true;
16
21
  this.isWalletConnect = true;
17
22
  this.name = opts.walletName;
@@ -27,24 +32,39 @@ class WalletConnectV2 extends EthWalletConnector {
27
32
  if (provider === null || provider === void 0 ? void 0 : provider.uri) {
28
33
  return;
29
34
  }
30
- yield provider.connect({
35
+ const defaultChain = 1;
36
+ provider
37
+ .connect({
31
38
  namespaces: {
39
+ eip155: {
40
+ chains: [`eip155:${defaultChain}`],
41
+ events: ['chainChanged', 'accountsChanged'],
42
+ methods: ['personal_sign', 'eth_sendTransaction'],
43
+ rpcMap: this.evmNetworkRpcMap(),
44
+ },
45
+ },
46
+ optionalNamespaces: {
32
47
  eip155: {
33
48
  chains: this.evmNetworks
34
- // filtering out Palm for now since it causes Trust to crash
35
- .filter((network) => network.chainId !== 11297108109)
49
+ // Filters out mainnet which is required and palm that crashes Trust Wallet
50
+ .filter((network) => network.chainId !== 11297108109 &&
51
+ network.chainId !== defaultChain)
36
52
  .map((network) => `eip155:${network.chainId}`),
37
- events: ['chainChanged', 'accountsChanged'],
53
+ events: [],
38
54
  methods: [
39
- 'eth_sendTransaction',
55
+ 'eth_signTypedData',
40
56
  'eth_signTransaction',
41
57
  'eth_sign',
42
58
  'personal_sign',
43
- 'eth_signTypedData',
59
+ 'eth_sendTransaction',
44
60
  ],
45
61
  rpcMap: this.evmNetworkRpcMap(),
46
62
  },
47
63
  },
64
+ })
65
+ .catch((e) => {
66
+ logger.error(e);
67
+ ee.emit('walletconnect_connection_failed');
48
68
  });
49
69
  });
50
70
  }
@@ -60,11 +80,13 @@ class WalletConnectV2 extends EthWalletConnector {
60
80
  });
61
81
  }
62
82
  refreshSession() {
63
- var _a, _b, _c, _d;
83
+ var _a, _b, _c, _d, _e;
64
84
  if ((_b = (_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.topic) {
65
85
  if (localStorage.getItem(this.sessionTopicKey) ===
66
86
  ((_d = (_c = WalletConnectV2.provider) === null || _c === void 0 ? void 0 : _c.session) === null || _d === void 0 ? void 0 : _d.topic)) {
67
87
  this.session = WalletConnectV2.provider.session;
88
+ this.activeAccount =
89
+ (_e = localStorage.getItem(this.activeAccountKey)) !== null && _e !== void 0 ? _e : undefined;
68
90
  }
69
91
  }
70
92
  }
@@ -72,11 +94,15 @@ class WalletConnectV2 extends EthWalletConnector {
72
94
  return __awaiter(this, void 0, void 0, function* () {
73
95
  yield this.initProvider();
74
96
  yield this.initConnection();
97
+ this.isInitialized = true;
75
98
  });
76
99
  }
77
100
  get sessionTopicKey() {
78
101
  return sessionTopicKey(this.key);
79
102
  }
103
+ get activeAccountKey() {
104
+ return activeAccountKey(this.key);
105
+ }
80
106
  supportsNetworkSwitching() {
81
107
  return true;
82
108
  }
@@ -92,18 +118,22 @@ class WalletConnectV2 extends EthWalletConnector {
92
118
  }
93
119
  const { name, data } = params.event;
94
120
  if (name === 'chainChanged') {
95
- if (typeof data !== 'string') {
121
+ const chainId = parseIntSafe(data);
122
+ if (chainId === undefined) {
96
123
  logger.debug(`received unexpected data for chainChanged: ${data} with type ${typeof data}}`);
97
124
  return;
98
125
  }
99
- (_a = listeners.onChainChange) === null || _a === void 0 ? void 0 : _a.call(listeners, data);
126
+ (_a = listeners.onChainChange) === null || _a === void 0 ? void 0 : _a.call(listeners, String(chainId));
100
127
  }
101
128
  else if (name === 'accountsChanged') {
102
- if (typeof data !== 'string' || !Array.isArray(data)) {
129
+ if (!Array.isArray(data)) {
103
130
  logger.debug(`received unexpected data for accountsChanged: ${data} with type ${typeof data}}`);
104
131
  return;
105
132
  }
106
- (_b = listeners.onAccountChange) === null || _b === void 0 ? void 0 : _b.call(listeners, data);
133
+ // eslint-disable-next-line prefer-destructuring
134
+ const account = data[0].split(':')[2];
135
+ this.setActiveAccount(account);
136
+ (_b = listeners.onAccountChange) === null || _b === void 0 ? void 0 : _b.call(listeners, [account]);
107
137
  }
108
138
  });
109
139
  WalletConnectV2.provider.client.on('session_delete', () => __awaiter(this, void 0, void 0, function* () {
@@ -140,15 +170,25 @@ class WalletConnectV2 extends EthWalletConnector {
140
170
  return new ethers.providers.Web3Provider(WalletConnectV2.provider, 'any');
141
171
  }
142
172
  fetchPublicAddress(opts) {
173
+ var _a, _b;
143
174
  return __awaiter(this, void 0, void 0, function* () {
144
- if (this.session) {
145
- return this.session.namespaces.eip155.accounts[0].split(':')[2];
175
+ if (this.activeAccount) {
176
+ return this.activeAccount;
146
177
  }
147
- if (!WalletConnectV2.provider) {
148
- throw new DynamicError('No provider found');
149
- }
150
- if (!WalletConnectV2.provider.uri) {
151
- throw new DynamicError('No uri found');
178
+ if (!WalletConnectV2.provider || !((_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.uri)) {
179
+ logger.debug('No WC2 provider found, re-initializing...');
180
+ yield this.endSession();
181
+ yield this.init();
182
+ // sleep 1 s to wait for connect call to finish
183
+ // the connect call isn't await-ed because it only resolves once
184
+ // the connection is established, but we need to wait for it to
185
+ // finish setting up the connection URI and making it available
186
+ // on the provider
187
+ yield new Promise((resolve) => setTimeout(resolve, 1000));
188
+ if (!WalletConnectV2.provider || !((_b = WalletConnectV2.provider) === null || _b === void 0 ? void 0 : _b.uri)) {
189
+ logger.debug('No WC2 provider found, escaping and throwing error');
190
+ throw new DynamicError('No provider found');
191
+ }
152
192
  }
153
193
  const metadata = getWalletBookWallet(this.name);
154
194
  performPlatformSpecificConnectionMethod(WalletConnectV2.provider.uri, metadata, {
@@ -160,13 +200,21 @@ class WalletConnectV2 extends EthWalletConnector {
160
200
  reject(new DynamicError('No provider found'));
161
201
  return;
162
202
  }
203
+ ee.on('walletconnect_connection_failed', () => {
204
+ const error = new DynamicError('Connection rejected. Please try again.');
205
+ error.code = 'connection_rejected';
206
+ if (WalletConnectV2.provider) {
207
+ WalletConnectV2.provider.uri = undefined;
208
+ }
209
+ reject(error);
210
+ });
163
211
  WalletConnectV2.provider.on('connect', ({ session }) => {
164
212
  if (!session) {
165
213
  reject(new DynamicError('No session found'));
166
214
  }
167
- this.session = session;
168
- localStorage.setItem(this.sessionTopicKey, session.topic);
169
- resolve(this.session.namespaces.eip155.accounts[0].split(':')[2]);
215
+ this.setSession(session);
216
+ this.setActiveAccount(session.namespaces.eip155.accounts[0].split(':')[2]);
217
+ resolve(this.activeAccount);
170
218
  });
171
219
  });
172
220
  });
@@ -186,34 +234,69 @@ class WalletConnectV2 extends EthWalletConnector {
186
234
  const deepLink = getDeepLink({ metadata, mode: 'regular' });
187
235
  window.location.href = deepLink;
188
236
  }
189
- // eslint-disable-next-line prefer-destructuring
190
- const address = this.session.namespaces.eip155.accounts[0].split(':')[2];
191
237
  const web3Provider = this.getWeb3Provider();
192
- return web3Provider.send('personal_sign', [
238
+ const response = web3Provider.send('personal_sign', [
193
239
  utf8ToHex(messageToSign, true),
194
- address,
240
+ this.activeAccount,
195
241
  ]);
242
+ try {
243
+ // A terrible hack to get MM to sign the message when it is on the wrong chain
244
+ // Since MM supports only one chain at a time, it just hangs when we call
245
+ // to sign, and it is not on Mainnet. For some magical reason,
246
+ // when we attempt to call again it just works.
247
+ if (this.key === 'metamask') {
248
+ yield this.promiseWithTimeout(7000, response);
249
+ }
250
+ }
251
+ catch (e) {
252
+ logger.debug('metmask failed to sign message');
253
+ throw e;
254
+ }
255
+ return response;
196
256
  });
197
257
  }
258
+ clearActiveAccount() {
259
+ localStorage.removeItem(this.activeAccountKey);
260
+ this.activeAccount = undefined;
261
+ }
262
+ clearSession() {
263
+ localStorage.removeItem(this.sessionTopicKey);
264
+ this.session = undefined;
265
+ }
266
+ setActiveAccount(account) {
267
+ localStorage.setItem(this.activeAccountKey, account);
268
+ this.activeAccount = account;
269
+ }
270
+ setSession(session) {
271
+ localStorage.setItem(this.sessionTopicKey, session.topic);
272
+ this.session = session;
273
+ }
198
274
  endSession() {
275
+ var _a;
199
276
  return __awaiter(this, void 0, void 0, function* () {
200
- if (!this.session) {
201
- return;
202
- }
203
- if (!WalletConnectV2.provider) {
277
+ this.clearActiveAccount();
278
+ this.clearSession();
279
+ if (!((_a = WalletConnectV2.provider) === null || _a === void 0 ? void 0 : _a.session)) {
204
280
  return;
205
281
  }
206
282
  try {
207
283
  yield WalletConnectV2.provider.disconnect();
208
284
  WalletConnectV2.provider.uri = undefined;
209
- localStorage.removeItem(this.sessionTopicKey);
210
- this.session = undefined;
211
285
  }
212
286
  catch (e) {
213
287
  logger.debug(e);
214
288
  }
215
289
  });
216
290
  }
291
+ getNetwork() {
292
+ const _super = Object.create(null, {
293
+ getNetwork: { get: () => super.getNetwork }
294
+ });
295
+ return __awaiter(this, void 0, void 0, function* () {
296
+ yield this.initProvider();
297
+ return _super.getNetwork.call(this);
298
+ });
299
+ }
217
300
  providerSwitchNetwork({ network, provider, }) {
218
301
  const _super = Object.create(null, {
219
302
  providerSwitchNetwork: { get: () => super.providerSwitchNetwork }
@@ -236,14 +319,46 @@ class WalletConnectV2 extends EthWalletConnector {
236
319
  });
237
320
  }
238
321
  getConnectedAccounts() {
322
+ return __awaiter(this, void 0, void 0, function* () {
323
+ yield this.initProvider();
324
+ this.refreshSession();
325
+ this.isInitialized = true;
326
+ if (!this.activeAccount) {
327
+ return [];
328
+ }
329
+ return [this.activeAccount];
330
+ });
331
+ }
332
+ getSupportedNetworks() {
239
333
  return __awaiter(this, void 0, void 0, function* () {
240
334
  yield this.initProvider();
241
335
  this.refreshSession();
242
336
  if (!this.session) {
243
337
  return [];
244
338
  }
245
- return [this.session.namespaces.eip155.accounts[0].split(':')[2]];
339
+ const chains = [];
340
+ // Some wallet (i.e ZenGo) use namespaces.account to list supported chains
341
+ // while others use keys within the namespaces object
342
+ Object.keys(this.session.namespaces).forEach((key) => {
343
+ if (key.startsWith('eip155:')) {
344
+ chains.push(key.split(':')[1]);
345
+ }
346
+ });
347
+ this.session.namespaces.eip155.accounts.forEach((account) => chains.push(account.split(':')[1]));
348
+ return chains.length ? chains : undefined;
349
+ });
350
+ }
351
+ // a HACK for MM to sign message when the selected chain is not mainnet
352
+ promiseWithTimeout(ms, promise) {
353
+ // Create a promise that rejects in <ms> milliseconds
354
+ const timeout = new Promise((_, reject) => {
355
+ const id = setTimeout(() => {
356
+ clearTimeout(id);
357
+ reject(new DynamicError(undefined, 'metamask_timeout'));
358
+ }, ms);
246
359
  });
360
+ // Returns a race between our timeout and the passed in promise
361
+ return Promise.race([promise, timeout]);
247
362
  }
248
363
  }
249
364