@0xsequence/provider 0.0.0-2021823181734

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 (54) hide show
  1. package/CHANGELOG.md +909 -0
  2. package/README.md +4 -0
  3. package/dist/0xsequence-provider.cjs.d.ts +1 -0
  4. package/dist/0xsequence-provider.cjs.dev.js +3540 -0
  5. package/dist/0xsequence-provider.cjs.js +7 -0
  6. package/dist/0xsequence-provider.cjs.prod.js +3540 -0
  7. package/dist/0xsequence-provider.esm.js +3508 -0
  8. package/dist/declarations/src/commands/index.d.ts +20 -0
  9. package/dist/declarations/src/index.d.ts +5 -0
  10. package/dist/declarations/src/provider.d.ts +59 -0
  11. package/dist/declarations/src/transports/base-provider-transport.d.ts +40 -0
  12. package/dist/declarations/src/transports/base-wallet-transport.d.ts +38 -0
  13. package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +18 -0
  14. package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +12 -0
  15. package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +11 -0
  16. package/dist/declarations/src/transports/extension-transport/index.d.ts +3 -0
  17. package/dist/declarations/src/transports/index.d.ts +7 -0
  18. package/dist/declarations/src/transports/mux-transport/index.d.ts +1 -0
  19. package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +22 -0
  20. package/dist/declarations/src/transports/proxy-transport/index.d.ts +3 -0
  21. package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +15 -0
  22. package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +12 -0
  23. package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +12 -0
  24. package/dist/declarations/src/transports/wallet-request-handler.d.ts +51 -0
  25. package/dist/declarations/src/transports/window-transport/index.d.ts +2 -0
  26. package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +15 -0
  27. package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +13 -0
  28. package/dist/declarations/src/types.d.ts +170 -0
  29. package/dist/declarations/src/utils.d.ts +14 -0
  30. package/dist/declarations/src/wallet.d.ts +99 -0
  31. package/package.json +37 -0
  32. package/src/commands/index.ts +146 -0
  33. package/src/index.ts +5 -0
  34. package/src/provider.ts +494 -0
  35. package/src/transports/base-provider-transport.ts +411 -0
  36. package/src/transports/base-wallet-transport.ts +441 -0
  37. package/src/transports/extension-transport/base-injected-transport.ts +100 -0
  38. package/src/transports/extension-transport/extension-message-handler.ts +25 -0
  39. package/src/transports/extension-transport/extension-message-provider.ts +41 -0
  40. package/src/transports/extension-transport/index.ts +3 -0
  41. package/src/transports/index.ts +7 -0
  42. package/src/transports/mux-transport/index.ts +1 -0
  43. package/src/transports/mux-transport/mux-message-provider.ts +160 -0
  44. package/src/transports/proxy-transport/index.ts +3 -0
  45. package/src/transports/proxy-transport/proxy-message-channel.ts +58 -0
  46. package/src/transports/proxy-transport/proxy-message-handler.ts +46 -0
  47. package/src/transports/proxy-transport/proxy-message-provider.ts +81 -0
  48. package/src/transports/wallet-request-handler.ts +702 -0
  49. package/src/transports/window-transport/index.ts +2 -0
  50. package/src/transports/window-transport/window-message-handler.ts +166 -0
  51. package/src/transports/window-transport/window-message-provider.ts +183 -0
  52. package/src/types.ts +252 -0
  53. package/src/utils.ts +76 -0
  54. package/src/wallet.ts +692 -0
@@ -0,0 +1,3508 @@
1
+ import { EventEmitter2 } from 'eventemitter2';
2
+ import { logger, packMessageData, sanitizeNumberString, sanitizeHost, sanitizeAlphanumeric, base64EncodeObject, base64DecodeObject, shallowCopy, resolveProperties, encodeMessageDigest, encodeTypedDataDigest } from '@0xsequence/utils';
3
+ import { ethers, BigNumber } from 'ethers';
4
+ import { addressOf } from '@0xsequence/config';
5
+ import { isValidSignature as isValidSignature$1, recoverConfig, Signer, resolveArrayProperties } from '@0xsequence/wallet';
6
+ import { isSignedTransactions } from '@0xsequence/transactions';
7
+ import { signAuthorization } from '@0xsequence/auth';
8
+ import { JsonRpcSender, maybeChainId, allowProviderMiddleware, networkProviderMiddleware, CachedProvider, JsonRpcRouter, loggingProviderMiddleware, exceptionProviderMiddleware, findNetworkConfig, EagerProvider, SigningProvider, updateNetworkConfig, ensureValidNetworks } from '@0xsequence/network';
9
+ import { Web3Provider as Web3Provider$1, JsonRpcProvider } from '@ethersproject/providers';
10
+
11
+ function _extends() {
12
+ _extends = Object.assign || function (target) {
13
+ for (var i = 1; i < arguments.length; i++) {
14
+ var source = arguments[i];
15
+
16
+ for (var key in source) {
17
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
18
+ target[key] = source[key];
19
+ }
20
+ }
21
+ }
22
+
23
+ return target;
24
+ };
25
+
26
+ return _extends.apply(this, arguments);
27
+ }
28
+
29
+ // ProviderMessageCallback is used to respond to ProviderMessage requests. The error
30
+ // argument is for exceptions during the execution, and response is the response payload
31
+ // which may contain the result or an error payload from the wallet.
32
+ class WindowSessionParams extends URLSearchParams {
33
+ static new(init) {
34
+ return new URLSearchParams(init);
35
+ }
36
+
37
+ }
38
+ let EventType;
39
+
40
+ (function (EventType) {
41
+ EventType["OPEN"] = "open";
42
+ EventType["CLOSE"] = "close";
43
+ EventType["MESSAGE"] = "message";
44
+ EventType["CONNECT"] = "connect";
45
+ EventType["DISCONNECT"] = "disconnect";
46
+ EventType["ACCOUNTS_CHANGED"] = "accountsChanged";
47
+ EventType["CHAIN_CHANGED"] = "chainChanged";
48
+ EventType["NETWORKS"] = "networks";
49
+ EventType["WALLET_CONTEXT"] = "walletContext";
50
+ EventType["INIT"] = "init";
51
+ EventType["DEBUG"] = "_debug";
52
+ })(EventType || (EventType = {}));
53
+
54
+ let OpenState;
55
+
56
+ (function (OpenState) {
57
+ OpenState[OpenState["CLOSED"] = 0] = "CLOSED";
58
+ OpenState[OpenState["OPENING"] = 1] = "OPENING";
59
+ OpenState[OpenState["OPENED"] = 2] = "OPENED";
60
+ })(OpenState || (OpenState = {}));
61
+
62
+ let InitState;
63
+
64
+ (function (InitState) {
65
+ InitState[InitState["NIL"] = 0] = "NIL";
66
+ InitState[InitState["SENT_NONCE"] = 1] = "SENT_NONCE";
67
+ InitState[InitState["OK"] = 2] = "OK";
68
+ })(InitState || (InitState = {}));
69
+
70
+ class ProviderError extends Error {
71
+ constructor(message) {
72
+ super(message);
73
+ this.name = 'ProviderError';
74
+ }
75
+
76
+ }
77
+ const ErrSignedInRequired = new ProviderError('Wallet is not signed in. Connect a wallet and try again.'); // TODO: lets build some nice error handling tools, prob in /utils ...
78
+
79
+ const PROVIDER_OPEN_TIMEOUT = 30000; // in ms
80
+
81
+ let _messageIdx = 0;
82
+ const nextMessageIdx = () => ++_messageIdx;
83
+ class BaseProviderTransport {
84
+ constructor() {
85
+ var _this = this;
86
+
87
+ this.pendingMessageRequests = [];
88
+ this.responseCallbacks = new Map();
89
+ this.state = void 0;
90
+ this.confirmationOnly = false;
91
+ this.events = new EventEmitter2();
92
+ this.openPayload = void 0;
93
+ this.connectPayload = void 0;
94
+ this.accountsChangedPayload = void 0;
95
+ this.networksPayload = void 0;
96
+ this.walletContextPayload = void 0;
97
+ this._sessionId = void 0;
98
+ this._init = void 0;
99
+ this._registered = void 0;
100
+
101
+ this.sendAsync = async function (request, callback, chainId) {
102
+ // here, we receive the message from the dapp provider call
103
+ if (_this.state === OpenState.CLOSED) {
104
+ // flag the wallet to auto-close once user submits input. ie.
105
+ // prompting to sign a message or transaction
106
+ _this.confirmationOnly = true;
107
+ } // open/focus the wallet.
108
+ // automatically open the wallet when a provider request makes it here.
109
+ //
110
+ // NOTE: if we're not signed in, then the provider will fail, users must first connect+sign in.
111
+ //
112
+ // TODO: how does this behave with a session has expired?
113
+
114
+
115
+ _this.openWallet(undefined, {
116
+ type: 'jsonRpcRequest',
117
+ method: request.method
118
+ }, chainId); // send message request, await, and then execute callback after receiving the response
119
+
120
+
121
+ try {
122
+ if (!_this.isOpened()) {
123
+ await _this.waitUntilOpened(); // will throw on timeout
124
+ }
125
+
126
+ const response = await _this.sendMessageRequest({
127
+ idx: nextMessageIdx(),
128
+ type: EventType.MESSAGE,
129
+ data: request,
130
+ chainId: chainId
131
+ });
132
+ callback(undefined, response.data);
133
+ } catch (err) {
134
+ callback(err);
135
+ }
136
+ };
137
+
138
+ this.sendMessageRequest = async function (message) {
139
+ return new Promise((resolve, reject) => {
140
+ if ((!message.idx || message.idx <= 0) && message.type !== 'init') {
141
+ reject(new Error('message idx not set'));
142
+ }
143
+
144
+ const responseCallback = (error, response) => {
145
+ if (error) {
146
+ reject(error);
147
+ } else if (response) {
148
+ resolve(response);
149
+ } else {
150
+ throw new Error('no valid response to return');
151
+ }
152
+ };
153
+
154
+ const idx = message.idx;
155
+
156
+ if (!_this.responseCallbacks.get(idx)) {
157
+ _this.responseCallbacks.set(idx, responseCallback);
158
+ } else {
159
+ reject(new Error('duplicate message idx, should never happen'));
160
+ }
161
+
162
+ if (!_this.isOpened()) {
163
+ logger.debug('pushing to pending requests', message);
164
+
165
+ _this.pendingMessageRequests.push(message);
166
+ } else {
167
+ _this.sendMessage(message);
168
+ }
169
+ });
170
+ };
171
+
172
+ this.waitUntilOpened = async function (openTimeout = PROVIDER_OPEN_TIMEOUT) {
173
+ let opened = false;
174
+ return Promise.race([new Promise((_, reject) => {
175
+ const timeout = setTimeout(() => {
176
+ clearTimeout(timeout); // only emit close if the timeout wins the race
177
+
178
+ if (!opened) {
179
+ _this.state = OpenState.CLOSED;
180
+
181
+ _this.events.emit('close', {
182
+ code: 1005,
183
+ message: 'opening wallet timed out'
184
+ });
185
+ }
186
+
187
+ reject(new Error('opening wallet timed out'));
188
+ }, openTimeout);
189
+ }), new Promise(resolve => {
190
+ if (_this.isOpened()) {
191
+ var _this$openPayload;
192
+
193
+ opened = true;
194
+ resolve((_this$openPayload = _this.openPayload) == null ? void 0 : _this$openPayload.session);
195
+ return;
196
+ }
197
+
198
+ _this.events.once('open', openInfo => {
199
+ _this.openPayload = openInfo;
200
+ opened = true;
201
+ resolve(openInfo.session);
202
+ });
203
+ })]);
204
+ };
205
+
206
+ this.waitUntilConnected = async function () {
207
+ await _this.waitUntilOpened();
208
+ const connect = new Promise(resolve => {
209
+ if (_this.connectPayload) {
210
+ resolve(_this.connectPayload);
211
+ return;
212
+ }
213
+
214
+ _this.events.once('connect', connectDetails => {
215
+ _this.connectPayload = connectDetails;
216
+ resolve(connectDetails);
217
+ });
218
+ });
219
+ const closeWallet = new Promise((_, reject) => {
220
+ _this.events.once('close', error => {
221
+ if (error) {
222
+ reject(new Error(`wallet closed due to ${JSON.stringify(error)}`));
223
+ } else {
224
+ reject(new Error(`user closed the wallet`));
225
+ }
226
+ });
227
+ });
228
+ return Promise.race([connect, closeWallet]);
229
+ };
230
+
231
+ this.state = OpenState.CLOSED;
232
+ this._registered = false;
233
+ this._init = InitState.NIL;
234
+ }
235
+
236
+ get registered() {
237
+ return this._registered;
238
+ }
239
+
240
+ register() {
241
+ throw new Error('abstract method');
242
+ }
243
+
244
+ unregister() {
245
+ throw new Error('abstract method');
246
+ }
247
+
248
+ openWallet(path, intent, networkId) {
249
+ throw new Error('abstract method');
250
+ }
251
+
252
+ closeWallet() {
253
+ throw new Error('abstract method');
254
+ }
255
+
256
+ isOpened() {
257
+ return this.registered && this.state === OpenState.OPENED;
258
+ }
259
+
260
+ isConnected() {
261
+ var _this$openPayload2;
262
+
263
+ // if we're registered, and we have the account details, then we are connected
264
+ const session = (_this$openPayload2 = this.openPayload) == null ? void 0 : _this$openPayload2.session;
265
+ return this.registered && session !== undefined && !!session.accountAddress && session.accountAddress.length === 42 && !!session.networks && session.networks.length > 0;
266
+ }
267
+
268
+ // handleMessage will handle message received from the remote wallet
269
+ handleMessage(message) {
270
+ var _this2 = this;
271
+
272
+ // init incoming for initial handshake with transport.
273
+ // always respond to INIT messages, e.g. on popup window reload
274
+ if (message.type === EventType.INIT) {
275
+ logger.debug('MessageProvider, received INIT message', message);
276
+ const {
277
+ nonce
278
+ } = message.data;
279
+
280
+ if (!nonce || nonce.length == 0) {
281
+ logger.error('invalid init nonce');
282
+ return;
283
+ }
284
+
285
+ this._init = InitState.OK;
286
+ this.sendMessage({
287
+ idx: -1,
288
+ type: EventType.INIT,
289
+ data: {
290
+ sessionId: this._sessionId,
291
+ nonce: nonce
292
+ }
293
+ });
294
+ }
295
+
296
+ if (this._init !== InitState.OK) {
297
+ // if provider is not init'd, then we drop any received messages. the only
298
+ // message we will process is of event type 'init', as our acknowledgement
299
+ return;
300
+ } // message is either a notification, or its a ProviderMessageResponse
301
+
302
+
303
+ logger.debug('RECEIVED MESSAGE FROM WALLET', message.idx, message);
304
+ const requestIdx = message.idx;
305
+ const responseCallback = this.responseCallbacks.get(requestIdx);
306
+
307
+ if (requestIdx) {
308
+ this.responseCallbacks.delete(requestIdx);
309
+ } // OPEN response
310
+ //
311
+ // Flip opened flag, and flush the pending queue
312
+
313
+
314
+ if (message.type === EventType.OPEN && !this.isOpened()) {
315
+ var _message$data, _message$data2;
316
+
317
+ if (this._sessionId && this._sessionId !== ((_message$data = message.data) == null ? void 0 : _message$data.sessionId)) {
318
+ logger.debug('open event received from wallet, but does not match sessionId', this._sessionId);
319
+ return;
320
+ } // check if open error occured due to invalid defaultNetworkId
321
+
322
+
323
+ if ((_message$data2 = message.data) != null && _message$data2.error) {
324
+ var _message$data3;
325
+
326
+ const err = new Error(`opening wallet failed: received ${(_message$data3 = message.data) == null ? void 0 : _message$data3.error}`);
327
+ logger.error(err);
328
+ this.close();
329
+ throw err;
330
+ } // success!
331
+
332
+
333
+ this.state = OpenState.OPENED;
334
+ this.openPayload = message.data;
335
+ this.events.emit('open', this.openPayload); // flush pending requests when connected
336
+
337
+ if (this.pendingMessageRequests.length !== 0) {
338
+ const pendingMessageRequests = this.pendingMessageRequests.splice(0, this.pendingMessageRequests.length);
339
+ pendingMessageRequests.forEach(async function (pendingMessageRequest) {
340
+ _this2.sendMessage(pendingMessageRequest);
341
+ });
342
+ }
343
+
344
+ return;
345
+ } // MESSAGE resposne
346
+
347
+
348
+ if (message.type === EventType.MESSAGE) {
349
+ // Require user confirmation, bring up wallet to prompt for input then close
350
+ // TODO: perhaps apply technique like in multicall to queue messages within
351
+ // a period of time, then close the window if responseCallbacks is empty, this is better.
352
+ if (this.confirmationOnly) {
353
+ setTimeout(() => {
354
+ if (this.responseCallbacks.size === 0) {
355
+ this.closeWallet();
356
+ }
357
+ }, 500); // TODO: be smarter about timer as we're processing the response callbacks..
358
+ }
359
+
360
+ if (!responseCallback) {
361
+ // NOTE: this would occur if 'idx' isn't set, which should never happen
362
+ // or when we register two handler, or duplicate messages with the same idx are sent,
363
+ // all of which should be prevented prior to getting to this point
364
+ throw new Error('impossible state');
365
+ } // Callback to original caller
366
+
367
+
368
+ if (responseCallback) {
369
+ this.events.emit('message', message);
370
+ responseCallback(message.data.error, message);
371
+ return;
372
+ }
373
+ } // ACCOUNTS_CHANGED -- when a user logs in or out
374
+
375
+
376
+ if (message.type === EventType.ACCOUNTS_CHANGED) {
377
+ this.accountsChangedPayload = undefined;
378
+
379
+ if (message.data && message.data.length > 0) {
380
+ this.accountsChangedPayload = ethers.utils.getAddress(message.data[0]);
381
+ this.events.emit('accountsChanged', [this.accountsChangedPayload]);
382
+ } else {
383
+ this.events.emit('accountsChanged', []);
384
+ }
385
+
386
+ return;
387
+ } // CHAIN_CHANGED -- when a user changes their default chain
388
+
389
+
390
+ if (message.type === EventType.CHAIN_CHANGED) {
391
+ this.events.emit('chainChanged', message.data);
392
+ return;
393
+ } // NOTIFY NETWORKS -- when a user connects or logs in
394
+
395
+
396
+ if (message.type === EventType.NETWORKS) {
397
+ this.networksPayload = message.data;
398
+ this.events.emit('networks', this.networksPayload);
399
+ return;
400
+ } // NOTIFY WALLET_CONTEXT -- when a user connects or logs in
401
+
402
+
403
+ if (message.type === EventType.WALLET_CONTEXT) {
404
+ this.walletContextPayload = message.data;
405
+ this.events.emit('walletContext', this.walletContextPayload);
406
+ return;
407
+ } // NOTIFY CLOSE -- when wallet instructs to close
408
+
409
+
410
+ if (message.type === EventType.CLOSE) {
411
+ if (this.state !== OpenState.CLOSED) {
412
+ this.close(message.data);
413
+ }
414
+ } // NOTIFY CONNECT -- when wallet instructs we've connected
415
+
416
+
417
+ if (message.type === EventType.CONNECT) {
418
+ this.connectPayload = message.data;
419
+ this.events.emit('connect', this.connectPayload);
420
+ } // NOTIFY DISCONNECT -- when wallet instructs to disconnect
421
+
422
+
423
+ if (message.type === EventType.DISCONNECT) {
424
+ if (this.isConnected()) {
425
+ this.events.emit('disconnect', message.data);
426
+ this.close();
427
+ }
428
+ }
429
+ } // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet
430
+
431
+
432
+ sendMessage(message) {
433
+ throw new Error('abstract method');
434
+ }
435
+
436
+ on(event, fn) {
437
+ this.events.on(event, fn);
438
+ }
439
+
440
+ once(event, fn) {
441
+ this.events.once(event, fn);
442
+ }
443
+
444
+ emit(event, ...args) {
445
+ return this.events.emit(event, ...args);
446
+ }
447
+
448
+ close(error) {
449
+ if (this.state === OpenState.CLOSED) return;
450
+ this.state = OpenState.CLOSED;
451
+ this.confirmationOnly = false;
452
+ this._sessionId = undefined;
453
+ logger.info('closing wallet and flushing!'); // flush pending requests and return error to all callbacks
454
+
455
+ this.pendingMessageRequests.length = 0;
456
+ this.responseCallbacks.forEach(responseCallback => {
457
+ responseCallback(_extends({}, new Error('wallet closed'), {
458
+ code: 4001
459
+ }));
460
+ });
461
+ this.responseCallbacks.clear();
462
+ this.connectPayload = undefined;
463
+ this.openPayload = undefined;
464
+ this.accountsChangedPayload = undefined;
465
+ this.networksPayload = undefined;
466
+ this.walletContextPayload = undefined;
467
+ this.events.emit('close', error);
468
+ }
469
+
470
+ }
471
+
472
+ const isValidSignature = async (address, digest, sig, provider, chainId, walletContext) => {
473
+ chainId = chainId || (await provider.getChainId());
474
+ walletContext = walletContext || (await provider.getSigner().getWalletContext());
475
+ return isValidSignature$1(address, digest, sig, provider, walletContext, chainId);
476
+ };
477
+ const recoverWalletConfig = async (address, digest, signature, chainId, walletContext) => {
478
+ const subDigest = packMessageData(address, chainId, digest);
479
+ const config = await recoverConfig(subDigest, signature);
480
+
481
+ if (walletContext) {
482
+ const recoveredWalletAddress = addressOf(config, walletContext);
483
+
484
+ if (config.address && config.address !== recoveredWalletAddress) {
485
+ throw new Error('recovered address does not match the WalletConfig address, check the WalletContext');
486
+ } else {
487
+ config.address = recoveredWalletAddress;
488
+ }
489
+ }
490
+
491
+ return config;
492
+ };
493
+ const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:'; // window.localstorage helper
494
+
495
+ class LocalStore {
496
+ constructor(key, def) {
497
+ this.def = def;
498
+ this.key = void 0;
499
+ this.key = key;
500
+ }
501
+
502
+ get() {
503
+ const val = window.localStorage.getItem(this.key);
504
+
505
+ if (val === null) {
506
+ return this.def;
507
+ }
508
+
509
+ try {
510
+ return JSON.parse(val);
511
+ } catch (err) {
512
+ console.error(err);
513
+ }
514
+
515
+ return;
516
+ }
517
+
518
+ set(val) {
519
+ val ? window.localStorage.setItem(this.key, JSON.stringify(val)) : window.localStorage.removeItem(this.key);
520
+ }
521
+
522
+ del() {
523
+ window.localStorage.removeItem(this.key);
524
+ }
525
+
526
+ }
527
+
528
+ const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession';
529
+ class BaseWalletTransport {
530
+ // appOrigin identifies the dapp's origin which opened the app. A transport
531
+ // will auto-detect and set this value if it can. This is determined
532
+ // as the parent app/window which opened the wallet.
533
+ constructor(walletRequestHandler) {
534
+ var _this = this;
535
+
536
+ this.walletRequestHandler = void 0;
537
+ this._sessionId = void 0;
538
+ this._registered = void 0;
539
+ this._init = void 0;
540
+ this._initNonce = void 0;
541
+ this._initCallback = void 0;
542
+ this.appOrigin = void 0;
543
+
544
+ this.sendAsync = async function (request, callback, chainId) {
545
+ throw new Error('abstract method');
546
+ };
547
+
548
+ this.handleMessage = async function (message) {
549
+ const request = message; // ensure initial handshake is complete before accepting
550
+ // other kinds of messages.
551
+
552
+ if (_this._init !== InitState.OK) {
553
+ if (request.type === EventType.INIT) {
554
+ if (_this.isValidInitAck(message)) {
555
+ // successful init
556
+ if (_this._initCallback) _this._initCallback();
557
+ } else {
558
+ // failed init
559
+ if (_this._initCallback) _this._initCallback('invalid init');
560
+ return;
561
+ }
562
+ }
563
+
564
+ return;
565
+ } // handle request
566
+
567
+
568
+ switch (request.type) {
569
+ case EventType.OPEN:
570
+ {
571
+ if (_this._init !== InitState.OK) return;
572
+ const session = {
573
+ sessionId: request.data.sessionId,
574
+ intent: request.data.intent,
575
+ networkId: request.data.networkId
576
+ };
577
+ await _this.open(session);
578
+ return;
579
+ }
580
+
581
+ case EventType.CLOSE:
582
+ {
583
+ if (_this._init !== InitState.OK) return; // noop. just here to capture the message so event emitters may be notified
584
+
585
+ return;
586
+ }
587
+
588
+ case EventType.MESSAGE:
589
+ {
590
+ const response = await _this.walletRequestHandler.sendMessageRequest(request);
591
+
592
+ _this.sendMessage(response);
593
+
594
+ if (response.data.error) ;
595
+
596
+ return;
597
+ }
598
+
599
+ default:
600
+ {
601
+ logger.error(`unexpected payload type ${request.type}`);
602
+ }
603
+ }
604
+ };
605
+
606
+ this.sendMessageRequest = async function (message) {
607
+ return _this.walletRequestHandler.sendMessageRequest(message);
608
+ };
609
+
610
+ this.open = async function ({
611
+ sessionId,
612
+ intent,
613
+ networkId
614
+ }) {
615
+ if (sessionId) {
616
+ _this._sessionId = sanitizeNumberString(sessionId); // persist transport session in localstorage for restoring after redirect/reload
617
+
618
+ _this.saveTransportSession({
619
+ sessionId,
620
+ intent,
621
+ networkId
622
+ });
623
+ } // init handshake for certain transports, before we can open the communication.
624
+ //
625
+ // for example, with the window-transport, we have to exchange messages to determine the
626
+ // origin host of the dapp.
627
+
628
+
629
+ await _this.init(); // Prepare connect options from intent
630
+
631
+ if (intent && intent.type === 'connect' && intent.options) {
632
+ const connectOptions = intent.options;
633
+ const authorizeOptions = connectOptions; // overlapping types
634
+ // Sanity/integrity check the intent payload, and set authorization origin
635
+ // if its been determined as part of the init handshake from earlier.
636
+
637
+ if (_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
638
+ if (!isBrowserExtension()) {
639
+ if (authorizeOptions.origin !== _this.appOrigin) {
640
+ throw new Error('origin is invalid');
641
+ }
642
+ }
643
+ } else if (!_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
644
+ // ie. when we can't determine the origin in our transport, but dapp provides it to us.
645
+ // we just sanitize the origin host.
646
+ connectOptions.origin = sanitizeHost(authorizeOptions.origin);
647
+ } else if (_this.appOrigin) {
648
+ // ie. when we auto-determine the origin such as in window-transport
649
+ connectOptions.origin = _this.appOrigin;
650
+ }
651
+
652
+ if (connectOptions.app) {
653
+ connectOptions.app = sanitizeAlphanumeric(connectOptions.app);
654
+ } // Set connect options on the walletRequestHandler as our primary
655
+ // wallet controller
656
+
657
+
658
+ _this.walletRequestHandler.setConnectOptions(connectOptions);
659
+
660
+ if (connectOptions.networkId) {
661
+ networkId = connectOptions.networkId;
662
+ }
663
+ } else {
664
+ _this.walletRequestHandler.setConnectOptions(undefined);
665
+ } // Notify open and proceed to prompt for connection if intended
666
+
667
+
668
+ if (!_this.walletRequestHandler.isSignedIn()) {
669
+ // open wallet without a specific connected chainId, as the user is not signed in
670
+ _this.notifyOpen({
671
+ sessionId: _this._sessionId
672
+ });
673
+
674
+ return true;
675
+ } else {
676
+ // Set default network, in case of error chainId will be undefined or 0
677
+ let chainId = undefined;
678
+
679
+ try {
680
+ if (networkId) {
681
+ chainId = await _this.walletRequestHandler.setDefaultNetwork(networkId, false);
682
+ } else {
683
+ chainId = await _this.walletRequestHandler.getChainId();
684
+ }
685
+ } catch (err) {
686
+ console.error(err);
687
+ } // Failed to set default network on open -- quit + close
688
+
689
+
690
+ if (!chainId || chainId <= 0) {
691
+ _this.notifyOpen({
692
+ sessionId: _this._sessionId,
693
+ error: `failed to open wallet on network ${networkId}`
694
+ });
695
+
696
+ return false;
697
+ } // prompt user with a connect request. the options will be used as previously set above.
698
+ // upon success, the walletRequestHandler will notify the dapp with the ConnectDetails.
699
+ // upon cancellation by user, the walletRequestHandler will throw an error
700
+
701
+
702
+ if (intent && intent.type === 'connect') {
703
+ // notify wallet is opened, without session details
704
+ _this.notifyOpen({
705
+ sessionId: _this._sessionId
706
+ });
707
+
708
+ try {
709
+ const connectDetails = await _this.walletRequestHandler.promptConnect(intent.options);
710
+
711
+ if (connectDetails.connected) {
712
+ _this.walletRequestHandler.notifyConnect(connectDetails);
713
+ }
714
+ } catch (err) {
715
+ logger.warn('promptConnect not connected:', err);
716
+ } finally {
717
+ // auto-close by default, unless intent is to keep open
718
+ if (!intent.options || intent.options.keepWalletOpened !== true) {
719
+ _this.notifyClose();
720
+ }
721
+ }
722
+ } else {
723
+ // user is already connected, notify session details.
724
+ // TODO: in future, keep list if 'connected' dapps / sessions in the session
725
+ // controller, and only sync with allowed apps
726
+ _this.notifyOpen({
727
+ sessionId: _this._sessionId,
728
+ chainId: `${chainId}`,
729
+ session: await _this.walletRequestHandler.walletSession()
730
+ });
731
+ }
732
+ }
733
+
734
+ return true;
735
+ };
736
+
737
+ this.saveTransportSession = session => {
738
+ window.localStorage.setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
739
+ };
740
+
741
+ this.getCachedTransportSession = () => {
742
+ const session = window.localStorage.getItem(TRANSPORT_SESSION_LS_KEY);
743
+
744
+ try {
745
+ return session ? JSON.parse(session) : null;
746
+ } catch (err) {
747
+ console.error(`unable to parse transport session: ${session}`);
748
+ return null;
749
+ }
750
+ };
751
+
752
+ this.walletRequestHandler = walletRequestHandler;
753
+ this._init = InitState.NIL;
754
+ this.walletRequestHandler.on('connect', connectDetails => {
755
+ if (!this.registered) return; // means user has logged in and wallet is connected to the app
756
+
757
+ this.notifyConnect(connectDetails);
758
+ });
759
+ this.walletRequestHandler.on('disconnect', error => {
760
+ if (!this.registered) return; // means user has logged out the app / disconnected wallet from the app
761
+
762
+ this.notifyDisconnect(error);
763
+ });
764
+ this.walletRequestHandler.on('accountsChanged', accounts => {
765
+ if (!this.registered) return;
766
+ this.notifyAccountsChanged(accounts);
767
+ });
768
+ this.walletRequestHandler.on('networks', networks => {
769
+ if (!this.registered) return;
770
+ this.notifyNetworks(networks);
771
+
772
+ if (!networks || networks.length === 0) {
773
+ this.notifyChainChanged('0x0');
774
+ } else {
775
+ this.notifyChainChanged(ethers.utils.hexlify(networks[0].chainId));
776
+ }
777
+ });
778
+ this.walletRequestHandler.on('walletContext', walletContext => {
779
+ if (!this.registered || !walletContext) return;
780
+ this.notifyWalletContext(walletContext);
781
+ });
782
+ this.walletRequestHandler.on('close', error => {
783
+ if (!this.registered) return;
784
+ this.notifyClose(error);
785
+ });
786
+ }
787
+
788
+ get registered() {
789
+ return this._registered;
790
+ }
791
+
792
+ register() {
793
+ throw new Error('abstract method');
794
+ }
795
+
796
+ unregister() {
797
+ throw new Error('abstract method');
798
+ }
799
+
800
+ sendMessage(message) {
801
+ throw new Error('abstract method');
802
+ }
803
+
804
+ notifyOpen(openInfo) {
805
+ const {
806
+ chainId,
807
+ sessionId,
808
+ session,
809
+ error
810
+ } = openInfo;
811
+ this.sendMessage({
812
+ idx: -1,
813
+ type: EventType.OPEN,
814
+ data: {
815
+ chainId,
816
+ sessionId,
817
+ session,
818
+ error
819
+ }
820
+ });
821
+ }
822
+
823
+ notifyClose(error) {
824
+ this.sendMessage({
825
+ idx: -1,
826
+ type: EventType.CLOSE,
827
+ data: error ? {
828
+ error
829
+ } : null
830
+ });
831
+ }
832
+
833
+ notifyConnect(connectDetails) {
834
+ this.sendMessage({
835
+ idx: -1,
836
+ type: EventType.CONNECT,
837
+ data: connectDetails
838
+ });
839
+ }
840
+
841
+ notifyDisconnect(error) {
842
+ this.sendMessage({
843
+ idx: -1,
844
+ type: EventType.DISCONNECT,
845
+ data: error ? {
846
+ error
847
+ } : null
848
+ });
849
+ }
850
+
851
+ notifyAccountsChanged(accounts) {
852
+ this.sendMessage({
853
+ idx: -1,
854
+ type: EventType.ACCOUNTS_CHANGED,
855
+ data: accounts
856
+ });
857
+ }
858
+
859
+ notifyChainChanged(chainIdHex) {
860
+ this.sendMessage({
861
+ idx: -1,
862
+ type: EventType.CHAIN_CHANGED,
863
+ data: chainIdHex
864
+ });
865
+ }
866
+
867
+ notifyNetworks(networks) {
868
+ this.sendMessage({
869
+ idx: -1,
870
+ type: EventType.NETWORKS,
871
+ data: networks
872
+ });
873
+ }
874
+
875
+ notifyWalletContext(walletContext) {
876
+ this.sendMessage({
877
+ idx: -1,
878
+ type: EventType.WALLET_CONTEXT,
879
+ data: walletContext
880
+ });
881
+ }
882
+
883
+ isValidInitAck(message) {
884
+ if (this._init === InitState.OK) {
885
+ // we're already in init state, we shouldn't handle this message
886
+ logger.warn("isValidInitAck, already in init'd state, so inquiry is invalid.");
887
+ return false;
888
+ }
889
+
890
+ if (message.type !== EventType.INIT) {
891
+ logger.warn('isValidInitAck, invalid message type, expecting init');
892
+ return false;
893
+ }
894
+
895
+ const {
896
+ sessionId,
897
+ nonce
898
+ } = message.data;
899
+
900
+ if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) {
901
+ logger.error('invalid init ack');
902
+ return false;
903
+ }
904
+
905
+ if (sessionId !== this._sessionId || nonce !== this._initNonce) {
906
+ logger.error('invalid init ack match');
907
+ return false;
908
+ } // all checks pass, its true
909
+
910
+
911
+ return true;
912
+ }
913
+
914
+ init() {
915
+ return new Promise((resolve, reject) => {
916
+ // avoid re-init`ing, or if there is a transport which doesn't require
917
+ // it, then it may set this._init to OK in its constructor.
918
+ if (this._init === InitState.OK) {
919
+ resolve();
920
+ return;
921
+ }
922
+
923
+ if (this._init !== InitState.NIL || this._initCallback) {
924
+ reject('transport init is in progress');
925
+ return;
926
+ } // start init timeout, if we don't receive confirmation
927
+ // from provider within this amount of time, then we timeout
928
+
929
+
930
+ const initTimeout = setTimeout(() => {
931
+ logger.warn('transport init timed out');
932
+
933
+ if (this._initCallback) {
934
+ this._initCallback('transport init timed out');
935
+ }
936
+ }, PROVIDER_OPEN_TIMEOUT / 2); // setup callback as we receive the init message async in the handleMessage function
937
+
938
+ this._initCallback = error => {
939
+ this._initCallback = undefined; // reset
940
+
941
+ clearTimeout(initTimeout);
942
+
943
+ if (error) {
944
+ reject(error);
945
+ } else {
946
+ this._init = InitState.OK;
947
+ resolve();
948
+ }
949
+ }; // send init request with random nonce to the provider, where we expect
950
+ // for the provider to echo it back to us as complete handshake
951
+
952
+
953
+ this._initNonce = `${performance.now()}`;
954
+ this.sendMessage({
955
+ idx: -1,
956
+ type: EventType.INIT,
957
+ data: {
958
+ nonce: this._initNonce
959
+ }
960
+ });
961
+ this._init = InitState.SENT_NONCE; // NOTE: the promise will resolve in the _initCallback method
962
+ // which will be called from either handleMessage or the initTimeout
963
+ });
964
+ }
965
+
966
+ }
967
+
968
+ class ProxyMessageChannel {
969
+ constructor() {
970
+ this.app = void 0;
971
+ this.wallet = void 0;
972
+ const port1 = new ProxyMessageChannelPort();
973
+ const port2 = new ProxyMessageChannelPort();
974
+ port1.conn = port2;
975
+ port2.conn = port1;
976
+ this.app = port1;
977
+ this.wallet = port2;
978
+ }
979
+
980
+ }
981
+ class ProxyMessageChannelPort {
982
+ constructor() {
983
+ this.conn = void 0;
984
+ this.events = new EventEmitter2();
985
+
986
+ this.handleMessage = message => {
987
+ throw new Error('ProxyMessageChannelPort is not registered');
988
+ };
989
+
990
+ this.sendMessage = message => {
991
+ this.conn.handleMessage(message); // trigger events
992
+
993
+ if (message.type === 'open') {
994
+ this.events.emit('open', message);
995
+ }
996
+
997
+ if (message.type === 'close') {
998
+ this.events.emit('close', message);
999
+ }
1000
+
1001
+ if (message.type === 'connect') {
1002
+ this.events.emit('connect', message);
1003
+ }
1004
+
1005
+ if (message.type === 'disconnect') {
1006
+ this.events.emit('disconnect', message);
1007
+ }
1008
+ };
1009
+ }
1010
+
1011
+ on(event, fn) {
1012
+ this.events.on(event, fn);
1013
+ }
1014
+
1015
+ once(event, fn) {
1016
+ this.events.once(event, fn);
1017
+ }
1018
+
1019
+ }
1020
+
1021
+ class ProxyMessageProvider extends BaseProviderTransport {
1022
+ constructor(port) {
1023
+ super();
1024
+ this.port = void 0;
1025
+
1026
+ this.register = () => {
1027
+ this.port.handleMessage = message => {
1028
+ this.handleMessage(message);
1029
+ };
1030
+
1031
+ this.on('open', (...args) => {
1032
+ this.port.events.emit('open', ...args);
1033
+ });
1034
+ this.on('close', (...args) => {
1035
+ this.port.events.emit('close', ...args);
1036
+ });
1037
+ this.on('connect', (...args) => {
1038
+ this.port.events.emit('connect', ...args);
1039
+ });
1040
+ this.on('disconnect', (...args) => {
1041
+ this.port.events.emit('disconnect', ...args);
1042
+ });
1043
+ this._registered = true;
1044
+ };
1045
+
1046
+ this.unregister = () => {
1047
+ this._registered = false;
1048
+ this.closeWallet();
1049
+ this.events.removeAllListeners(); // @ts-ignore
1050
+
1051
+ this.port.handleMessage = undefined;
1052
+ };
1053
+
1054
+ this.openWallet = (path, intent, networkId) => {
1055
+ if (this.state === OpenState.CLOSED) {
1056
+ this.state = OpenState.OPENING;
1057
+ const sessionId = `${performance.now()}`;
1058
+ this._sessionId = sessionId;
1059
+ this.sendMessage({
1060
+ idx: -1,
1061
+ type: EventType.OPEN,
1062
+ data: {
1063
+ path,
1064
+ intent,
1065
+ networkId,
1066
+ sessionId
1067
+ }
1068
+ });
1069
+ }
1070
+ };
1071
+
1072
+ this.state = OpenState.CLOSED;
1073
+ this.port = port;
1074
+
1075
+ if (!port) {
1076
+ throw new Error('port argument cannot be empty');
1077
+ } // disable init handshake for proxy-transport, we set it to OK, to
1078
+ // consider it in completed state.
1079
+
1080
+
1081
+ this._init = InitState.OK;
1082
+ }
1083
+
1084
+ closeWallet() {
1085
+ this.sendMessage({
1086
+ idx: -1,
1087
+ type: EventType.CLOSE,
1088
+ data: null
1089
+ });
1090
+ this.close();
1091
+ }
1092
+
1093
+ sendMessage(message) {
1094
+ if (!message.idx) {
1095
+ throw new Error('message idx is empty');
1096
+ }
1097
+
1098
+ this.port.sendMessage(message);
1099
+ }
1100
+
1101
+ }
1102
+
1103
+ class ProxyMessageHandler extends BaseWalletTransport {
1104
+ constructor(walletRequestHandler, port) {
1105
+ super(walletRequestHandler);
1106
+ this.port = void 0;
1107
+ this.port = port;
1108
+ this._init = InitState.OK;
1109
+ }
1110
+
1111
+ register() {
1112
+ this.port.handleMessage = message => {
1113
+ this.handleMessage(message);
1114
+ };
1115
+
1116
+ this._registered = true;
1117
+ } // note: we can't decide whether to restore the session within register(), because session info is
1118
+ // received asyncronously via EventType.OPEN after register() is executed.
1119
+ // And in the case of a redirect/reload, EventType.OPEN is not sent at all,
1120
+ // because the wallet is already open.
1121
+ //
1122
+ // call this method from wallet redirect hander when a session restore is needed
1123
+
1124
+
1125
+ restoreSession() {
1126
+ const cachedSession = this.getCachedTransportSession();
1127
+
1128
+ if (cachedSession) {
1129
+ this.open(cachedSession);
1130
+ }
1131
+ }
1132
+
1133
+ unregister() {
1134
+ // @ts-ignore
1135
+ this.port.handleMessage = undefined;
1136
+ this._registered = false;
1137
+ }
1138
+
1139
+ sendMessage(message) {
1140
+ this.port.sendMessage(message);
1141
+ }
1142
+
1143
+ }
1144
+
1145
+ class MuxMessageProvider {
1146
+ constructor(...messageProviders) {
1147
+ var _this = this;
1148
+
1149
+ this.messageProviders = void 0;
1150
+ this.provider = void 0;
1151
+
1152
+ this.register = () => {
1153
+ if (this.messageProviders.length === 1) {
1154
+ this.provider = this.messageProviders[0];
1155
+ this.provider.register();
1156
+ return;
1157
+ }
1158
+
1159
+ this.messageProviders.forEach(m => {
1160
+ m.register();
1161
+ m.once('open', () => {
1162
+ // the first one to open is the winner, and others will be unregistered
1163
+ if (!this.provider) {
1164
+ this.provider = m; // unregister other providers
1165
+
1166
+ this.messageProviders.forEach(m => {
1167
+ if (this.provider !== m) {
1168
+ m.unregister();
1169
+ }
1170
+ });
1171
+ }
1172
+ });
1173
+ });
1174
+ };
1175
+
1176
+ this.unregister = () => {
1177
+ this.messageProviders.forEach(m => m.unregister());
1178
+ this.provider = undefined;
1179
+ };
1180
+
1181
+ this.openWallet = (path, intent, networkId) => {
1182
+ if (this.provider) {
1183
+ this.provider.openWallet(path, intent, networkId);
1184
+ return;
1185
+ }
1186
+
1187
+ this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
1188
+ };
1189
+
1190
+ this.sendAsync = async function (request, callback, chainId) {
1191
+ if (_this.provider) {
1192
+ _this.provider.sendAsync(request, callback, chainId);
1193
+
1194
+ return;
1195
+ }
1196
+
1197
+ throw new Error('impossible state, wallet must be opened first');
1198
+ };
1199
+
1200
+ this.sendMessageRequest = async function (message) {
1201
+ if (_this.provider) {
1202
+ return _this.provider.sendMessageRequest(message);
1203
+ }
1204
+
1205
+ throw new Error('impossible state, wallet must be opened first');
1206
+ };
1207
+
1208
+ this.waitUntilOpened = async function () {
1209
+ if (_this.provider) {
1210
+ return _this.provider.waitUntilOpened();
1211
+ }
1212
+
1213
+ return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
1214
+ };
1215
+
1216
+ this.waitUntilConnected = async function () {
1217
+ if (_this.provider) {
1218
+ return _this.provider.waitUntilConnected();
1219
+ }
1220
+
1221
+ throw new Error('impossible state, wallet must be opened first');
1222
+ };
1223
+
1224
+ this.messageProviders = messageProviders;
1225
+ this.provider = undefined;
1226
+ }
1227
+
1228
+ add(...messageProviders) {
1229
+ this.messageProviders.push(...messageProviders);
1230
+ }
1231
+
1232
+ closeWallet() {
1233
+ if (this.provider) {
1234
+ this.provider.closeWallet();
1235
+ }
1236
+ }
1237
+
1238
+ isOpened() {
1239
+ if (this.provider) {
1240
+ return this.provider.isOpened();
1241
+ }
1242
+
1243
+ return false;
1244
+ }
1245
+
1246
+ isConnected() {
1247
+ if (this.provider) {
1248
+ return this.provider.isConnected();
1249
+ }
1250
+
1251
+ return false;
1252
+ }
1253
+
1254
+ on(event, fn) {
1255
+ if (this.provider) {
1256
+ this.provider.on(event, fn);
1257
+ return;
1258
+ }
1259
+
1260
+ this.messageProviders.forEach(m => {
1261
+ m.on(event, fn);
1262
+ });
1263
+ }
1264
+
1265
+ once(event, fn) {
1266
+ if (this.provider) {
1267
+ this.provider.once(event, fn);
1268
+ return;
1269
+ }
1270
+
1271
+ this.messageProviders.forEach(m => {
1272
+ m.once(event, fn);
1273
+ });
1274
+ }
1275
+
1276
+ emit(event, ...args) {
1277
+ if (this.provider) {
1278
+ return this.provider.emit(event, ...args);
1279
+ }
1280
+
1281
+ for (let i = 0; i < this.messageProviders.length; i++) {
1282
+ this.messageProviders[i].emit(event, ...args);
1283
+ }
1284
+
1285
+ return true;
1286
+ }
1287
+
1288
+ sendMessage(message) {
1289
+ if (!message.idx || message.idx <= 0) {
1290
+ throw new Error('message idx is empty');
1291
+ }
1292
+
1293
+ if (this.provider) {
1294
+ this.provider.sendMessage(message);
1295
+ } else {
1296
+ throw new Error('impossible state, wallet must be opened first');
1297
+ }
1298
+ }
1299
+
1300
+ handleMessage(message) {
1301
+ if (this.provider) {
1302
+ this.provider.handleMessage(message);
1303
+ return;
1304
+ }
1305
+
1306
+ throw new Error('impossible state, wallet must be opened first');
1307
+ }
1308
+
1309
+ }
1310
+
1311
+ let registeredWindowMessageProvider;
1312
+ class WindowMessageProvider extends BaseProviderTransport {
1313
+ constructor(walletAppURL) {
1314
+ super();
1315
+ this.walletURL = void 0;
1316
+ this.walletWindow = void 0;
1317
+
1318
+ this.register = () => {
1319
+ if (registeredWindowMessageProvider) {
1320
+ throw new Error('A WindowMessageProvider is already registered. There can only be one.');
1321
+ } // listen for incoming messages from wallet
1322
+
1323
+
1324
+ window.addEventListener('message', this.onWindowEvent);
1325
+ registeredWindowMessageProvider = this; // open heartbeat
1326
+
1327
+ this.on('open', () => {
1328
+ // Heartbeat to track if window closed
1329
+ const popup = this.walletWindow;
1330
+ const interval = setInterval(() => {
1331
+ if (popup && popup.closed) {
1332
+ clearInterval(interval);
1333
+ this.close();
1334
+ }
1335
+ }, 500);
1336
+ }); // close clean up
1337
+
1338
+ this.on('close', () => {
1339
+ if (this.walletWindow) {
1340
+ this.walletWindow.close();
1341
+ this.walletWindow = null;
1342
+ }
1343
+ });
1344
+ this._registered = true;
1345
+ };
1346
+
1347
+ this.unregister = () => {
1348
+ this._registered = false;
1349
+ this.closeWallet(); // disable message listener
1350
+
1351
+ if (registeredWindowMessageProvider === this) {
1352
+ registeredWindowMessageProvider = undefined;
1353
+ }
1354
+
1355
+ window.removeEventListener('message', this.onWindowEvent); // clear event listeners
1356
+
1357
+ this.events.removeAllListeners();
1358
+ };
1359
+
1360
+ this.openWallet = (path, intent, networkId) => {
1361
+ if (this.walletWindow && this.isOpened()) {
1362
+ // TODO: update the location of window to path
1363
+ this.walletWindow.focus();
1364
+ return;
1365
+ } // Instantiate new walletURL for this call
1366
+
1367
+
1368
+ const walletURL = new URL(this.walletURL.href);
1369
+ const windowSessionParams = new WindowSessionParams();
1370
+
1371
+ if (path && path !== '') {
1372
+ walletURL.pathname = path.toLowerCase();
1373
+ } // Set session, intent and network id on walletURL
1374
+
1375
+
1376
+ this._init = InitState.NIL;
1377
+ this._sessionId = `${performance.now()}`;
1378
+ windowSessionParams.set('sid', this._sessionId);
1379
+
1380
+ if (intent) {
1381
+ // for the window-transport, we eagerly/optimistically set the origin host
1382
+ // when connecting to the wallet, however, this will be verified and enforced
1383
+ // on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
1384
+ if (intent.type === 'connect') {
1385
+ if (!intent.options) intent.options = {}; // skip setting origin host if we're in an browser extension execution context
1386
+ // allow origin that is passed in
1387
+
1388
+ if (!isBrowserExtension()) {
1389
+ intent.options.origin = window.location.origin;
1390
+ }
1391
+ } // encode intent as base6 url-encoded param
1392
+
1393
+
1394
+ windowSessionParams.set('intent', base64EncodeObject(intent));
1395
+ }
1396
+
1397
+ if (networkId) {
1398
+ windowSessionParams.set('net', `${networkId}`);
1399
+ } // Open popup window on center of the app window
1400
+
1401
+
1402
+ const windowSize = [450, 750];
1403
+ const windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1404
+ const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`; // serialize params
1405
+
1406
+ walletURL.search = windowSessionParams.toString();
1407
+ this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures); // TODO: move this somewhere else
1408
+ // TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
1409
+ // Popup blocking detection and notice
1410
+ // let warned = false
1411
+ // const warnPopupBlocked = () => {
1412
+ // if (warned) return
1413
+ // warned = true
1414
+ // // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
1415
+ // throw new Error('popup is blocked')
1416
+ // }
1417
+ // const popupCheck = setTimeout(() => {
1418
+ // if (!popup || popup.closed || typeof popup.closed === 'undefined') {
1419
+ // // popup is definitely blocked if we reach here.
1420
+ // warnPopupBlocked()
1421
+ // }
1422
+ // }, 1000)
1423
+ // const popupBlocked = popup === null || popup === undefined
1424
+ // if (popupBlocked) {
1425
+ // warnPopupBlocked()
1426
+ // return
1427
+ // }
1428
+ };
1429
+
1430
+ this.onWindowEvent = event => {
1431
+ // Security check, ensure message is coming from wallet origin url
1432
+ if (event.origin !== this.walletURL.origin) {
1433
+ // Safetly can skip events not from the wallet
1434
+ return;
1435
+ }
1436
+
1437
+ let message;
1438
+
1439
+ try {
1440
+ message = JSON.parse(event.data);
1441
+ } catch (err) {
1442
+ // event is not a ProviderMessage JSON object, skip
1443
+ return;
1444
+ }
1445
+
1446
+ if (!message) {
1447
+ throw new Error('ProviderMessage object is empty');
1448
+ } // handle message with base message provider
1449
+
1450
+
1451
+ this.handleMessage(message);
1452
+ };
1453
+
1454
+ this.walletURL = new URL(walletAppURL);
1455
+ }
1456
+
1457
+ closeWallet() {
1458
+ var _this$walletWindow;
1459
+
1460
+ this.close();
1461
+ (_this$walletWindow = this.walletWindow) == null ? void 0 : _this$walletWindow.close();
1462
+ } // onmessage, receives ProviderMessageResponse from the wallet post-message transport
1463
+
1464
+
1465
+ sendMessage(message) {
1466
+ if (!this.walletWindow) {
1467
+ logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
1468
+ return;
1469
+ }
1470
+
1471
+ const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message;
1472
+ this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
1473
+ }
1474
+
1475
+ }
1476
+
1477
+ class WindowMessageHandler extends BaseWalletTransport {
1478
+ constructor(walletRequestHandler) {
1479
+ var _this;
1480
+
1481
+ super(walletRequestHandler);
1482
+ _this = this;
1483
+ this.parentWindow = void 0;
1484
+ this._isPopup = false;
1485
+
1486
+ this.onWindowEvent = async function (event) {
1487
+ if (!event.origin || event.origin === '') {
1488
+ // skip same-origin or when event.origin is empty/undefined
1489
+ return;
1490
+ }
1491
+
1492
+ if (_this.appOrigin && event.origin !== _this.appOrigin) {
1493
+ // skip message as not from expected app origin
1494
+ return;
1495
+ } // Wallet always expects json-rpc request messages from a dapp
1496
+
1497
+
1498
+ let request;
1499
+
1500
+ try {
1501
+ request = JSON.parse(event.data);
1502
+ } catch (err) {
1503
+ // event is not a ProviderMessage JSON object, skip
1504
+ return;
1505
+ }
1506
+
1507
+ logger.debug('RECEIVED MESSAGE', request); // Record event origin for valid init ack
1508
+
1509
+ if (_this._init !== InitState.OK && _this.isValidInitAck(request)) {
1510
+ _this.appOrigin = event.origin;
1511
+ }
1512
+
1513
+ if (_this._init === InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
1514
+ // impossible state
1515
+ logger.error('impossible state, init.OK and appOrigin required');
1516
+ return;
1517
+ } // Handle message via the base transport
1518
+
1519
+
1520
+ _this.handleMessage(request);
1521
+ };
1522
+
1523
+ this.getWindowTransportSession = windowParams => {
1524
+ const params = new WindowSessionParams(windowParams);
1525
+ return {
1526
+ sessionId: params.get('sid'),
1527
+ networkId: params.get('net'),
1528
+ intent: base64DecodeObject(params.get('intent'))
1529
+ };
1530
+ };
1531
+
1532
+ this._init = InitState.NIL;
1533
+ }
1534
+
1535
+ register() {
1536
+ const isPopup = parent.window.opener !== null;
1537
+ this._isPopup = isPopup;
1538
+
1539
+ if (isPopup !== true) {
1540
+ return;
1541
+ } // record open details (sessionId + default network) from the window url
1542
+
1543
+
1544
+ const {
1545
+ pathname,
1546
+ search: rawParams
1547
+ } = new URL(window.location.href);
1548
+ let session = this.getWindowTransportSession(rawParams); // provider should always include sid when opening a new window
1549
+
1550
+ const isNewWindowSession = !!session.sessionId; // attempt to restore previous session in the case of a redirect or window reload
1551
+
1552
+ if (!isNewWindowSession) {
1553
+ session = this.getCachedTransportSession();
1554
+ }
1555
+
1556
+ if (!session) {
1557
+ logger.error('window session is undefined');
1558
+ return;
1559
+ }
1560
+
1561
+ if (session.intent && isNewWindowSession) {
1562
+ window.history.replaceState({
1563
+ openWalletIntent: true
1564
+ }, document.title, pathname);
1565
+ } // record parent window instance for communication
1566
+
1567
+
1568
+ this.parentWindow = parent.window.opener; // listen for window-transport requests
1569
+
1570
+ window.addEventListener('message', this.onWindowEvent, false);
1571
+ this._registered = true; // send open event to the app which opened us
1572
+
1573
+ this.open(session).then(opened => {
1574
+ if (!opened) {
1575
+ var _session;
1576
+
1577
+ const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
1578
+ logger.error(err);
1579
+ this.notifyClose({
1580
+ message: err
1581
+ });
1582
+ window.close();
1583
+ }
1584
+ }).catch(e => {
1585
+ var _session2;
1586
+
1587
+ const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
1588
+ logger.error(err);
1589
+ this.notifyClose({
1590
+ message: err
1591
+ });
1592
+ window.close();
1593
+ });
1594
+ }
1595
+
1596
+ unregister() {
1597
+ window.removeEventListener('message', this.onWindowEvent);
1598
+ this._registered = false;
1599
+ } // onmessage is called when (the wallet) receives request messages from the dapp
1600
+ // over the window post-messaging transport
1601
+
1602
+
1603
+ // postMessage sends message to the dapp window
1604
+ sendMessage(message) {
1605
+ // prepare payload
1606
+ const payload = JSON.stringify(message); // post-message to app.
1607
+ // only for init requests, we send to '*' origin
1608
+
1609
+ if (message.type === EventType.INIT) {
1610
+ this.postMessage(payload, true);
1611
+ } else {
1612
+ this.postMessage(payload);
1613
+ }
1614
+ }
1615
+
1616
+ get isPopup() {
1617
+ return this._isPopup;
1618
+ }
1619
+
1620
+ postMessage(message, init = false) {
1621
+ if (init !== true && this._init !== InitState.OK) {
1622
+ logger.error('impossible state, should not be calling postMessage until inited');
1623
+ return;
1624
+ }
1625
+
1626
+ if (init) {
1627
+ // init message transmission to global target -- for 'init' payloads only
1628
+ this.parentWindow.postMessage(message, '*');
1629
+ } else {
1630
+ // open message transmission
1631
+ if (this.appOrigin && this.appOrigin.length > 4) {
1632
+ // just above '.com'
1633
+ this.parentWindow.postMessage(message, this.appOrigin);
1634
+ } else {
1635
+ logger.error('unable to postMessage as parentOrigin is invalid');
1636
+ }
1637
+ }
1638
+ }
1639
+
1640
+ }
1641
+
1642
+ class WalletRequestHandler {
1643
+ constructor(_signer, prompter, mainnetNetworks, testnetNetworks = []) {
1644
+ var _this = this;
1645
+
1646
+ this.signer = void 0;
1647
+ this.prompter = void 0;
1648
+ this.mainnetNetworks = void 0;
1649
+ this.testnetNetworks = void 0;
1650
+ this._connectOptions = void 0;
1651
+ this._defaultNetworkId = void 0;
1652
+ this._chainId = void 0;
1653
+ this.events = new EventEmitter2();
1654
+
1655
+ this.promptConnect = async function (options) {
1656
+ if (!options && !_this._connectOptions) {
1657
+ // this is an unexpected state and should not happen
1658
+ throw new Error('prompter connect options are empty');
1659
+ }
1660
+
1661
+ if (!_this.prompter) {
1662
+ // if prompter is null, we'll auto connect
1663
+ return _this.connect(options);
1664
+ }
1665
+
1666
+ const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
1667
+ return {
1668
+ connected: false
1669
+ };
1670
+ });
1671
+ const connectDetails = promptConnectDetails;
1672
+
1673
+ if (connectDetails.connected && !connectDetails.session) {
1674
+ connectDetails.session = await _this.walletSession();
1675
+ }
1676
+
1677
+ return promptConnectDetails;
1678
+ };
1679
+
1680
+ this.sendAsync = async function (request, callback, chainId) {
1681
+ const response = {
1682
+ jsonrpc: '2.0',
1683
+ id: request.id,
1684
+ result: null
1685
+ };
1686
+
1687
+ try {
1688
+ // only allow public json rpc method to the provider when user is not logged in, aka signer is not set
1689
+ if ((!_this.signer || _this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) {
1690
+ // throw new Error(`not logged in. ${request.method} is unavailable`)
1691
+ throw ErrSignedInRequired;
1692
+ } // wallet signer
1693
+
1694
+
1695
+ const signer = _this.signer;
1696
+ if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured'); // fetch the provider for the specific chain, or undefined will select defaultChain
1697
+
1698
+ const provider = await signer.getProvider(chainId);
1699
+ if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`);
1700
+
1701
+ switch (request.method) {
1702
+ case 'net_version':
1703
+ {
1704
+ const result = await provider.send('net_version', []);
1705
+ response.result = result;
1706
+ break;
1707
+ }
1708
+
1709
+ case 'eth_chainId':
1710
+ {
1711
+ const result = await provider.send('eth_chainId', []);
1712
+ response.result = result;
1713
+ break;
1714
+ }
1715
+
1716
+ case 'eth_accounts':
1717
+ {
1718
+ const walletAddress = await signer.getAddress();
1719
+ response.result = [walletAddress];
1720
+ break;
1721
+ }
1722
+
1723
+ case 'eth_getBalance':
1724
+ {
1725
+ const [accountAddress, blockTag] = request.params;
1726
+ const walletBalance = await provider.getBalance(accountAddress, blockTag);
1727
+ response.result = walletBalance.toHexString();
1728
+ break;
1729
+ }
1730
+
1731
+ case 'eth_sign':
1732
+ {
1733
+ // note: message from json-rpc input is in hex format
1734
+ const [signingAddress, message] = request.params;
1735
+ let sig = ''; // TODO:
1736
+ // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
1737
+
1738
+ if (_this.prompter === null) {
1739
+ // prompter is null, so we'll sign from here
1740
+ sig = await signer.signMessage(ethers.utils.arrayify(message), chainId);
1741
+ } else {
1742
+ // prompt user to provide the response
1743
+ sig = await _this.prompter.promptSignMessage({
1744
+ chainId: chainId,
1745
+ message: message
1746
+ });
1747
+ }
1748
+
1749
+ if (sig && sig.length > 0) {
1750
+ response.result = sig;
1751
+ } else {
1752
+ // The user has declined the request when value is null
1753
+ throw new Error('declined by user');
1754
+ }
1755
+
1756
+ break;
1757
+ }
1758
+
1759
+ case 'eth_signTypedData':
1760
+ case 'eth_signTypedData_v4':
1761
+ {
1762
+ // note: signingAddress from json-rpc input is in hex format, and typedDataObject
1763
+ // should be an object, but in some instances may be double string encoded
1764
+ const [signingAddress, typedDataObject] = request.params;
1765
+ let typedData = undefined;
1766
+
1767
+ if (typeof typedDataObject === 'string') {
1768
+ try {
1769
+ typedData = JSON.parse(typedDataObject);
1770
+ } catch (e) {}
1771
+ } else {
1772
+ typedData = typedDataObject;
1773
+ }
1774
+
1775
+ if (!typedData || !typedData.domain || !typedData.types || !typedData.message) {
1776
+ throw new Error('invalid typedData object');
1777
+ }
1778
+
1779
+ let sig = '';
1780
+
1781
+ if (_this.prompter === null) {
1782
+ // prompter is null, so we'll sign from here
1783
+ sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId);
1784
+ } else {
1785
+ // prompt user to provide the response
1786
+ sig = await _this.prompter.promptSignMessage({
1787
+ chainId: chainId,
1788
+ typedData: typedData
1789
+ });
1790
+ }
1791
+
1792
+ if (sig && sig.length > 0) {
1793
+ response.result = sig;
1794
+ } else {
1795
+ // The user has declined the request when value is null
1796
+ throw new Error('declined by user');
1797
+ }
1798
+
1799
+ break;
1800
+ }
1801
+
1802
+ case 'eth_sendTransaction':
1803
+ {
1804
+ // https://eth.wiki/json-rpc/API#eth_sendtransaction
1805
+ const [transactionParams] = request.params;
1806
+ let txnHash = '';
1807
+
1808
+ if (_this.prompter === null) {
1809
+ // prompter is null, so we'll send from here
1810
+ const txnResponse = await signer.sendTransaction(transactionParams, chainId);
1811
+ txnHash = txnResponse.hash;
1812
+ } else {
1813
+ // prompt user to provide the response
1814
+ txnHash = await _this.prompter.promptSendTransaction(transactionParams, chainId);
1815
+ }
1816
+
1817
+ if (txnHash) {
1818
+ response.result = txnHash;
1819
+ } else {
1820
+ // The user has declined the request when value is null
1821
+ throw new Error('declined by user');
1822
+ }
1823
+
1824
+ break;
1825
+ }
1826
+
1827
+ case 'eth_signTransaction':
1828
+ {
1829
+ // https://eth.wiki/json-rpc/API#eth_signTransaction
1830
+ const [transaction] = request.params;
1831
+ const sender = ethers.utils.getAddress(transaction.from);
1832
+
1833
+ if (sender !== (await signer.getAddress())) {
1834
+ throw new Error('sender address does not match wallet');
1835
+ }
1836
+
1837
+ if (_this.prompter === null) {
1838
+ // The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
1839
+ // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
1840
+ //
1841
+ // TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
1842
+ // we will want to resolveProperties the bignumber values to hex strings
1843
+ response.result = await signer.signTransactions(transaction, chainId);
1844
+ } else {
1845
+ response.result = await _this.prompter.promptSignTransaction(transaction, chainId);
1846
+ }
1847
+
1848
+ break;
1849
+ }
1850
+
1851
+ case 'eth_sendRawTransaction':
1852
+ {
1853
+ // NOTE: we're not using a prompter here as the transaction is already signed
1854
+ // and would have prompted the user upon signing.
1855
+ // https://eth.wiki/json-rpc/API#eth_sendRawTransaction
1856
+ if (isSignedTransactions(request.params[0])) {
1857
+ const txChainId = BigNumber.from(request.params[0].chainId).toNumber();
1858
+ const tx = await (await signer.getRelayer(txChainId)).relay(request.params[0]);
1859
+ response.result = (await tx).hash;
1860
+ } else {
1861
+ const tx = await provider.sendTransaction(request.params[0]);
1862
+ response.result = tx.hash;
1863
+ }
1864
+
1865
+ break;
1866
+ }
1867
+
1868
+ case 'eth_getTransactionCount':
1869
+ {
1870
+ const address = ethers.utils.getAddress(request.params[0]);
1871
+ const tag = request.params[1];
1872
+ const walletAddress = ethers.utils.getAddress(await signer.getAddress());
1873
+
1874
+ if (address === walletAddress) {
1875
+ const count = await signer.getTransactionCount(tag);
1876
+ response.result = ethers.BigNumber.from(count).toHexString();
1877
+ } else {
1878
+ const count = await provider.getTransactionCount(address, tag);
1879
+ response.result = ethers.BigNumber.from(count).toHexString();
1880
+ }
1881
+
1882
+ break;
1883
+ }
1884
+
1885
+ case 'eth_blockNumber':
1886
+ {
1887
+ response.result = await provider.getBlockNumber();
1888
+ break;
1889
+ }
1890
+
1891
+ case 'eth_getBlockByNumber':
1892
+ {
1893
+ response.result = await provider.getBlock(request.params[0]
1894
+ /* , jsonRpcRequest.params[1] */
1895
+ );
1896
+ break;
1897
+ }
1898
+
1899
+ case 'eth_getBlockByHash':
1900
+ {
1901
+ response.result = await provider.getBlock(request.params[0]
1902
+ /* , jsonRpcRequest.params[1] */
1903
+ );
1904
+ break;
1905
+ }
1906
+
1907
+ case 'eth_getTransactionByHash':
1908
+ {
1909
+ response.result = await provider.getTransaction(request.params[0]);
1910
+ break;
1911
+ }
1912
+
1913
+ case 'eth_call':
1914
+ {
1915
+ const [transactionObject, blockTag] = request.params;
1916
+ response.result = await provider.call(transactionObject, blockTag);
1917
+ break;
1918
+ }
1919
+
1920
+ case 'eth_getCode':
1921
+ {
1922
+ const [contractAddress, blockTag] = request.params;
1923
+ response.result = await provider.getCode(contractAddress, blockTag);
1924
+ break;
1925
+ }
1926
+
1927
+ case 'eth_estimateGas':
1928
+ {
1929
+ const [transactionObject] = request.params;
1930
+ response.result = await provider.estimateGas(transactionObject);
1931
+ break;
1932
+ }
1933
+
1934
+ case 'eth_gasPrice':
1935
+ {
1936
+ const gasPrice = await provider.getGasPrice();
1937
+ response.result = gasPrice.toHexString();
1938
+ break;
1939
+ }
1940
+ // smart wallet method
1941
+
1942
+ case 'sequence_getWalletContext':
1943
+ {
1944
+ response.result = await signer.getWalletContext();
1945
+ break;
1946
+ }
1947
+ // smart wallet method
1948
+
1949
+ case 'sequence_getWalletConfig':
1950
+ {
1951
+ const [_chainId] = request.params;
1952
+ response.result = await signer.getWalletConfig(_chainId);
1953
+ break;
1954
+ }
1955
+ // smart wallet method
1956
+
1957
+ case 'sequence_getWalletState':
1958
+ {
1959
+ const [_chainId2] = request.params;
1960
+ response.result = await signer.getWalletState(_chainId2);
1961
+ break;
1962
+ }
1963
+ // smart wallet method
1964
+
1965
+ case 'sequence_getNetworks':
1966
+ {
1967
+ // NOTE: must ensure that the response result below returns clean serialized data, which is to omit
1968
+ // the provider and relayer objects and only return the urls so can be reinstantiated on dapp side.
1969
+ // This is handled by this.getNetworks() but noted here for future readers.
1970
+ response.result = await _this.getNetworks(true);
1971
+ break;
1972
+ }
1973
+ // smart wallet method
1974
+
1975
+ case 'sequence_updateConfig':
1976
+ {
1977
+ throw new Error('sequence_updateConfig method is not allowed from a dapp'); // NOTE: method is disabled as we don't need a dapp to request to update a config.
1978
+ // However, if we ever want this, we can enable it but must also use the prompter
1979
+ // for confirmation.
1980
+ //
1981
+ // const [newConfig] = request.params
1982
+ // response.result = await signer.updateConfig(newConfig)
1983
+
1984
+ break;
1985
+ }
1986
+ // smart wallet method
1987
+
1988
+ case 'sequence_publishConfig':
1989
+ {
1990
+ throw new Error('sequence_publishConfig method is not allowed from a dapp');
1991
+ break;
1992
+ }
1993
+ // relayer method
1994
+
1995
+ case 'sequence_estimateGasLimits':
1996
+ {
1997
+ // TODO
1998
+ break;
1999
+ }
2000
+ // relayer method
2001
+
2002
+ case 'sequence_gasRefundOptions':
2003
+ {
2004
+ // TODO
2005
+ break;
2006
+ }
2007
+ // relayer method
2008
+
2009
+ case 'sequence_getNonce':
2010
+ {
2011
+ // TODO
2012
+ break;
2013
+ }
2014
+ // relayer method
2015
+
2016
+ case 'sequence_relay':
2017
+ {
2018
+ // TODO
2019
+ break;
2020
+ }
2021
+ // set default network of wallet
2022
+
2023
+ case 'sequence_setDefaultNetwork':
2024
+ {
2025
+ const [defaultNetworkId] = request.params;
2026
+
2027
+ if (!defaultNetworkId) {
2028
+ throw new Error('invalid request, method argument defaultNetworkId cannot be empty');
2029
+ }
2030
+
2031
+ const ok = await _this.setDefaultNetwork(defaultNetworkId);
2032
+
2033
+ if (!ok) {
2034
+ throw new Error(`unable to set default network ${defaultNetworkId}`);
2035
+ }
2036
+
2037
+ response.result = await _this.getNetworks(true);
2038
+ break;
2039
+ }
2040
+
2041
+ default:
2042
+ {
2043
+ // NOTE: provider here will be chain-bound if chainId is provided
2044
+ const providerResponse = await provider.send(request.method, request.params);
2045
+ response.result = providerResponse;
2046
+ }
2047
+ }
2048
+ } catch (err) {
2049
+ logger.error(err); // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#rpc-errors
2050
+
2051
+ response.result = null;
2052
+ response.error = _extends({}, new Error(err), {
2053
+ code: 4001
2054
+ });
2055
+ }
2056
+
2057
+ callback(undefined, response);
2058
+ };
2059
+
2060
+ this.signer = _signer;
2061
+ this.prompter = prompter;
2062
+ this.mainnetNetworks = mainnetNetworks;
2063
+ this.testnetNetworks = testnetNetworks;
2064
+ }
2065
+
2066
+ async signIn(signer, options = {}) {
2067
+ this.signer = signer;
2068
+ const {
2069
+ connect,
2070
+ mainnetNetworks,
2071
+ testnetNetworks,
2072
+ defaultNetworkId
2073
+ } = options;
2074
+
2075
+ if (mainnetNetworks && mainnetNetworks.length > 0) {
2076
+ this.mainnetNetworks = mainnetNetworks;
2077
+ }
2078
+
2079
+ if (testnetNetworks && testnetNetworks.length > 0) {
2080
+ this.testnetNetworks = testnetNetworks;
2081
+ }
2082
+
2083
+ if ((!this.mainnetNetworks || this.mainnetNetworks.length === 0) && (!this.testnetNetworks || this.testnetNetworks.length === 0)) {
2084
+ throw new Error('signIn failed as network configuration is empty');
2085
+ }
2086
+
2087
+ const networkId = defaultNetworkId || this._defaultNetworkId;
2088
+
2089
+ if (networkId) {
2090
+ if (!(await this.setDefaultNetwork(networkId, false))) {
2091
+ throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`);
2092
+ }
2093
+ } // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
2094
+ // necessary auth request, and then notify the dapp of the 'connect' details.
2095
+ //
2096
+ // NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
2097
+ // we don't trigger the promptConnect flow, as we consider the user just authenticated
2098
+ // for this dapp, so its safe to authorize in the connect() method without the prompt.
2099
+ //
2100
+ // NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
2101
+
2102
+
2103
+ if (connect) {
2104
+ const connectOptions = this._connectOptions;
2105
+ const connectDetails = await this.connect(connectOptions);
2106
+ this.notifyConnect(connectDetails);
2107
+
2108
+ if (!connectOptions || connectOptions.keepWalletOpened !== true) {
2109
+ this.notifyClose();
2110
+ }
2111
+ }
2112
+ }
2113
+
2114
+ async connect(options) {
2115
+ if (!this.signer) {
2116
+ return {
2117
+ connected: false,
2118
+ chainId: '0x0',
2119
+ error: 'unable to connect without signed in account'
2120
+ };
2121
+ }
2122
+
2123
+ const connectDetails = {
2124
+ connected: true,
2125
+ chainId: ethers.utils.hexlify(await this.getChainId())
2126
+ };
2127
+
2128
+ if (options && options.authorize) {
2129
+ // Perform ethauth eip712 request and construct the ConnectDetails response
2130
+ // including the auth proof
2131
+ const authOptions = {
2132
+ app: options.app,
2133
+ origin: options.origin,
2134
+ expiry: options.expiry
2135
+ }; // if (typeof(options.authorize) === 'object') {
2136
+ // authOptions = { ...authOptions, ...options.authorize }
2137
+ // }
2138
+
2139
+ try {
2140
+ connectDetails.proof = await signAuthorization(this.signer, authOptions);
2141
+ } catch (err) {
2142
+ logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
2143
+ return {
2144
+ connected: false,
2145
+ chainId: '0x0',
2146
+ error: `signAuthorization failed: ${err.message}`
2147
+ };
2148
+ }
2149
+ } // Build session response for connect details
2150
+
2151
+
2152
+ connectDetails.session = await this.walletSession();
2153
+ return connectDetails;
2154
+ }
2155
+
2156
+ // sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
2157
+ // (aka, the signer in this instance) and then responds with a wrapped response of
2158
+ // ProviderMessageResponse to be sent over the transport
2159
+ sendMessageRequest(message) {
2160
+ return new Promise(resolve => {
2161
+ this.sendAsync(message.data, (error, response) => {
2162
+ // TODO: if response includes data.error, why do we need a separate error argument here?
2163
+ const responseMessage = _extends({}, message, {
2164
+ data: response
2165
+ }); // NOTE: we always resolve here, are the sendAsync call will wrap any exceptions
2166
+ // in the error field of the response to ensure we send back to the user
2167
+
2168
+
2169
+ resolve(responseMessage);
2170
+ }, message.chainId);
2171
+ });
2172
+ } // sendAsync implements the JsonRpcHandler interface for sending JsonRpcRequests to the wallet
2173
+
2174
+
2175
+ on(event, fn) {
2176
+ this.events.on(event, fn);
2177
+ }
2178
+
2179
+ once(event, fn) {
2180
+ this.events.once(event, fn);
2181
+ }
2182
+
2183
+ async getAddress() {
2184
+ if (!this.signer) {
2185
+ return '';
2186
+ } else {
2187
+ return this.signer.getAddress();
2188
+ }
2189
+ }
2190
+
2191
+ async getChainId() {
2192
+ if (!this.signer) {
2193
+ return 0;
2194
+ } else {
2195
+ if (this._chainId) return this._chainId; // memoized
2196
+
2197
+ this._chainId = await this.signer.getChainId();
2198
+ return this._chainId;
2199
+ }
2200
+ }
2201
+
2202
+ get connectOptions() {
2203
+ return this._connectOptions;
2204
+ }
2205
+
2206
+ setConnectOptions(options) {
2207
+ this._connectOptions = options;
2208
+ }
2209
+
2210
+ get defaultNetworkId() {
2211
+ return this._defaultNetworkId;
2212
+ }
2213
+
2214
+ async setDefaultNetwork(chainId, notifyNetworks = true) {
2215
+ if (!chainId) return undefined;
2216
+ this._defaultNetworkId = chainId;
2217
+ this._chainId = undefined;
2218
+
2219
+ if (this.signer && this.signer.setNetworks) {
2220
+ const defaultChainId = this.signer.setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId);
2221
+
2222
+ if (defaultChainId && notifyNetworks) {
2223
+ await this.notifyNetworks();
2224
+ }
2225
+
2226
+ return defaultChainId;
2227
+ } else {
2228
+ return undefined;
2229
+ }
2230
+ }
2231
+
2232
+ async getNetworks(jsonRpcResponse) {
2233
+ if (!this.signer) {
2234
+ logger.warn('signer not set: getNetworks is returning an empty list');
2235
+ return [];
2236
+ }
2237
+
2238
+ const networks = await this.signer.getNetworks();
2239
+
2240
+ if (jsonRpcResponse) {
2241
+ // omit provider and relayer objects as they are not serializable
2242
+ return networks.map(n => {
2243
+ const network = _extends({}, n);
2244
+
2245
+ network.provider = undefined;
2246
+ network.relayer = undefined;
2247
+ return network;
2248
+ });
2249
+ } else {
2250
+ return networks;
2251
+ }
2252
+ }
2253
+
2254
+ async walletSession() {
2255
+ return !this.signer ? undefined : {
2256
+ walletContext: await this.signer.getWalletContext(),
2257
+ accountAddress: await this.signer.getAddress(),
2258
+ networks: await this.getNetworks(true)
2259
+ };
2260
+ }
2261
+
2262
+ notifyConnect(connectDetails) {
2263
+ var _connectDetails$sessi;
2264
+
2265
+ this.events.emit('connect', connectDetails);
2266
+
2267
+ if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
2268
+ var _connectDetails$sessi2;
2269
+
2270
+ this.events.emit('accountsChanged', [(_connectDetails$sessi2 = connectDetails.session) == null ? void 0 : _connectDetails$sessi2.accountAddress]);
2271
+ }
2272
+ }
2273
+
2274
+ notifyDisconnect() {
2275
+ this.events.emit('accountsChanged', []);
2276
+ this.events.emit('networks', []);
2277
+ this.events.emit('disconnect');
2278
+ }
2279
+
2280
+ async notifyNetworks(networks) {
2281
+ const n = networks || (await this.getNetworks(true));
2282
+ this.events.emit('networks', n);
2283
+
2284
+ if (n && n.length > 0) {
2285
+ this.events.emit('chainChanged', ethers.utils.hexlify(n[0].chainId));
2286
+ } else {
2287
+ this.events.emit('chainChanged', '0x0');
2288
+ }
2289
+ }
2290
+
2291
+ async notifyWalletContext() {
2292
+ if (!this.signer) {
2293
+ logger.warn('signer not set: skipping to notify wallet context');
2294
+ return;
2295
+ }
2296
+
2297
+ const walletContext = await this.signer.getWalletContext();
2298
+ this.events.emit('walletContext', walletContext);
2299
+ }
2300
+
2301
+ notifyClose(error) {
2302
+ this.events.emit('close', error);
2303
+ }
2304
+
2305
+ isSignedIn() {
2306
+ return !!this.signer;
2307
+ }
2308
+
2309
+ getSigner() {
2310
+ return this.signer;
2311
+ }
2312
+
2313
+ setSigner(signer) {
2314
+ this.signer = signer;
2315
+ }
2316
+
2317
+ }
2318
+ const permittedJsonRpcMethods = ['net_version', 'eth_chainId', 'eth_getBalance', 'eth_getTransactionCount', 'eth_blockNumber', 'eth_getBlockByNumber', 'eth_getBlockByHash', 'eth_getTransactionByHash', 'eth_getCode', 'eth_estimateGas', 'eth_gasPrice', 'sequence_getWalletContext', 'sequence_getNetworks', 'sequence_setDefaultNetwork'];
2319
+
2320
+ const CHANNEL_ID = 'sequence-extension-message-handler';
2321
+ class ExtensionMessageHandler extends BaseWalletTransport {
2322
+ constructor(walletRequestHandler, runtime) {
2323
+ super(walletRequestHandler);
2324
+ this.runtime = runtime;
2325
+ this.port = void 0;
2326
+ this._init = InitState.OK;
2327
+ }
2328
+
2329
+ register() {
2330
+ this._registered = true;
2331
+ this.port = this.runtime.connect({
2332
+ name: CHANNEL_ID
2333
+ });
2334
+ }
2335
+
2336
+ sendMessage(message) {
2337
+ console.log('[ExtensionMessageHandler send]', message);
2338
+ this.port.postMessage(message);
2339
+ }
2340
+
2341
+ }
2342
+
2343
+ class ExtensionMessageProvider extends BaseProviderTransport {
2344
+ constructor(runtime) {
2345
+ super();
2346
+
2347
+ this.register = () => {
2348
+ this._registered = true;
2349
+ };
2350
+
2351
+ runtime.onConnect.addListener(port => {
2352
+ if (port.name === CHANNEL_ID) {
2353
+ this._init = InitState.OK;
2354
+ port.onMessage.addListener(message => {
2355
+ this.handleMessage(message);
2356
+ });
2357
+ }
2358
+ });
2359
+ }
2360
+
2361
+ sendMessage(message) {//noop
2362
+ }
2363
+
2364
+ unregister() {//noop
2365
+ }
2366
+
2367
+ openWallet(path, intent, networkId) {//noop
2368
+ }
2369
+
2370
+ closeWallet() {//noop
2371
+ }
2372
+
2373
+ }
2374
+
2375
+ // to be used on injected window.ethereum EIP1193 proxy
2376
+ class BaseInjectedTransport extends EventEmitter2 {
2377
+ constructor(stream) {
2378
+ var _this;
2379
+
2380
+ super();
2381
+ _this = this;
2382
+ this.stream = stream;
2383
+ this.responseCallbacks = new Map();
2384
+ this._messageIdx = 0;
2385
+
2386
+ this.nextMessageIdx = () => ++this._messageIdx;
2387
+
2388
+ this.handleMessage = message => {
2389
+ if (!message.type || !message.data) {
2390
+ return;
2391
+ }
2392
+
2393
+ console.log('[received message]', message);
2394
+ const requestIdx = message.idx;
2395
+ const responseCallback = this.responseCallbacks.get(requestIdx);
2396
+
2397
+ if (requestIdx) {
2398
+ this.responseCallbacks.delete(requestIdx);
2399
+ }
2400
+
2401
+ switch (message.type) {
2402
+ case EventType.MESSAGE:
2403
+ if (responseCallback) {
2404
+ this.emit(EventType.MESSAGE, message);
2405
+ responseCallback(message.data.error, message);
2406
+ } else {
2407
+ // NOTE: this would occur if 'idx' isn't set, which should never happen
2408
+ // or when we register two handler, or duplicate messages with the same idx are sent,
2409
+ // all of which should be prevented prior to getting to this point
2410
+ throw new Error('impossible state');
2411
+ }
2412
+
2413
+ break;
2414
+
2415
+ case EventType.DISCONNECT:
2416
+ case EventType.ACCOUNTS_CHANGED:
2417
+ case EventType.CHAIN_CHANGED:
2418
+ this.emit(message.type, message.data);
2419
+ break;
2420
+
2421
+ default:
2422
+ console.error('unknown message type', message);
2423
+ break;
2424
+ }
2425
+ };
2426
+
2427
+ this.sendMessageRequest = async function (message) {
2428
+ return new Promise((resolve, reject) => {
2429
+ if (!message.idx || message.idx <= 0) {
2430
+ reject(new Error('message idx not set'));
2431
+ }
2432
+
2433
+ const responseCallback = (error, response) => {
2434
+ if (error) {
2435
+ reject(error);
2436
+ } else if (response) {
2437
+ resolve(response);
2438
+ } else {
2439
+ throw new Error('no valid response to return');
2440
+ }
2441
+ };
2442
+
2443
+ const {
2444
+ idx
2445
+ } = message;
2446
+
2447
+ if (!_this.responseCallbacks.get(idx)) {
2448
+ _this.responseCallbacks.set(idx, responseCallback);
2449
+ } else {
2450
+ reject(new Error('duplicate message idx, should never happen'));
2451
+ }
2452
+
2453
+ _this.sendMessage(message);
2454
+ });
2455
+ };
2456
+
2457
+ this.stream.on('data', this.handleMessage);
2458
+ }
2459
+
2460
+ sendMessage(message) {
2461
+ if (!this.stream.writable) {
2462
+ console.error('window post message stream is not writable');
2463
+ }
2464
+
2465
+ this.stream.write(message);
2466
+ }
2467
+
2468
+ }
2469
+
2470
+ class Web3Provider extends Web3Provider$1 {
2471
+ static isSequenceProvider(cand) {
2472
+ return isSequenceProvider(cand);
2473
+ }
2474
+
2475
+ constructor(provider, defaultChainId) {
2476
+ const sender = new JsonRpcSender(provider, maybeChainId(defaultChainId));
2477
+ provider = sender;
2478
+ super(provider, 'any');
2479
+ this._sender = void 0;
2480
+ this._isSequenceProvider = void 0;
2481
+ this._defaultChainId = void 0;
2482
+ this._sender = sender;
2483
+ this._isSequenceProvider = true;
2484
+ this._defaultChainId = maybeChainId(defaultChainId);
2485
+ }
2486
+
2487
+ sendAsync(request, callback, chainId) {
2488
+ this._sender.sendAsync(request, callback, chainId);
2489
+ }
2490
+
2491
+ send(method, params, chainId) {
2492
+ return this._sender.send(method, params, chainId);
2493
+ }
2494
+
2495
+ getSigner() {
2496
+ return new Web3Signer(this, this._defaultChainId);
2497
+ }
2498
+
2499
+ async getChainId() {
2500
+ const result = await this.send('eth_chainId', []);
2501
+ const chainId = ethers.BigNumber.from(result).toNumber();
2502
+
2503
+ if (this._defaultChainId && this._defaultChainId !== chainId) {
2504
+ throw new Error(`provider chainId (${chainId}) does not match provider-bound chainId ${this._defaultChainId}`);
2505
+ }
2506
+
2507
+ return chainId;
2508
+ }
2509
+
2510
+ }
2511
+ function isSequenceProvider(provider) {
2512
+ const cand = provider;
2513
+ return cand && cand.send !== undefined && cand._isSequenceProvider === true;
2514
+ }
2515
+ class LocalWeb3Provider extends Web3Provider {
2516
+ constructor(signer, networks) {
2517
+ const walletRequestHandler = new WalletRequestHandler(signer, null, networks || []);
2518
+ super(walletRequestHandler);
2519
+ }
2520
+
2521
+ }
2522
+ class Web3Signer extends Signer {
2523
+ constructor(provider, defaultChainId) {
2524
+ super();
2525
+ this.provider = void 0;
2526
+ this.defaultChainId = void 0;
2527
+ this._address = void 0;
2528
+ this._index = void 0;
2529
+ this._context = void 0;
2530
+ this._networks = void 0;
2531
+ this._providers = {};
2532
+ this.provider = provider;
2533
+ this.defaultChainId = defaultChainId;
2534
+ } // memoized
2535
+
2536
+
2537
+ //
2538
+ // ethers AbstractSigner methods
2539
+ //
2540
+ async getAddress() {
2541
+ if (this._address) return this._address;
2542
+ const accounts = await this.provider.send('eth_accounts', []);
2543
+ this._address = accounts[0];
2544
+ this._index = 0;
2545
+ return ethers.utils.getAddress(this._address);
2546
+ }
2547
+
2548
+ signTransaction(transaction) {
2549
+ // TODO .. since ethers isn't using this method, perhaps we will?
2550
+ throw new Error('signTransaction is unsupported, use signTransactions instead');
2551
+ }
2552
+
2553
+ connect(provider) {
2554
+ throw new Error('unsupported: cannot alter JSON-RPC Signer connection');
2555
+ } //
2556
+ // Sequence Signer methods
2557
+ //
2558
+ // getProvider returns a Web3Provider instance for the current chain. Note that this method
2559
+ // and signer is bound to a particular chain to prevent misuse. If you'd like a provider
2560
+ // for a specific chain, try getSender(chainId), or wallet.getProvider(chainId).
2561
+
2562
+
2563
+ async getProvider(chainId) {
2564
+ if (chainId) {
2565
+ const currentChainId = await this.getChainId();
2566
+
2567
+ if (currentChainId !== chainId) {
2568
+ throw new Error(`signer is attempting to access chain ${chainId}, but is already bound to chain ${currentChainId}`);
2569
+ }
2570
+ }
2571
+
2572
+ return this.provider;
2573
+ } // getSender returns a Web3Provider instance via the signer transport. Note: for our case
2574
+ // the of sequence wallet, this will bring up the wallet window whenever using it, as the json-rpc
2575
+ // requests are sent to the window transport. Therefore, for anything non-signing related
2576
+ // you can write a higher-order JsonRpcRouter sender to route to the public provider endpoints
2577
+ // as we do in the WalletProvider.
2578
+ //
2579
+ // This method is primarily utilized internally when routing requests to a particular chainId.
2580
+
2581
+
2582
+ async getSender(chainId) {
2583
+ if (!chainId || chainId && chainId === this.defaultChainId) {
2584
+ return this.provider;
2585
+ }
2586
+
2587
+ if (!this._providers[chainId]) {
2588
+ this._providers[chainId] = new Web3Provider(new JsonRpcSender(this.provider, chainId), chainId);
2589
+ }
2590
+
2591
+ return this._providers[chainId];
2592
+ }
2593
+
2594
+ getRelayer(chainId) {
2595
+ // TODO: JsonRpcRelayer ......? or, Web3Relayer.. or SequenceRelayer?
2596
+ // sequence_estimateGasLimits
2597
+ // sequence_gasRefundOptions
2598
+ // sequence_getNonce
2599
+ // sequence_relay
2600
+ throw new Error('TODO');
2601
+ }
2602
+
2603
+ async getWalletContext() {
2604
+ if (!this._context) {
2605
+ this._context = await this.provider.send('sequence_getWalletContext', []);
2606
+ }
2607
+
2608
+ return this._context;
2609
+ }
2610
+
2611
+ async getWalletConfig(chainId) {
2612
+ return await this.provider.send('sequence_getWalletConfig', [maybeChainId(chainId)], maybeChainId(chainId) || this.defaultChainId);
2613
+ }
2614
+
2615
+ async getWalletState(chainId) {
2616
+ return await this.provider.send('sequence_getWalletState', [maybeChainId(chainId)], maybeChainId(chainId) || this.defaultChainId);
2617
+ }
2618
+
2619
+ async getNetworks() {
2620
+ if (!this._networks) {
2621
+ this._networks = await this.provider.send('sequence_getNetworks', []);
2622
+ }
2623
+
2624
+ return this._networks;
2625
+ }
2626
+
2627
+ async getSigners() {
2628
+ const networks = await this.getNetworks();
2629
+ const authChainId = networks.find(n => n.isAuthChain);
2630
+
2631
+ if (!authChainId) {
2632
+ throw new Error('authChainId could not be determined from network list');
2633
+ }
2634
+
2635
+ const walletConfig = await this.getWalletConfig(authChainId);
2636
+
2637
+ if (!walletConfig || walletConfig.length === 0) {
2638
+ throw new Error(`walletConfig returned zero results for authChainId {authChainId}`);
2639
+ }
2640
+
2641
+ return walletConfig[0].signers.map(s => s.address);
2642
+ } // signMessage matches implementation from ethers JsonRpcSigner for compatibility, but with
2643
+ // multi-chain support.
2644
+
2645
+
2646
+ async signMessage(message, chainId, allSigners) {
2647
+ const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
2648
+ const data = typeof message === 'string' ? ethers.utils.toUtf8Bytes(message) : message;
2649
+ const address = await this.getAddress(); // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
2650
+
2651
+ return await provider.send('eth_sign', [address, ethers.utils.hexlify(data)]);
2652
+ } // signTypedData matches implementation from ethers JsonRpcSigner for compatibility, but with
2653
+ // multi-chain support.
2654
+
2655
+
2656
+ async signTypedData(domain, types, message, chainId, allSigners) {
2657
+ // Populate any ENS names (in-place)
2658
+ // const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => {
2659
+ // return this.provider.resolveName(name)
2660
+ // })
2661
+ return await this.provider.send('eth_signTypedData_v4', [await this.getAddress(), ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], maybeChainId(chainId) || this.defaultChainId);
2662
+ } // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with
2663
+ // multi-chain support.
2664
+
2665
+
2666
+ async sendTransaction(transaction, chainId, allSigners) {
2667
+ const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
2668
+ const tx = this.sendUncheckedTransaction(transaction, chainId).then(hash => {
2669
+ return ethers.utils.poll(() => {
2670
+ return provider.getTransaction(hash).then(tx => {
2671
+ if (tx === null) {
2672
+ return undefined;
2673
+ }
2674
+
2675
+ return provider._wrapTransaction(tx, hash);
2676
+ });
2677
+ }, {
2678
+ onceBlock: this.provider
2679
+ }).catch(error => {
2680
+ error.transactionHash = hash;
2681
+ throw error;
2682
+ });
2683
+ }); // @ts-ignore
2684
+
2685
+ return tx;
2686
+ } // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to
2687
+ // send multiple transaction as a single payload and just one on-chain transaction.
2688
+
2689
+
2690
+ async sendTransactionBatch(transactions, chainId, allSigners) {
2691
+ const batch = await resolveArrayProperties(transactions);
2692
+
2693
+ if (!batch || batch.length === 0) {
2694
+ throw new Error('cannot send empty batch');
2695
+ }
2696
+
2697
+ const tx = _extends({}, batch[0]);
2698
+
2699
+ if (batch.length > 1) {
2700
+ tx.auxiliary = batch.splice(1);
2701
+ }
2702
+
2703
+ return this.sendTransaction(tx, chainId, allSigners);
2704
+ }
2705
+
2706
+ signTransactions(transaction, chainId, allSigners) {
2707
+ transaction = shallowCopy(transaction); // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over
2708
+ // the wire.. see sendUncheckedTransaction and resolveProperties
2709
+
2710
+ return this.provider.send('eth_signTransaction', [transaction], maybeChainId(chainId) || this.defaultChainId);
2711
+ }
2712
+
2713
+ sendSignedTransactions(signedTxs, chainId) {
2714
+ // sequence_relay
2715
+ throw new Error('TODO');
2716
+ } // updateConfig..
2717
+ // NOTE: this is not supported by the remote wallet by default.
2718
+
2719
+
2720
+ async updateConfig(newConfig) {
2721
+ // sequence_updateConfig
2722
+ const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
2723
+
2724
+ if (tx === null) {
2725
+ return [config, undefined];
2726
+ }
2727
+
2728
+ const provider = await this.getSender(this.defaultChainId);
2729
+ return [config, provider._wrapTransaction(tx, tx.hash)];
2730
+ } // publishConfig..
2731
+ // NOTE: this is not supported by the remote wallet by default.
2732
+
2733
+
2734
+ async publishConfig() {
2735
+ const provider = await this.getSender(this.defaultChainId);
2736
+ const tx = await provider.send('sequence_publishConfig', []);
2737
+
2738
+ if (tx === null) {
2739
+ return undefined;
2740
+ }
2741
+
2742
+ return provider._wrapTransaction(tx, tx.hash);
2743
+ }
2744
+
2745
+ async isDeployed(chainId) {
2746
+ const provider = await this.getSender(maybeChainId(chainId));
2747
+ const walletCode = await provider.getCode(await this.getAddress());
2748
+ return !!walletCode && walletCode !== '0x';
2749
+ } //
2750
+ // ethers JsonRpcSigner methods
2751
+ //
2752
+
2753
+
2754
+ async _signTypedData(domain, types, message, chainId, allSigners) {
2755
+ return this.signTypedData(domain, types, message, chainId, allSigners);
2756
+ }
2757
+
2758
+ async sendUncheckedTransaction(transaction, chainId) {
2759
+ transaction = shallowCopy(transaction);
2760
+ const fromAddress = this.getAddress(); // NOTE: we do not use provider estimation, and instead rely on our relayer to determine the gasLimit and gasPrice
2761
+ //
2762
+ // TODO: alternatively/one day, we could write a provider middleware to eth_estimateGas
2763
+ // and send it to our relayer url instead for estimation..
2764
+ //
2765
+ // if (!transaction.gasLimit) {
2766
+ // const estimate = shallowCopy(transaction)
2767
+ // estimate.from = fromAddress
2768
+ // transaction.gasLimit = this.provider.estimateGas(estimate)
2769
+ // }
2770
+
2771
+ const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId);
2772
+ return resolveProperties({
2773
+ tx: resolveProperties(transaction),
2774
+ sender: await fromAddress
2775
+ }).then(({
2776
+ tx,
2777
+ sender
2778
+ }) => {
2779
+ if (tx.from != null) {
2780
+ if (ethers.utils.getAddress(tx.from) !== sender) {
2781
+ // logger.throwArgumentError("from address mismatch", "transaction", transaction)
2782
+ throw new Error(`from address mismatch for transaction ${transaction}`);
2783
+ }
2784
+ } else {
2785
+ tx.from = sender;
2786
+ }
2787
+
2788
+ const hexTx = hexlifyTransaction(tx);
2789
+ return provider.send('eth_sendTransaction', [hexTx]).then(hash => {
2790
+ return hash;
2791
+ }, error => {
2792
+ // return checkError("sendTransaction", error, hexTx)
2793
+ throw new Error(`sendTransaction ${error}`);
2794
+ });
2795
+ });
2796
+ }
2797
+
2798
+ connectUnchecked() {
2799
+ throw new Error('connectUnchecked is unsupported');
2800
+ }
2801
+
2802
+ async unlock(password) {
2803
+ const address = await this.getAddress();
2804
+ return this.provider.send('personal_unlockAccount', [address, password, null]);
2805
+ }
2806
+
2807
+ } // NOTE: method has been copied + modified from ethers.js JsonRpcProvider
2808
+ // Convert an ethers.js transaction into a JSON-RPC transaction
2809
+
2810
+ const allowedTransactionKeys = {
2811
+ chainId: true,
2812
+ data: true,
2813
+ gasLimit: true,
2814
+ gasPrice: true,
2815
+ nonce: true,
2816
+ to: true,
2817
+ value: true,
2818
+ from: true,
2819
+ auxiliary: true,
2820
+ expiration: true,
2821
+ afterNonce: true,
2822
+ delegateCall: true,
2823
+ revertOnError: true
2824
+ };
2825
+
2826
+ const hexlifyTransaction = (transaction, allowExtra) => {
2827
+ // Check only allowed properties are given
2828
+ const allowed = shallowCopy(allowedTransactionKeys);
2829
+
2830
+ if (allowExtra) {
2831
+ for (const key in allowExtra) {
2832
+ if (allowExtra[key]) {
2833
+ allowed[key] = true;
2834
+ }
2835
+ }
2836
+ }
2837
+
2838
+ ethers.utils.checkProperties(transaction, allowed);
2839
+ const result = {} // Some nodes (INFURA ropsten; INFURA mainnet is fine) do not like leading zeros.
2840
+ ;
2841
+ ['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(key => {
2842
+ if (!transaction[key]) {
2843
+ return;
2844
+ }
2845
+
2846
+ const value = ethers.utils.hexValue(transaction[key]);
2847
+
2848
+ if (key === 'gasLimit') {
2849
+ key = 'gas';
2850
+ }
2851
+
2852
+ result[key] = value;
2853
+ });
2854
+ ['from', 'to', 'data'].forEach(key => {
2855
+ if (!transaction[key]) {
2856
+ return;
2857
+ }
2858
+
2859
+ result[key] = ethers.utils.hexlify(transaction[key]);
2860
+ });
2861
+ const auxiliary = transaction['auxiliary'];
2862
+
2863
+ if (auxiliary && auxiliary.length > 0) {
2864
+ result['auxiliary'] = [];
2865
+ auxiliary.forEach(a => {
2866
+ result['auxiliary'].push(hexlifyTransaction(a));
2867
+ });
2868
+ }
2869
+
2870
+ return result;
2871
+ };
2872
+
2873
+ class WalletCommands {
2874
+ constructor(walletProvider) {
2875
+ var _this = this;
2876
+
2877
+ this.wallet = void 0;
2878
+
2879
+ this.recoverWalletConfig = async function (address, digest, signature, chainId, walletContext) {
2880
+ walletContext = walletContext || (await _this.wallet.getWalletContext());
2881
+ return recoverWalletConfig(address, digest, signature, chainId, walletContext);
2882
+ };
2883
+
2884
+ this.recoverWalletConfigFromMessage = async function (address, message, signature, chainId, walletContext) {
2885
+ walletContext = walletContext || (await _this.wallet.getWalletContext());
2886
+ return recoverWalletConfig(address, encodeMessageDigest(message), signature, chainId, walletContext);
2887
+ };
2888
+
2889
+ this.recoverWalletConfigFromTypedData = async function (address, typedData, signature, chainId, walletContext) {
2890
+ walletContext = walletContext || (await _this.wallet.getWalletContext());
2891
+ return recoverWalletConfig(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext);
2892
+ };
2893
+
2894
+ this.wallet = walletProvider;
2895
+ } // Sign message on a specified chain, or DefaultChain by default
2896
+
2897
+
2898
+ signMessage(message, chainId, allSigners) {
2899
+ const signer = this.wallet.getSigner();
2900
+ if (!signer) throw new Error('unable to get signer');
2901
+ return signer.signMessage(message, chainId, allSigners);
2902
+ } // Sign message on the AuthChain
2903
+
2904
+
2905
+ async signAuthMessage(message, allSigners) {
2906
+ const signer = await this.wallet.getAuthSigner();
2907
+ if (!signer) throw new Error('unable to get AuthChain signer');
2908
+ return signer.signMessage(message, await signer.getChainId(), allSigners);
2909
+ } // Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
2910
+
2911
+
2912
+ signTypedData(domain, types, message, chainId, allSigners) {
2913
+ const signer = this.wallet.getSigner();
2914
+ if (!signer) throw new Error('unable to get signer');
2915
+ return signer.signTypedData(domain, types, message, chainId, allSigners);
2916
+ } // Sign EIP-712 TypedData on the AuthChain
2917
+
2918
+
2919
+ async signAuthTypedData(domain, types, message, allSigners) {
2920
+ const signer = await this.wallet.getAuthSigner();
2921
+ if (!signer) throw new Error('unable to get AuthChain signer');
2922
+ return signer.signTypedData(domain, types, message, await signer.getChainId(), allSigners);
2923
+ } // Verify signature of a digest, one of a message, typedData or other
2924
+
2925
+
2926
+ async isValidSignature(address, digest, signature, chainId, walletContext) {
2927
+ const provider = this.wallet.getProvider(chainId);
2928
+ if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
2929
+ return isValidSignature(address, digest, signature, provider, chainId, walletContext);
2930
+ } // Verify message signature
2931
+
2932
+
2933
+ isValidMessageSignature(address, message, signature, chainId, walletContext) {
2934
+ return this.isValidSignature(address, encodeMessageDigest(message), signature, chainId, walletContext);
2935
+ } // Verify typedData signature
2936
+
2937
+
2938
+ isValidTypedDataSignature(address, typedData, signature, chainId, walletContext) {
2939
+ return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext);
2940
+ } // Recover the WalletConfig from a signature + digest combo
2941
+ // sendTransaction()
2942
+ // sendTransactions()
2943
+ // sendETH()
2944
+ // sendToken()
2945
+ // sendCoin() -- sugar for sendToken()
2946
+ // sendCollectible() -- sugar for sendToken()
2947
+ // callContract()
2948
+ // transactionHistory()
2949
+ // getReceipt()
2950
+ // getLogs()
2951
+ // // ..
2952
+ // isWalletDeployed()
2953
+ // deployWallet()
2954
+ // validateSignature()
2955
+ // recoverWalletConfig()
2956
+ // recoverAddress()
2957
+
2958
+
2959
+ }
2960
+
2961
+ class Wallet {
2962
+ constructor(defaultNetworkId, config) {
2963
+ var _this = this;
2964
+
2965
+ this.commands = void 0;
2966
+ this.config = void 0;
2967
+ this.session = void 0;
2968
+ this.connectedSites = void 0;
2969
+ this.transport = void 0;
2970
+ this.networks = void 0;
2971
+ this.providers = void 0;
2972
+
2973
+ this.init = () => {
2974
+ var _this$config$transpor, _this$config$transpor2, _this$config$transpor3, _this$config$transpor4, _this$config$transpor5, _this$config$transpor6;
2975
+
2976
+ if (this.transport.provider) {
2977
+ // init must have already been called
2978
+ return;
2979
+ } // Setup provider
2980
+
2981
+
2982
+ this.transport.messageProvider = new MuxMessageProvider(); // multiple message provider setup, first one to connect will be the main transport
2983
+
2984
+ if ((_this$config$transpor = this.config.transports) != null && (_this$config$transpor2 = _this$config$transpor.windowTransport) != null && _this$config$transpor2.enabled) {
2985
+ this.transport.windowMessageProvider = new WindowMessageProvider(this.config.walletAppURL);
2986
+ this.transport.messageProvider.add(this.transport.windowMessageProvider);
2987
+ }
2988
+
2989
+ if ((_this$config$transpor3 = this.config.transports) != null && (_this$config$transpor4 = _this$config$transpor3.proxyTransport) != null && _this$config$transpor4.enabled) {
2990
+ this.transport.proxyMessageProvider = new ProxyMessageProvider(this.config.transports.proxyTransport.appPort);
2991
+ this.transport.messageProvider.add(this.transport.proxyMessageProvider);
2992
+ }
2993
+
2994
+ if ((_this$config$transpor5 = this.config.transports) != null && (_this$config$transpor6 = _this$config$transpor5.extensionTransport) != null && _this$config$transpor6.enabled) {
2995
+ this.transport.extensionMessageProvider = new ExtensionMessageProvider(this.config.transports.extensionTransport.runtime);
2996
+ this.transport.messageProvider.add(this.transport.extensionMessageProvider);
2997
+ }
2998
+
2999
+ this.transport.messageProvider.register(); // .....
3000
+
3001
+ this.transport.allowProvider = allowProviderMiddleware(request => {
3002
+ if (request.method === 'sequence_setDefaultNetwork') return true;
3003
+ const isConnected = this.isConnected();
3004
+
3005
+ if (!isConnected) {
3006
+ throw new Error('Sequence: wallet not connected');
3007
+ }
3008
+
3009
+ return isConnected;
3010
+ }); // ...
3011
+
3012
+ this.transport.networkProvider = networkProviderMiddleware(request => {
3013
+ // return stub chainId of 0 when not connected to any
3014
+ if (!this.networks || this.networks.length === 0) return 0; // return the default chainId as we're connected
3015
+
3016
+ return this.networks[0].chainId;
3017
+ }); // Provider proxy to support middleware stack of logging, caching and read-only rpc calls
3018
+
3019
+ this.transport.cachedProvider = new CachedProvider();
3020
+ this.transport.cachedProvider.onUpdate(() => {
3021
+ if (!this.session) this.session = {
3022
+ providerCache: {}
3023
+ };
3024
+ this.session.providerCache = this.transport.cachedProvider.getCache();
3025
+ this.saveSession(this.session);
3026
+ }); // ..
3027
+
3028
+ this.transport.router = new JsonRpcRouter([loggingProviderMiddleware, this.transport.networkProvider, this.transport.allowProvider, exceptionProviderMiddleware, this.transport.cachedProvider], this.transport.messageProvider);
3029
+ this.transport.provider = new Web3Provider(this.transport.router); // NOTE: we don't listen on 'connect' even here as we handle it within connect() method
3030
+ // in more synchronous flow.
3031
+ // below will update the wallet session object and persist it. In case the session
3032
+ // is undefined, we consider the session to have been removed by the user, so we clear it.
3033
+
3034
+ this.transport.messageProvider.on('open', openInfo => {
3035
+ const {
3036
+ session
3037
+ } = openInfo;
3038
+
3039
+ if (!session) {
3040
+ if (this.session && this.session.accountAddress) {
3041
+ // emit disconnect even if previously we had a session, and now we don't.
3042
+ this.transport.messageProvider.emit('disconnect');
3043
+ }
3044
+
3045
+ this.clearSession();
3046
+ } else {
3047
+ this.useSession(session, true);
3048
+ }
3049
+ }); // below will update the account upon wallet connect/disconnect (aka, login/logout)
3050
+
3051
+ this.transport.messageProvider.on('accountsChanged', accounts => {
3052
+ if (!accounts || accounts.length === 0 || accounts[0] === '') {
3053
+ this.clearSession();
3054
+ } else {
3055
+ this.useSession({
3056
+ accountAddress: accounts[0]
3057
+ }, true);
3058
+ }
3059
+ }); // below will update the networks automatically when the wallet networks change
3060
+
3061
+ this.transport.messageProvider.on('networks', networks => {
3062
+ this.useSession({
3063
+ networks: networks
3064
+ }, true);
3065
+ }); // below will update the wallet context automatically
3066
+
3067
+ this.transport.messageProvider.on('walletContext', walletContext => {
3068
+ this.useSession({
3069
+ walletContext: walletContext
3070
+ }, true);
3071
+ }); // Load existing session from localStorage
3072
+
3073
+ const session = this.loadSession();
3074
+
3075
+ if (session) {
3076
+ this.useSession(session, false);
3077
+ }
3078
+ };
3079
+
3080
+ this.connect = async function (options) {
3081
+ if ((options == null ? void 0 : options.refresh) === true) {
3082
+ _this.disconnect();
3083
+ }
3084
+
3085
+ if (_this.isConnected() && _this.isSiteConnected(options == null ? void 0 : options.origin) && !!_this.session && !(options != null && options.authorize) && !(options != null && options.askForEmail)) {
3086
+ return {
3087
+ connected: true,
3088
+ session: _this.session,
3089
+ chainId: ethers.utils.hexlify(await _this.getChainId())
3090
+ };
3091
+ }
3092
+
3093
+ if (options) {
3094
+ if (options.authorize && (!options.app || options.app === '')) {
3095
+ throw new Error(`connecting with 'authorize' option also requires 'app' to be set`);
3096
+ }
3097
+ }
3098
+
3099
+ await _this.openWallet(undefined, {
3100
+ type: 'connect',
3101
+ options
3102
+ });
3103
+ const connectDetails = await _this.transport.messageProvider.waitUntilConnected().catch(_ => {
3104
+ return {
3105
+ connected: false
3106
+ };
3107
+ });
3108
+
3109
+ if (connectDetails.connected) {
3110
+ if (!!connectDetails.session) {
3111
+ _this.useSession(connectDetails.session, true);
3112
+
3113
+ _this.addConnectedSite(options == null ? void 0 : options.origin);
3114
+ } else {
3115
+ throw new Error('impossible state, connect response is missing session');
3116
+ }
3117
+ }
3118
+
3119
+ return connectDetails;
3120
+ };
3121
+
3122
+ this.authorize = async function (options) {
3123
+ return _this.connect(_extends({}, options, {
3124
+ authorize: true
3125
+ }));
3126
+ };
3127
+
3128
+ this.getSession = () => {
3129
+ if (!this.isConnected()) {
3130
+ return undefined;
3131
+ }
3132
+
3133
+ return this.session;
3134
+ };
3135
+
3136
+ this.getAddress = async function () {
3137
+ if (!_this.isConnected()) {
3138
+ throw new Error('connect first');
3139
+ }
3140
+
3141
+ const session = _this.getSession();
3142
+
3143
+ return session.accountAddress;
3144
+ };
3145
+
3146
+ this.getNetworks = async function (chainId) {
3147
+ if (!_this.isConnected() || !_this.networks) {
3148
+ throw new Error('connect first');
3149
+ }
3150
+
3151
+ if (chainId) {
3152
+ // filter list to just the specific chain requested
3153
+ const network = findNetworkConfig(_this.networks, chainId);
3154
+ return network ? [network] : [];
3155
+ }
3156
+
3157
+ return _this.networks;
3158
+ };
3159
+
3160
+ this.getChainId = async function () {
3161
+ if (!_this.networks || _this.networks.length < 1) {
3162
+ throw new Error('networks have not been set by session. connect first.');
3163
+ } // default chain id is first one in the list, by design
3164
+
3165
+
3166
+ const network = _this.networks[0];
3167
+
3168
+ if (!network.isDefaultChain) {
3169
+ throw new Error('expecting first network in list to be default chain');
3170
+ }
3171
+
3172
+ return network.chainId;
3173
+ };
3174
+
3175
+ this.getAuthChainId = async function () {
3176
+ if (!_this.networks || _this.networks.length < 1) {
3177
+ throw new Error('networks have not been set by session. connect first.');
3178
+ } // auth chain is first or second one in the list, by design
3179
+
3180
+
3181
+ const network0 = _this.networks[0];
3182
+
3183
+ if (network0.isAuthChain) {
3184
+ return network0.chainId;
3185
+ }
3186
+
3187
+ if (_this.networks.length > 1) {
3188
+ const network1 = _this.networks[1];
3189
+
3190
+ if (network1.isAuthChain) {
3191
+ return network1.chainId;
3192
+ }
3193
+ }
3194
+
3195
+ throw new Error('expecting first or second network in list to be the auth chain');
3196
+ };
3197
+
3198
+ this.openWallet = async function (path, intent, networkId) {
3199
+ if ((intent == null ? void 0 : intent.type) !== 'connect' && !_this.isConnected()) {
3200
+ throw new Error('connect first');
3201
+ }
3202
+
3203
+ let currentNetworkId;
3204
+
3205
+ if (!_this.networks || _this.networks.length < 1) {
3206
+ currentNetworkId = _this.config.defaultNetworkId;
3207
+ } else {
3208
+ currentNetworkId = await _this.getChainId();
3209
+ }
3210
+
3211
+ _this.transport.messageProvider.openWallet(path, intent, networkId || currentNetworkId);
3212
+
3213
+ await _this.transport.messageProvider.waitUntilOpened();
3214
+ return true;
3215
+ };
3216
+
3217
+ this.closeWallet = () => {
3218
+ this.transport.messageProvider.closeWallet();
3219
+ };
3220
+
3221
+ this.loadSession = () => {
3222
+ const data = window.localStorage.getItem('@sequence.session');
3223
+
3224
+ if (!data || data === '') {
3225
+ return undefined;
3226
+ }
3227
+
3228
+ try {
3229
+ const session = JSON.parse(data);
3230
+ return session;
3231
+ } catch (err) {
3232
+ logger.warn('loadSession failed, unable to parse session payload from localStorage.');
3233
+ return undefined;
3234
+ }
3235
+ };
3236
+
3237
+ this.saveSession = session => {
3238
+ logger.debug('wallet provider: saving session');
3239
+ const data = JSON.stringify(session);
3240
+ window.localStorage.setItem('@sequence.session', data);
3241
+ };
3242
+
3243
+ this.useSession = async function (session, autoSave = true) {
3244
+ if (!_this.session) _this.session = {}; // setup wallet context
3245
+
3246
+ if (_this.config.walletContext) {
3247
+ _this.session.walletContext = _this.config.walletContext;
3248
+ } else if (session.walletContext) {
3249
+ _this.session.walletContext = session.walletContext;
3250
+ } // setup account
3251
+
3252
+
3253
+ if (session.accountAddress) {
3254
+ _this.useAccountAddress(session.accountAddress);
3255
+ } // setup networks
3256
+
3257
+
3258
+ if (session.networks) {
3259
+ _this.useNetworks(session.networks);
3260
+ } // setup provider cache
3261
+
3262
+
3263
+ if (session.providerCache) {
3264
+ _this.transport.cachedProvider.setCache(session.providerCache);
3265
+ } // persist
3266
+
3267
+
3268
+ if (autoSave) {
3269
+ _this.saveSession(_this.session);
3270
+ }
3271
+ };
3272
+
3273
+ // config is a Partial, so that we may intersect it with the DefaultProviderConfig,
3274
+ // which allows easy overriding and control of the config.
3275
+ this.config = _extends({}, DefaultProviderConfig);
3276
+
3277
+ if (config) {
3278
+ this.config = _extends({}, this.config, config);
3279
+ }
3280
+
3281
+ if (defaultNetworkId) {
3282
+ this.config.defaultNetworkId = defaultNetworkId;
3283
+ } else if (!this.config.defaultNetworkId) {
3284
+ this.config.defaultNetworkId = 'mainnet';
3285
+ }
3286
+
3287
+ this.transport = {};
3288
+ this.networks = [];
3289
+ this.providers = {};
3290
+ this.connectedSites = new LocalStore('@sequence.connectedSites', []);
3291
+ this.commands = new WalletCommands(this);
3292
+ this.init();
3293
+ }
3294
+
3295
+ addConnectedSite(origin) {
3296
+ origin = origin || window.location.origin;
3297
+ const connectedSites = this.connectedSites.get();
3298
+
3299
+ if (connectedSites) {
3300
+ if (connectedSites.includes(origin)) {
3301
+ return;
3302
+ }
3303
+
3304
+ this.connectedSites.set([...connectedSites, origin]);
3305
+ } else {
3306
+ this.connectedSites.set([origin]);
3307
+ }
3308
+ }
3309
+
3310
+ removeConnectedSite(origin) {
3311
+ const authorized = this.connectedSites.get();
3312
+
3313
+ if (authorized) {
3314
+ this.connectedSites.set(authorized.filter(domain => domain !== origin));
3315
+ }
3316
+ }
3317
+
3318
+ getConnectedSites() {
3319
+ return this.connectedSites.get();
3320
+ }
3321
+
3322
+ isSiteConnected(origin) {
3323
+ const authorized = this.connectedSites.get();
3324
+ return !!authorized && authorized.includes(origin || window.location.origin);
3325
+ }
3326
+
3327
+ disconnect() {
3328
+ if (this.isOpened()) {
3329
+ this.closeWallet();
3330
+ }
3331
+
3332
+ this.clearSession();
3333
+ }
3334
+
3335
+ getProviderConfig() {
3336
+ return this.config;
3337
+ }
3338
+
3339
+ isOpened() {
3340
+ return this.transport.messageProvider.isOpened();
3341
+ }
3342
+
3343
+ isConnected() {
3344
+ return this.session !== undefined && this.session.networks !== undefined && this.session.networks.length > 0 && this.networks !== undefined && this.networks.length > 0 && !!this.session.accountAddress && this.session.accountAddress.startsWith('0x');
3345
+ }
3346
+
3347
+ getProvider(chainId) {
3348
+ // return the top-level provider message transport when chainId is unspecified
3349
+ // and user has not logged in
3350
+ if (!this.isConnected()) {
3351
+ if (chainId) {
3352
+ throw new Error(`session is empty. connect and try again.`);
3353
+ } else {
3354
+ return this.transport.provider;
3355
+ }
3356
+ }
3357
+
3358
+ let network = this.networks[0];
3359
+
3360
+ if (chainId) {
3361
+ network = findNetworkConfig(this.networks, chainId);
3362
+
3363
+ if (!network) {
3364
+ throw new Error(`network ${chainId} is not in the network list`);
3365
+ }
3366
+ } // return memoized network provider
3367
+
3368
+
3369
+ if (this.providers[network.chainId]) {
3370
+ return this.providers[network.chainId];
3371
+ } // builder web3 provider stack
3372
+
3373
+
3374
+ let provider; // network.provider may be set by the ProviderConfig override
3375
+
3376
+ const rpcProvider = network.provider ? network.provider : new JsonRpcProvider(network.rpcUrl, network.chainId);
3377
+
3378
+ if (network.isDefaultChain) {
3379
+ // communicating with defaultChain will prioritize the wallet message transport
3380
+ const router = new JsonRpcRouter([loggingProviderMiddleware, exceptionProviderMiddleware, new EagerProvider({
3381
+ accountAddress: this.session.accountAddress,
3382
+ walletContext: this.session.walletContext
3383
+ }), new SigningProvider(this.transport.provider), this.transport.cachedProvider], new JsonRpcSender(rpcProvider));
3384
+ provider = new Web3Provider(router, network.chainId);
3385
+ } else {
3386
+ // communicating with another chain will bind to that network, but will forward
3387
+ // any signing-related requests to the wallet message transport
3388
+ const router = new JsonRpcRouter([loggingProviderMiddleware, exceptionProviderMiddleware, new EagerProvider({
3389
+ accountAddress: this.session.accountAddress,
3390
+ walletContext: this.session.walletContext,
3391
+ chainId: network.chainId
3392
+ }), new SigningProvider(this.transport.provider), new CachedProvider(network.chainId)], new JsonRpcSender(rpcProvider));
3393
+ provider = new Web3Provider(router, network.chainId);
3394
+ }
3395
+
3396
+ this.providers[network.chainId] = provider;
3397
+ return provider;
3398
+ }
3399
+
3400
+ async getAuthProvider() {
3401
+ return this.getProvider((await this.getAuthNetwork()).chainId);
3402
+ }
3403
+
3404
+ async getAuthNetwork() {
3405
+ return (await this.getNetworks()).find(n => n.isAuthChain);
3406
+ }
3407
+
3408
+ getAllProviders() {
3409
+ return this.providers;
3410
+ }
3411
+
3412
+ getSigner(chainId) {
3413
+ return this.getProvider(chainId).getSigner();
3414
+ }
3415
+
3416
+ async getAuthSigner() {
3417
+ return (await this.getAuthProvider()).getSigner();
3418
+ }
3419
+
3420
+ getWalletConfig(chainId) {
3421
+ return this.getSigner().getWalletConfig(chainId);
3422
+ }
3423
+
3424
+ getWalletState(chainId) {
3425
+ return this.getSigner().getWalletState(chainId);
3426
+ }
3427
+
3428
+ getWalletContext() {
3429
+ return this.getSigner().getWalletContext();
3430
+ }
3431
+
3432
+ isDeployed(chainId) {
3433
+ return this.getSigner(chainId).isDeployed();
3434
+ }
3435
+
3436
+ on(event, fn) {
3437
+ this.transport.messageProvider.on(event, fn);
3438
+ }
3439
+
3440
+ once(event, fn) {
3441
+ this.transport.messageProvider.once(event, fn);
3442
+ }
3443
+
3444
+ useAccountAddress(accountAddress) {
3445
+ if (!this.session) this.session = {};
3446
+ this.session.accountAddress = ethers.utils.getAddress(accountAddress);
3447
+ }
3448
+
3449
+ useNetworks(networks) {
3450
+ // set networks in the session
3451
+ if (!this.session) this.session = {}; // set networks on session object
3452
+
3453
+ this.session.networks = networks; // short-circuit if setting empty network list (aka logged out state)
3454
+
3455
+ if (!this.session.networks || this.session.networks.length === 0) {
3456
+ return;
3457
+ } // check if any custom network settings, otherwise return early
3458
+
3459
+
3460
+ if (!this.config.networks && !this.config.networkRpcUrl) {
3461
+ this.networks = networks;
3462
+ return;
3463
+ } // combine custom network config with networks in the session
3464
+
3465
+
3466
+ if (this.config.networks) {
3467
+ this.networks = networks.map(n => _extends({}, n)); // copy
3468
+
3469
+ this.config.networks.forEach(n => {
3470
+ const network = findNetworkConfig(this.networks, n.chainId || n.name);
3471
+ if (!network) return;
3472
+ updateNetworkConfig(n, network);
3473
+ });
3474
+ ensureValidNetworks(this.networks, true);
3475
+ } // an extra override for convenience
3476
+
3477
+
3478
+ if (this.config.networkRpcUrl) {
3479
+ this.networks[0].rpcUrl = this.config.networkRpcUrl;
3480
+ }
3481
+ }
3482
+
3483
+ clearSession() {
3484
+ var _this$transport$cache;
3485
+
3486
+ logger.debug('wallet provider: clearing session');
3487
+ window.localStorage.removeItem('@sequence.session');
3488
+ this.session = undefined;
3489
+ this.networks = [];
3490
+ this.providers = {};
3491
+ (_this$transport$cache = this.transport.cachedProvider) == null ? void 0 : _this$transport$cache.clearCache();
3492
+ }
3493
+
3494
+ }
3495
+ const DefaultProviderConfig = {
3496
+ walletAppURL: 'https://sequence.app',
3497
+ // walletSessionURL: 'https://session.sequence.app',
3498
+ transports: {
3499
+ windowTransport: {
3500
+ enabled: true
3501
+ },
3502
+ proxyTransport: {
3503
+ enabled: false
3504
+ }
3505
+ }
3506
+ };
3507
+
3508
+ export { BaseInjectedTransport, BaseProviderTransport, BaseWalletTransport, CHANNEL_ID, DefaultProviderConfig, ErrSignedInRequired, EventType, ExtensionMessageHandler, ExtensionMessageProvider, InitState, LocalStore, LocalWeb3Provider, MuxMessageProvider, OpenState, PROVIDER_OPEN_TIMEOUT, ProviderError, ProxyMessageChannel, ProxyMessageChannelPort, ProxyMessageHandler, ProxyMessageProvider, Wallet, WalletRequestHandler, Web3Provider, Web3Signer, WindowMessageHandler, WindowMessageProvider, WindowSessionParams, isBrowserExtension, isSequenceProvider, isValidSignature, nextMessageIdx, recoverWalletConfig };