@0xsequence/provider 2.3.19 → 2.3.21

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 (36) hide show
  1. package/dist/0xsequence-provider.cjs.d.ts +2 -2
  2. package/dist/0xsequence-provider.cjs.dev.js +4347 -0
  3. package/dist/0xsequence-provider.cjs.js +6 -15
  4. package/dist/0xsequence-provider.cjs.prod.js +4347 -0
  5. package/dist/0xsequence-provider.esm.js +4290 -0
  6. package/dist/declarations/src/analytics.d.ts +13 -0
  7. package/dist/declarations/src/client.d.ts +92 -0
  8. package/dist/declarations/src/index.d.ts +9 -0
  9. package/dist/declarations/src/init.d.ts +36 -0
  10. package/dist/declarations/src/provider.d.ts +141 -0
  11. package/dist/declarations/src/signer.d.ts +88 -0
  12. package/dist/declarations/src/transactions.d.ts +2 -0
  13. package/dist/declarations/src/transports/base-provider-transport.d.ts +49 -0
  14. package/dist/declarations/src/transports/base-wallet-transport.d.ts +43 -0
  15. package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +17 -0
  16. package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +12 -0
  17. package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +11 -0
  18. package/dist/declarations/src/transports/extension-transport/index.d.ts +3 -0
  19. package/dist/declarations/src/transports/index.d.ts +8 -0
  20. package/dist/declarations/src/transports/mux-transport/index.d.ts +1 -0
  21. package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +47 -0
  22. package/dist/declarations/src/transports/proxy-transport/index.d.ts +3 -0
  23. package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +15 -0
  24. package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +12 -0
  25. package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +12 -0
  26. package/dist/declarations/src/transports/unreal-transport/index.d.ts +2 -0
  27. package/dist/declarations/src/transports/unreal-transport/unreal-message-handler.d.ts +14 -0
  28. package/dist/declarations/src/transports/unreal-transport/unreal-message-provider.d.ts +15 -0
  29. package/dist/declarations/src/transports/wallet-request-handler.d.ts +64 -0
  30. package/dist/declarations/src/transports/window-transport/index.d.ts +2 -0
  31. package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +15 -0
  32. package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +13 -0
  33. package/dist/declarations/src/types.d.ts +262 -0
  34. package/dist/declarations/src/utils/index.d.ts +18 -0
  35. package/dist/declarations/src/utils.d.ts +37 -0
  36. package/package.json +10 -10
@@ -0,0 +1,4347 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var abi = require('@0xsequence/abi');
6
+ var core = require('@0xsequence/core');
7
+ var ethers = require('ethers');
8
+ var eventemitter2 = require('eventemitter2');
9
+ var utils = require('@0xsequence/utils');
10
+ var network = require('@0xsequence/network');
11
+ var auth = require('@0xsequence/auth');
12
+ var tracker = require('@databeat/tracker');
13
+
14
+ const PROHIBITED_FUNCTIONS = new Map(['addHook(bytes4,address)', 'clearExtraImageHashes(bytes32[])', 'removeHook(bytes4)', 'setExtraImageHash(bytes32,uint256)', 'updateIPFSRoot(bytes32)', 'updateImageHash(bytes32)', 'updateImageHashAndIPFS(bytes32,bytes32)', 'updateImplementation(address)'].map(signature => [ethers.ethers.id(signature).slice(0, 10), signature]));
15
+ function validateTransactionRequest(wallet, transaction) {
16
+ const transactions = core.commons.transaction.fromTransactionish(wallet, transaction);
17
+ const unwound = core.commons.transaction.unwind(wallet, transactions);
18
+ unwound.forEach(transaction => validateTransaction(wallet, transaction));
19
+ }
20
+ function validateTransaction(wallet, transaction) {
21
+ if (transaction.to.toLowerCase() === wallet.toLowerCase()) {
22
+ if (transaction.data) {
23
+ const data = ethers.ethers.getBytes(transaction.data);
24
+ if (data.length >= 4 && !isCreateContractCall(data)) {
25
+ throw new Error('self calls are forbidden');
26
+ }
27
+ }
28
+ }
29
+ if (transaction.delegateCall) {
30
+ throw new Error('delegate calls are forbidden');
31
+ }
32
+ if (transaction.data) {
33
+ const data = ethers.ethers.hexlify(transaction.data);
34
+ const selector = data.slice(0, 10);
35
+ const signature = PROHIBITED_FUNCTIONS.get(selector);
36
+ if (signature) {
37
+ const name = signature.slice(0, signature.indexOf('('));
38
+ throw new Error(`${name} calls are forbidden`);
39
+ }
40
+ }
41
+ }
42
+ function isCreateContractCall(data) {
43
+ const walletInterface = new ethers.ethers.Interface(abi.walletContracts.mainModule.abi);
44
+ try {
45
+ walletInterface.decodeFunctionData('createContract', data);
46
+ return true;
47
+ } catch (_unused) {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ function _extends() {
53
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
54
+ for (var e = 1; e < arguments.length; e++) {
55
+ var t = arguments[e];
56
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
57
+ }
58
+ return n;
59
+ }, _extends.apply(null, arguments);
60
+ }
61
+
62
+ function isProviderTransport(transport) {
63
+ return transport && typeof transport === 'object' && typeof transport.register === 'function' && typeof transport.unregister === 'function' && typeof transport.openWallet === 'function' && typeof transport.closeWallet === 'function' && typeof transport.isOpened === 'function' && typeof transport.isConnected === 'function' && typeof transport.on === 'function';
64
+ }
65
+
66
+ // Older versions of sequence.js will require a JsonRpcResponse result type, but newer versions use raw EIP1193 results
67
+
68
+ // ProviderMessageCallback is used to respond to ProviderMessage requests. The error
69
+ // argument is for exceptions during the execution, and response is the response payload
70
+ // which may contain the result or an error payload from the wallet.
71
+
72
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
73
+
74
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
75
+ class WindowSessionParams extends URLSearchParams {
76
+ static new(init) {
77
+ return new URLSearchParams(init);
78
+ }
79
+ }
80
+ let EventType = /*#__PURE__*/function (EventType) {
81
+ EventType["OPEN"] = "open";
82
+ EventType["CLOSE"] = "close";
83
+ EventType["MESSAGE"] = "message";
84
+ EventType["CONNECT"] = "connect";
85
+ EventType["DISCONNECT"] = "disconnect";
86
+ EventType["ACCOUNTS_CHANGED"] = "accountsChanged";
87
+ EventType["CHAIN_CHANGED"] = "chainChanged";
88
+ EventType["NETWORKS"] = "networks";
89
+ EventType["WALLET_CONTEXT"] = "walletContext";
90
+ EventType["INIT"] = "init";
91
+ EventType["DEBUG"] = "_debug";
92
+ return EventType;
93
+ }({});
94
+ let OpenState = /*#__PURE__*/function (OpenState) {
95
+ OpenState[OpenState["CLOSED"] = 0] = "CLOSED";
96
+ OpenState[OpenState["OPENING"] = 1] = "OPENING";
97
+ OpenState[OpenState["OPENED"] = 2] = "OPENED";
98
+ return OpenState;
99
+ }({});
100
+ let InitState = /*#__PURE__*/function (InitState) {
101
+ InitState[InitState["NIL"] = 0] = "NIL";
102
+ InitState[InitState["SENT_NONCE"] = 1] = "SENT_NONCE";
103
+ InitState[InitState["OK"] = 2] = "OK";
104
+ return InitState;
105
+ }({});
106
+
107
+ /** Options to further customize the wallet experience. */
108
+
109
+ /** light and dark are the main themes, to use other themes in wallet settings,
110
+ * you can use the camel case version of the name in the wallet settings.
111
+ * For example: "Blue Dark" on wallet UI can be passed as "blueDark" */
112
+
113
+ // | 'large'
114
+
115
+ class ProviderError extends Error {
116
+ constructor(message) {
117
+ super(message);
118
+ this.name = 'ProviderError';
119
+ }
120
+ }
121
+ const ErrSignedInRequired = new ProviderError('Wallet is not signed in. Connect a wallet and try again.');
122
+
123
+ // TODO: lets build some nice error handling tools, prob in /utils ...
124
+
125
+ const PROVIDER_OPEN_TIMEOUT = 30000; // in ms
126
+
127
+ let _messageIdx = 0;
128
+ const nextMessageIdx = () => ++_messageIdx;
129
+ class BaseProviderTransport {
130
+ constructor(projectAccessKey) {
131
+ var _this = this;
132
+ this.pendingMessageRequests = [];
133
+ this.responseCallbacks = new Map();
134
+ this.state = void 0;
135
+ this.confirmationOnly = false;
136
+ this.events = new eventemitter2.EventEmitter2();
137
+ this.openPayload = void 0;
138
+ this.connectPayload = void 0;
139
+ this.accountsChangedPayload = void 0;
140
+ this.networksPayload = void 0;
141
+ this.walletContextPayload = void 0;
142
+ this._sessionId = void 0;
143
+ this._init = void 0;
144
+ this._registered = void 0;
145
+ this.projectAccessKey = void 0;
146
+ // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet
147
+ this.sendMessageRequest = async function (message) {
148
+ return new Promise((resolve, reject) => {
149
+ if ((!message.idx || message.idx <= 0) && message.type !== 'init') {
150
+ reject(new Error('message idx not set'));
151
+ }
152
+ const responseCallback = (error, response) => {
153
+ if (error) {
154
+ reject(error);
155
+ } else if (response) {
156
+ resolve(response);
157
+ } else {
158
+ throw new Error('no valid response to return');
159
+ }
160
+ };
161
+ const idx = message.idx;
162
+ if (!_this.responseCallbacks.get(idx)) {
163
+ _this.responseCallbacks.set(idx, responseCallback);
164
+ } else {
165
+ reject(new Error('duplicate message idx, should never happen'));
166
+ }
167
+ if (!_this.isOpened()) {
168
+ utils.logger.debug('pushing to pending requests', message);
169
+ _this.pendingMessageRequests.push(message);
170
+ } else {
171
+ _this.sendMessage(message);
172
+ }
173
+ });
174
+ };
175
+ this.waitUntilOpened = async function (openTimeout = PROVIDER_OPEN_TIMEOUT) {
176
+ let opened = false;
177
+ return Promise.race([new Promise((_, reject) => {
178
+ const timeout = setTimeout(() => {
179
+ clearTimeout(timeout);
180
+ // only emit close if the timeout wins the race
181
+ if (!opened) {
182
+ _this.state = OpenState.CLOSED;
183
+ _this.events.emit('close', {
184
+ code: 1005,
185
+ message: 'opening wallet timed out'
186
+ });
187
+ }
188
+ reject(new Error('opening wallet timed out'));
189
+ }, openTimeout);
190
+ }), new Promise(resolve => {
191
+ if (_this.isOpened()) {
192
+ var _this$openPayload;
193
+ opened = true;
194
+ resolve((_this$openPayload = _this.openPayload) == null ? void 0 : _this$openPayload.session);
195
+ return;
196
+ }
197
+ _this.events.once('open', openInfo => {
198
+ _this.openPayload = openInfo;
199
+ opened = true;
200
+ resolve(openInfo.session);
201
+ });
202
+ })]);
203
+ };
204
+ this.waitUntilConnected = async function () {
205
+ await _this.waitUntilOpened();
206
+ const connect = new Promise(resolve => {
207
+ if (_this.connectPayload) {
208
+ resolve(_this.connectPayload);
209
+ return;
210
+ }
211
+ _this.events.once('connect', connectDetails => {
212
+ _this.connectPayload = connectDetails;
213
+ resolve(connectDetails);
214
+ });
215
+ });
216
+ const closeWallet = new Promise((_, reject) => {
217
+ _this.events.once('close', error => {
218
+ if (error) {
219
+ reject(new Error(`wallet closed due to ${JSON.stringify(error)}`));
220
+ } else {
221
+ reject(new Error(`user closed the wallet`));
222
+ }
223
+ });
224
+ });
225
+ return Promise.race([connect, closeWallet]);
226
+ };
227
+ this.state = OpenState.CLOSED;
228
+ this._registered = false;
229
+ this._init = InitState.NIL;
230
+ this.projectAccessKey = projectAccessKey;
231
+ }
232
+ get registered() {
233
+ return this._registered;
234
+ }
235
+ register() {
236
+ throw new Error('abstract method');
237
+ }
238
+ unregister() {
239
+ throw new Error('abstract method');
240
+ }
241
+ openWallet(path, intent, networkId) {
242
+ throw new Error('abstract method');
243
+ }
244
+ closeWallet() {
245
+ throw new Error('abstract method');
246
+ }
247
+ isOpened() {
248
+ return this.registered && this.state === OpenState.OPENED;
249
+ }
250
+ isConnected() {
251
+ var _this$openPayload2;
252
+ // if we're registered, and we have the account details, then we are connected
253
+ const session = (_this$openPayload2 = this.openPayload) == null ? void 0 : _this$openPayload2.session;
254
+ return this.registered && session !== undefined && !!session.accountAddress && session.accountAddress.length === 42 && !!session.networks && session.networks.length > 0;
255
+ }
256
+ async request(request) {
257
+ if (this.state === OpenState.CLOSED) {
258
+ // flag the wallet to auto-close once user submits input. ie.
259
+ // prompting to sign a message or transaction
260
+ this.confirmationOnly = true;
261
+ }
262
+
263
+ // open/focus the wallet.
264
+ // automatically open the wallet when a provider request makes it here.
265
+ //
266
+ // NOTE: if we're not signed in, then the provider will fail, users must first connect+sign in.
267
+ //
268
+ // TODO: how does this behave with a session has expired?
269
+ this.openWallet(undefined, {
270
+ type: 'jsonRpcRequest',
271
+ method: request.method
272
+ }, request.chainId);
273
+ if (!this.isOpened()) {
274
+ await this.waitUntilOpened(); // will throw on timeout
275
+ }
276
+ const response = await this.sendMessageRequest({
277
+ idx: nextMessageIdx(),
278
+ type: EventType.MESSAGE,
279
+ data: request,
280
+ chainId: request.chainId,
281
+ clientVersion: core.VERSION,
282
+ projectAccessKey: this.projectAccessKey
283
+ });
284
+ return response.data;
285
+ }
286
+
287
+ // handleMessage will handle message received from the remote wallet
288
+ handleMessage(message) {
289
+ var _this2 = this;
290
+ // init incoming for initial handshake with transport.
291
+ // always respond to INIT messages, e.g. on popup window reload
292
+ if (message.type === EventType.INIT) {
293
+ utils.logger.debug('MessageProvider, received INIT message', message);
294
+ const {
295
+ nonce
296
+ } = message.data;
297
+ if (!nonce || nonce.length == 0) {
298
+ utils.logger.error('invalid init nonce');
299
+ return;
300
+ }
301
+ this._init = InitState.OK;
302
+ this.sendMessage({
303
+ idx: -1,
304
+ type: EventType.INIT,
305
+ data: {
306
+ sessionId: this._sessionId,
307
+ nonce: nonce
308
+ },
309
+ clientVersion: core.VERSION
310
+ });
311
+ }
312
+ if (this._init !== InitState.OK) {
313
+ // if provider is not init'd, then we drop any received messages. the only
314
+ // message we will process is of event type 'init', as our acknowledgement
315
+ return;
316
+ }
317
+
318
+ // message is either a notification, or its a ProviderMessageResponse
319
+ utils.logger.debug('RECEIVED MESSAGE FROM WALLET', message.idx, message);
320
+ const requestIdx = message.idx;
321
+ const responseCallback = this.responseCallbacks.get(requestIdx);
322
+ if (requestIdx) {
323
+ this.responseCallbacks.delete(requestIdx);
324
+ }
325
+
326
+ // OPEN response
327
+ //
328
+ // Flip opened flag, and flush the pending queue
329
+ if (message.type === EventType.OPEN && !this.isOpened()) {
330
+ var _message$data, _message$data2;
331
+ if (this._sessionId && this._sessionId !== ((_message$data = message.data) == null ? void 0 : _message$data.sessionId)) {
332
+ utils.logger.debug('open event received from wallet, but does not match sessionId', this._sessionId);
333
+ return;
334
+ }
335
+
336
+ // check if open error occured due to invalid defaultNetworkId
337
+ if ((_message$data2 = message.data) != null && _message$data2.error) {
338
+ var _message$data3;
339
+ const err = new Error(`opening wallet failed: received ${(_message$data3 = message.data) == null ? void 0 : _message$data3.error}`);
340
+ utils.logger.error(err);
341
+ this.close();
342
+ throw err;
343
+ }
344
+
345
+ // success!
346
+ this.state = OpenState.OPENED;
347
+ this.openPayload = message.data;
348
+ this.events.emit('open', this.openPayload);
349
+
350
+ // flush pending requests when connected
351
+ if (this.pendingMessageRequests.length !== 0) {
352
+ const pendingMessageRequests = this.pendingMessageRequests.splice(0, this.pendingMessageRequests.length);
353
+ pendingMessageRequests.forEach(async function (pendingMessageRequest) {
354
+ _this2.sendMessage(pendingMessageRequest);
355
+ });
356
+ }
357
+ return;
358
+ }
359
+
360
+ // MESSAGE resposne
361
+ if (message.type === EventType.MESSAGE) {
362
+ // Require user confirmation, bring up wallet to prompt for input then close
363
+ // TODO: perhaps apply technique like in multicall to queue messages within
364
+ // a period of time, then close the window if responseCallbacks is empty, this is better.
365
+ if (this.confirmationOnly) {
366
+ setTimeout(() => {
367
+ if (this.responseCallbacks.size === 0) {
368
+ this.closeWallet();
369
+ }
370
+ }, 500); // TODO: be smarter about timer as we're processing the response callbacks..
371
+ }
372
+ if (!responseCallback) {
373
+ // NOTE: this would occur if 'idx' isn't set, which should never happen
374
+ // or when we register two handler, or duplicate messages with the same idx are sent,
375
+ // all of which should be prevented prior to getting to this point
376
+ throw new Error('impossible state');
377
+ }
378
+
379
+ // Callback to original caller
380
+ if (responseCallback) {
381
+ this.events.emit('message', message);
382
+ responseCallback(message.data.error, message);
383
+ return;
384
+ }
385
+ }
386
+
387
+ // ACCOUNTS_CHANGED -- when a user logs in or out
388
+ if (message.type === EventType.ACCOUNTS_CHANGED) {
389
+ this.accountsChangedPayload = {
390
+ accounts: []
391
+ };
392
+ if (message.data && message.data.length > 0) {
393
+ this.accountsChangedPayload = {
394
+ accounts: [ethers.ethers.getAddress(message.data[0])],
395
+ origin: message.origin
396
+ };
397
+ this.events.emit('accountsChanged', this.accountsChangedPayload.accounts, this.accountsChangedPayload.origin);
398
+ } else {
399
+ this.events.emit('accountsChanged', [], message.origin);
400
+ }
401
+ return;
402
+ }
403
+
404
+ // CHAIN_CHANGED -- when a user changes their default chain
405
+ if (message.type === EventType.CHAIN_CHANGED) {
406
+ this.events.emit('chainChanged', message.data, message.origin);
407
+ return;
408
+ }
409
+
410
+ // NOTIFY NETWORKS -- when a user connects or logs in
411
+ if (message.type === EventType.NETWORKS) {
412
+ this.networksPayload = message.data;
413
+ this.events.emit('networks', this.networksPayload);
414
+ return;
415
+ }
416
+
417
+ // NOTIFY WALLET_CONTEXT -- when a user connects or logs in
418
+ if (message.type === EventType.WALLET_CONTEXT) {
419
+ this.walletContextPayload = message.data;
420
+ this.events.emit('walletContext', this.walletContextPayload);
421
+ return;
422
+ }
423
+
424
+ // NOTIFY CLOSE -- when wallet instructs to close
425
+ if (message.type === EventType.CLOSE) {
426
+ if (this.state !== OpenState.CLOSED) {
427
+ this.close(message.data);
428
+ }
429
+ }
430
+
431
+ // NOTIFY CONNECT -- when wallet instructs we've connected
432
+ if (message.type === EventType.CONNECT) {
433
+ this.connectPayload = message.data;
434
+ this.events.emit('connect', this.connectPayload);
435
+ }
436
+
437
+ // NOTIFY DISCONNECT -- when wallet instructs to disconnect
438
+ if (message.type === EventType.DISCONNECT) {
439
+ if (this.isConnected()) {
440
+ this.events.emit('disconnect', message.data, message.origin);
441
+ this.close();
442
+ }
443
+ }
444
+ }
445
+ sendMessage(message) {
446
+ throw new Error('abstract method');
447
+ }
448
+ on(event, fn) {
449
+ this.events.on(event, fn);
450
+ }
451
+ once(event, fn) {
452
+ this.events.once(event, fn);
453
+ }
454
+ emit(event, ...args) {
455
+ return this.events.emit(event, ...args);
456
+ }
457
+ close(error) {
458
+ if (this.state === OpenState.CLOSED) return;
459
+ this.state = OpenState.CLOSED;
460
+ this.confirmationOnly = false;
461
+ this._sessionId = undefined;
462
+ utils.logger.info('closing wallet and flushing!');
463
+
464
+ // flush pending requests and return error to all callbacks
465
+ this.pendingMessageRequests.length = 0;
466
+ this.responseCallbacks.forEach(responseCallback => {
467
+ responseCallback(_extends({}, new Error('wallet closed'), {
468
+ code: 4001
469
+ }));
470
+ });
471
+ this.responseCallbacks.clear();
472
+ this.connectPayload = undefined;
473
+ this.openPayload = undefined;
474
+ this.accountsChangedPayload = undefined;
475
+ this.networksPayload = undefined;
476
+ this.walletContextPayload = undefined;
477
+ this.events.emit('close', error);
478
+ }
479
+ }
480
+
481
+ function messageIsExemptFromEIP191Prefix(message) {
482
+ return EIP_191_PREFIX_EXCEPTIONS.some(e => e.predicate(message));
483
+ }
484
+ const EIP_191_PREFIX_EXCEPTIONS = [
485
+ // NOTE: Decentraland does not support 191 correctly.
486
+ {
487
+ name: 'Decentraland Exception',
488
+ predicate: isDecentralandLoginMessage
489
+ },
490
+ // NOTE: 0x v3 does not support 191 correctly.
491
+ // See https://gov.0x.org/t/zeip-proposal-fix-v3-eip-191-non-compliance-when-validating-eip-1271-signatures/3396 for more info.
492
+ {
493
+ name: '0x v3 Exception',
494
+ predicate: isZeroExV3Order
495
+ }];
496
+ const DCL_REGEX = /^Decentraland Login\nEphemeral address: 0x[a-fA-F0-9]{40}\nExpiration: (\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$/;
497
+ function isDecentralandLoginMessage(bytes) {
498
+ try {
499
+ const stringified = ethers.ethers.toUtf8String(bytes);
500
+ return DCL_REGEX.test(stringified);
501
+ } catch (_unused) {
502
+ return false;
503
+ }
504
+ }
505
+
506
+ // try to interpret bytes as abi-encoded 0x v3 OrderWithHash -
507
+ // see https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md
508
+ function isZeroExV3Order(bytes) {
509
+ const abi = new ethers.ethers.Interface(ZeroXV3EIP1271OrderWithHashAbi);
510
+ try {
511
+ abi.decodeFunctionData('OrderWithHash', bytes);
512
+ return true;
513
+ } catch (err) {
514
+ // failed to decode ABI, so it's not a v3 order.
515
+ return false;
516
+ }
517
+ }
518
+ const ZeroXV3EIP1271OrderWithHashAbi = [{
519
+ inputs: [{
520
+ components: [{
521
+ internalType: 'address',
522
+ name: 'makerAddress',
523
+ type: 'address'
524
+ }, {
525
+ internalType: 'address',
526
+ name: 'takerAddress',
527
+ type: 'address'
528
+ }, {
529
+ internalType: 'address',
530
+ name: 'feeRecipientAddress',
531
+ type: 'address'
532
+ }, {
533
+ internalType: 'address',
534
+ name: 'senderAddress',
535
+ type: 'address'
536
+ }, {
537
+ internalType: 'uint256',
538
+ name: 'makerAssetAmount',
539
+ type: 'uint256'
540
+ }, {
541
+ internalType: 'uint256',
542
+ name: 'takerAssetAmount',
543
+ type: 'uint256'
544
+ }, {
545
+ internalType: 'uint256',
546
+ name: 'makerFee',
547
+ type: 'uint256'
548
+ }, {
549
+ internalType: 'uint256',
550
+ name: 'takerFee',
551
+ type: 'uint256'
552
+ }, {
553
+ internalType: 'uint256',
554
+ name: 'expirationTimeSeconds',
555
+ type: 'uint256'
556
+ }, {
557
+ internalType: 'uint256',
558
+ name: 'salt',
559
+ type: 'uint256'
560
+ }, {
561
+ internalType: 'bytes',
562
+ name: 'makerAssetData',
563
+ type: 'bytes'
564
+ }, {
565
+ internalType: 'bytes',
566
+ name: 'takerAssetData',
567
+ type: 'bytes'
568
+ }, {
569
+ internalType: 'bytes',
570
+ name: 'makerFeeAssetData',
571
+ type: 'bytes'
572
+ }, {
573
+ internalType: 'bytes',
574
+ name: 'takerFeeAssetData',
575
+ type: 'bytes'
576
+ }],
577
+ internalType: 'struct IEIP1271Data.Order',
578
+ name: 'order',
579
+ type: 'tuple'
580
+ }, {
581
+ internalType: 'bytes32',
582
+ name: 'orderHash',
583
+ type: 'bytes32'
584
+ }],
585
+ name: 'OrderWithHash',
586
+ outputs: [],
587
+ stateMutability: 'pure',
588
+ type: 'function'
589
+ }];
590
+
591
+ const eip191prefix = ethers.ethers.toUtf8Bytes('\x19Ethereum Signed Message:\n');
592
+ const messageToBytes = message => {
593
+ if (ethers.ethers.isBytesLike(message)) {
594
+ return ethers.ethers.getBytes(message);
595
+ }
596
+ return ethers.ethers.toUtf8Bytes(message);
597
+ };
598
+ const prefixEIP191Message = message => {
599
+ const messageBytes = messageToBytes(message);
600
+ if (messageIsExemptFromEIP191Prefix(messageBytes)) {
601
+ return messageBytes;
602
+ } else {
603
+ return ethers.ethers.getBytes(ethers.ethers.concat([eip191prefix, ethers.ethers.toUtf8Bytes(String(messageBytes.length)), messageBytes]));
604
+ }
605
+ };
606
+ const trimEIP191Prefix = prefixedMessage => {
607
+ // If the message is not prefixed, we return the message as is.
608
+ if (JSON.stringify(prefixedMessage.slice(0, eip191prefix.length)) !== JSON.stringify(eip191prefix)) {
609
+ return prefixedMessage;
610
+ }
611
+
612
+ // We have two parts to remove.
613
+ // First is the EIP-191 prefix.
614
+ const ethereumSignedMessagePartSlicedArray = prefixedMessage.slice(eip191prefix.length);
615
+
616
+ // Second is the digits added which represent length of the message without the prefix
617
+ // and we need to find the prefix that will match this.
618
+ // Here first we take the max prefix char length, and check if as a number it is bigger
619
+ // than the length of the message (since prefix is added to represent length of original message),
620
+ // if it is we remove 1 from char length, if not we keep the max prefix char length.
621
+ // As an example for the case where , if the message is 123456789, the expected prefix char is 9, with starting value 9123456789
622
+ // the char length of the total message with the prefix is 10, so the max prefix char length we start is 2 from [1,0], and as a number 10, it is longer
623
+ // than the length of the message after removing prefix (10 - 2 = 8), so we slice 1 char less, which is 9, and we get the correct prefix.
624
+ const maxPrefixCharLength = String(ethereumSignedMessagePartSlicedArray.length).length;
625
+ let prefixCharLenght;
626
+ let prefixAsNumber;
627
+ try {
628
+ prefixAsNumber = Number(ethers.ethers.toUtf8String(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength)));
629
+ } catch (_unused) {
630
+ prefixAsNumber = Number(ethers.ethers.hexlify(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength)));
631
+ }
632
+ if (prefixAsNumber > ethereumSignedMessagePartSlicedArray.length || !Number.isInteger(prefixAsNumber)) {
633
+ prefixCharLenght = maxPrefixCharLength - 1;
634
+ } else {
635
+ prefixCharLenght = maxPrefixCharLength;
636
+ }
637
+ const prefixRevertedMessage = ethereumSignedMessagePartSlicedArray.slice(prefixCharLenght);
638
+ return prefixRevertedMessage;
639
+ };
640
+ const isValidSignature = async (address, digest, sig, provider) => {
641
+ const reader = new core.commons.reader.OnChainReader(provider);
642
+ return reader.isValidSignature(address, digest, sig);
643
+ };
644
+
645
+ // Verify message signature
646
+ const isValidMessageSignature = async (address, message, signature, provider) => {
647
+ const prefixed = prefixEIP191Message(message);
648
+ const digest = utils.encodeMessageDigest(prefixed);
649
+ return isValidSignature(address, digest, signature, provider);
650
+ };
651
+
652
+ // Verify typedData signature
653
+ const isValidTypedDataSignature = (address, typedData, signature, provider) => {
654
+ return isValidSignature(address, utils.encodeTypedDataDigest(typedData), signature, provider);
655
+ };
656
+ const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:';
657
+ const isUnityPlugin = () => !!navigator.userAgent.match(/UnitySequence/i);
658
+
659
+ // /**
660
+ // * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
661
+ // *
662
+ // * @param {Status} of the wallet
663
+ // */
664
+ const isWalletUpToDate = status => {
665
+ return status.onChain.deployed && status.fullyMigrated;
666
+ };
667
+ class MemoryItemStore {
668
+ constructor() {
669
+ this.callbacks = [];
670
+ this.store = {};
671
+ }
672
+ getItem(key) {
673
+ return this.store[key] || null;
674
+ }
675
+ setItem(key, value) {
676
+ this.store[key] = value;
677
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
678
+ }
679
+ removeItem(key) {
680
+ delete this.store[key];
681
+ }
682
+ onItemChange(key, cb) {
683
+ this.callbacks.push({
684
+ key,
685
+ cb
686
+ });
687
+ return () => {
688
+ this.callbacks = this.callbacks.filter(c => c.cb !== cb);
689
+ };
690
+ }
691
+ }
692
+ class LocalStorage {
693
+ static isAvailable() {
694
+ return typeof window === 'object' && typeof window.localStorage === 'object';
695
+ }
696
+ constructor() {
697
+ this.callbacks = [];
698
+ if (!LocalStorage.isAvailable()) {
699
+ throw new Error('LocalStorage is not available');
700
+ }
701
+ window.addEventListener('storage', e => {
702
+ const {
703
+ key
704
+ } = e;
705
+ const cb = this.callbacks.filter(c => c.key === key);
706
+ cb.forEach(c => c.cb(this.getItem(key)));
707
+ });
708
+ }
709
+ getItem(key) {
710
+ return window.localStorage.getItem(key);
711
+ }
712
+ setItem(key, value) {
713
+ window.localStorage.setItem(key, value);
714
+
715
+ // Trigger callbacks
716
+ // NOTICE: the event is not triggered on the same window
717
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(value));
718
+ }
719
+ removeItem(key) {
720
+ window.localStorage.removeItem(key);
721
+
722
+ // Trigger callbacks
723
+ // NOTICE: the event is not triggered on the same window
724
+ this.callbacks.filter(c => c.key === key).forEach(c => c.cb(null));
725
+ }
726
+ onItemChange(key, cb) {
727
+ this.callbacks.push({
728
+ key,
729
+ cb
730
+ });
731
+ return () => {
732
+ this.callbacks = this.callbacks.filter(c => c.cb !== cb);
733
+ };
734
+ }
735
+ }
736
+ function useBestStore() {
737
+ if (LocalStorage.isAvailable()) {
738
+ return new LocalStorage();
739
+ }
740
+ return new MemoryItemStore();
741
+ }
742
+ async function resolveArrayProperties(object) {
743
+ if (Array.isArray(object)) {
744
+ // T must include array type
745
+ return Promise.all(object.map(o => ethers.ethers.resolveProperties(o)));
746
+ }
747
+ return ethers.ethers.resolveProperties(object);
748
+ }
749
+
750
+ const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession';
751
+ class BaseWalletTransport {
752
+ constructor(walletRequestHandler) {
753
+ var _this = this;
754
+ this.walletRequestHandler = void 0;
755
+ this._sessionId = void 0;
756
+ this._registered = void 0;
757
+ this._init = void 0;
758
+ this._initNonce = void 0;
759
+ this._initCallback = void 0;
760
+ // appOrigin identifies the dapp's origin which opened the app. A transport
761
+ // will auto-detect and set this value if it can. This is determined
762
+ // as the parent app/window which opened the wallet.
763
+ this.appOrigin = void 0;
764
+ this.handleMessage = async function (message) {
765
+ const request = message;
766
+
767
+ // ensure initial handshake is complete before accepting
768
+ // other kinds of messages.
769
+ if (_this._init !== InitState.OK) {
770
+ if (request.type === EventType.INIT) {
771
+ if (_this.isValidInitAck(message)) {
772
+ // successful init
773
+ if (_this._initCallback) _this._initCallback();
774
+ } else {
775
+ // failed init
776
+ if (_this._initCallback) _this._initCallback('invalid init');
777
+ return;
778
+ }
779
+ }
780
+ return;
781
+ }
782
+
783
+ // ensure signer is ready to handle requests
784
+ // if (this.walletRequestHandler.getSigner() === undefined) {
785
+ // await this.walletRequestHandler.signerReady()
786
+ // }
787
+
788
+ // handle request
789
+ switch (request.type) {
790
+ case EventType.OPEN:
791
+ {
792
+ if (_this._init !== InitState.OK) return;
793
+ const session = {
794
+ sessionId: request.data.sessionId,
795
+ intent: request.data.intent,
796
+ networkId: request.data.networkId
797
+ };
798
+ await _this.open(session);
799
+ return;
800
+ }
801
+ case EventType.CLOSE:
802
+ {
803
+ if (_this._init !== InitState.OK) return;
804
+ // noop. just here to capture the message so event emitters may be notified
805
+ return;
806
+ }
807
+ case EventType.MESSAGE:
808
+ {
809
+ const response = await _this.walletRequestHandler.sendMessageRequest(request);
810
+ _this.sendMessage(response);
811
+ if (response.data.error) ;
812
+ return;
813
+ }
814
+ default:
815
+ {
816
+ utils.logger.error(`unexpected payload type ${request.type}`);
817
+ }
818
+ }
819
+ };
820
+ // sendMessageRequest sends a ProviderMessageRequest to the wallet post-message transport
821
+ this.sendMessageRequest = async function (message) {
822
+ return _this.walletRequestHandler.sendMessageRequest(message);
823
+ };
824
+ this.open = async function ({
825
+ sessionId,
826
+ intent,
827
+ networkId
828
+ }) {
829
+ if (sessionId) {
830
+ _this._sessionId = utils.sanitizeNumberString(sessionId);
831
+ // persist transport session in localstorage for restoring after redirect/reload
832
+ _this.saveTransportSession({
833
+ sessionId,
834
+ intent,
835
+ networkId
836
+ });
837
+ }
838
+ _this.walletRequestHandler.setOpenIntent(intent);
839
+
840
+ // init handshake for certain transports, before we can open the communication.
841
+ //
842
+ // for example, with the window-transport, we have to exchange messages to determine the
843
+ // origin host of the dapp.
844
+ await _this.init();
845
+
846
+ // determine chainId from networkId (string or number)
847
+ let chainId = undefined;
848
+ try {
849
+ if (networkId) {
850
+ const network$1 = network.findSupportedNetwork(networkId);
851
+ if (network$1) {
852
+ chainId = network$1.chainId;
853
+ } else {
854
+ throw new Error(`unknown network ${networkId}`);
855
+ }
856
+ } else {
857
+ // if not provided, use defaultChainId
858
+ chainId = _this.walletRequestHandler.defaultChainId();
859
+ }
860
+ } catch (err) {
861
+ console.error(err);
862
+ }
863
+
864
+ // Prepare connect options from intent
865
+ if (intent && intent.type === 'connect' && intent.options) {
866
+ const connectOptions = intent.options;
867
+ const authorizeOptions = connectOptions; // overlapping types
868
+
869
+ // Sanity/integrity check the intent payload, and set authorization origin
870
+ // if its been determined as part of the init handshake from earlier.
871
+ if (_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
872
+ if (!isBrowserExtension()) {
873
+ if (authorizeOptions.origin !== _this.appOrigin) {
874
+ throw new Error('origin is invalid');
875
+ }
876
+ }
877
+ } else if (!_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
878
+ // ie. when we can't determine the origin in our transport, but dapp provides it to us.
879
+ // we just sanitize the origin host.
880
+ connectOptions.origin = utils.sanitizeHost(authorizeOptions.origin);
881
+ } else if (_this.appOrigin) {
882
+ // ie. when we auto-determine the origin such as in window-transport
883
+ connectOptions.origin = _this.appOrigin;
884
+ }
885
+ if (connectOptions.app) {
886
+ connectOptions.app = utils.sanitizeAlphanumeric(connectOptions.app);
887
+ }
888
+ if (connectOptions.networkId) {
889
+ networkId = connectOptions.networkId;
890
+ } else if (networkId) {
891
+ connectOptions.networkId = networkId;
892
+ }
893
+
894
+ // Set connect options on the walletRequestHandler as our primary
895
+ // wallet controller, and fall back to networkId if necessary
896
+ _this.walletRequestHandler.setConnectOptions(connectOptions);
897
+ } else {
898
+ _this.walletRequestHandler.setConnectOptions(undefined);
899
+ }
900
+
901
+ // ensure signer is ready
902
+ await _this.walletRequestHandler.getAccount();
903
+
904
+ // Notify open and proceed to prompt for connection if intended
905
+ if (!(await _this.walletRequestHandler.isSignedIn())) {
906
+ // open wallet without a specific connected chainId, as the user is not signed in
907
+ _this.notifyOpen({
908
+ sessionId: _this._sessionId
909
+ });
910
+ return true;
911
+ } else {
912
+ // prompt user with a connect request. the options will be used as previously set above.
913
+ // upon success, the walletRequestHandler will notify the dapp with the ConnectDetails.
914
+ // upon cancellation by user, the walletRequestHandler will throw an error
915
+
916
+ if (intent && intent.type === 'connect') {
917
+ // Failed to set default network on open
918
+ // Fail silently here so we can continue with connect flow and ask
919
+ // user to connect on a different network if necessary
920
+ if (!chainId || chainId <= 0) {
921
+ console.log('Failed to set default network on open');
922
+ }
923
+
924
+ // notify wallet is opened, without session details
925
+ _this.notifyOpen({
926
+ sessionId: _this._sessionId
927
+ });
928
+ try {
929
+ const connectDetails = await _this.walletRequestHandler.promptConnect(intent.options);
930
+ if (connectDetails.connected) {
931
+ _this.walletRequestHandler.notifyConnect(connectDetails);
932
+ }
933
+ } catch (err) {
934
+ utils.logger.warn('promptConnect not connected:', err);
935
+ } finally {
936
+ // auto-close by default, unless intent is to keep open
937
+ if (!intent.options || intent.options.keepWalletOpened !== true) {
938
+ _this.notifyClose();
939
+ }
940
+ }
941
+ } else {
942
+ // Using default network
943
+
944
+ // Failed to set default network on open -- quit + close
945
+ if (!chainId || chainId <= 0) {
946
+ _this.notifyOpen({
947
+ sessionId: _this._sessionId,
948
+ error: `failed to open wallet on network ${networkId}`
949
+ });
950
+ return false;
951
+ }
952
+
953
+ // user is already connected, notify session details.
954
+ // TODO: in future, keep list if 'connected' dapps / sessions in the session
955
+ // controller, and only sync with allowed apps
956
+ _this.notifyOpen({
957
+ sessionId: _this._sessionId,
958
+ chainId: `${chainId}`,
959
+ session: await _this.walletRequestHandler.walletSession(chainId)
960
+ });
961
+ }
962
+ }
963
+ return true;
964
+ };
965
+ this.saveTransportSession = session => {
966
+ useBestStore().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
967
+ };
968
+ this.getCachedTransportSession = async function () {
969
+ const session = useBestStore().getItem(TRANSPORT_SESSION_LS_KEY);
970
+ try {
971
+ return session ? JSON.parse(session) : null;
972
+ } catch (err) {
973
+ console.error(`unable to parse transport session: ${session}`);
974
+ return null;
975
+ }
976
+ };
977
+ this.walletRequestHandler = walletRequestHandler;
978
+ this._init = InitState.NIL;
979
+ this.walletRequestHandler.on('connect', connectDetails => {
980
+ if (!this.registered) return;
981
+ // means user has logged in and wallet is connected to the app
982
+ this.notifyConnect(connectDetails);
983
+ });
984
+ this.walletRequestHandler.on('disconnect', (error, origin) => {
985
+ if (!this.registered) return;
986
+ // means user has logged out the app / disconnected wallet from the app
987
+ this.notifyDisconnect(error, origin);
988
+ });
989
+ this.walletRequestHandler.on('accountsChanged', (accounts, origin) => {
990
+ if (!this.registered) return;
991
+ this.notifyAccountsChanged(accounts, origin);
992
+ });
993
+ this.walletRequestHandler.on('networks', networks => {
994
+ if (!this.registered) return;
995
+ this.notifyNetworks(networks);
996
+ if (!networks || networks.length === 0) {
997
+ this.notifyChainChanged('0x0');
998
+ } else {
999
+ this.notifyChainChanged(ethers.ethers.toQuantity(networks.find(network => network.isDefaultChain).chainId));
1000
+ }
1001
+ });
1002
+ this.walletRequestHandler.on('chainChanged', (chainIdHex, origin) => {
1003
+ this.notifyChainChanged(chainIdHex, origin);
1004
+ });
1005
+ this.walletRequestHandler.on('walletContext', walletContext => {
1006
+ if (!this.registered || !walletContext) return;
1007
+ this.notifyWalletContext(walletContext);
1008
+ });
1009
+ this.walletRequestHandler.on('close', error => {
1010
+ if (!this.registered) return;
1011
+ this.notifyClose(error);
1012
+ });
1013
+ }
1014
+ get registered() {
1015
+ return this._registered;
1016
+ }
1017
+ register() {
1018
+ throw new Error('abstract method');
1019
+ }
1020
+ unregister() {
1021
+ throw new Error('abstract method');
1022
+ }
1023
+ request(request) {
1024
+ throw new Error('abstract method');
1025
+ }
1026
+ sendMessage(message) {
1027
+ throw new Error('abstract method');
1028
+ }
1029
+ notifyOpen(openInfo) {
1030
+ const {
1031
+ chainId,
1032
+ sessionId,
1033
+ session,
1034
+ error
1035
+ } = openInfo;
1036
+ this.sendMessage({
1037
+ idx: -1,
1038
+ type: EventType.OPEN,
1039
+ data: {
1040
+ chainId,
1041
+ sessionId,
1042
+ session,
1043
+ error
1044
+ },
1045
+ clientVersion: core.VERSION
1046
+ });
1047
+ }
1048
+ notifyClose(error) {
1049
+ this.sendMessage({
1050
+ idx: -1,
1051
+ type: EventType.CLOSE,
1052
+ data: error ? {
1053
+ error
1054
+ } : null,
1055
+ clientVersion: core.VERSION
1056
+ });
1057
+ }
1058
+ notifyConnect(connectDetails) {
1059
+ this.sendMessage({
1060
+ idx: -1,
1061
+ type: EventType.CONNECT,
1062
+ data: connectDetails,
1063
+ clientVersion: core.VERSION
1064
+ });
1065
+ }
1066
+ notifyDisconnect(error, origin) {
1067
+ this.sendMessage({
1068
+ idx: -1,
1069
+ type: EventType.DISCONNECT,
1070
+ data: error ? {
1071
+ error
1072
+ } : null,
1073
+ origin,
1074
+ clientVersion: core.VERSION
1075
+ });
1076
+ }
1077
+ notifyAccountsChanged(accounts, origin) {
1078
+ this.sendMessage({
1079
+ idx: -1,
1080
+ type: EventType.ACCOUNTS_CHANGED,
1081
+ data: accounts,
1082
+ origin,
1083
+ clientVersion: core.VERSION
1084
+ });
1085
+ }
1086
+ notifyChainChanged(chainIdHex, origin) {
1087
+ this.sendMessage({
1088
+ idx: -1,
1089
+ type: EventType.CHAIN_CHANGED,
1090
+ data: chainIdHex,
1091
+ origin,
1092
+ clientVersion: core.VERSION
1093
+ });
1094
+ }
1095
+ notifyNetworks(networks) {
1096
+ this.sendMessage({
1097
+ idx: -1,
1098
+ type: EventType.NETWORKS,
1099
+ data: networks,
1100
+ clientVersion: core.VERSION
1101
+ });
1102
+ }
1103
+ notifyWalletContext(walletContext) {
1104
+ this.sendMessage({
1105
+ idx: -1,
1106
+ type: EventType.WALLET_CONTEXT,
1107
+ data: walletContext,
1108
+ clientVersion: core.VERSION
1109
+ });
1110
+ }
1111
+ isValidInitAck(message) {
1112
+ if (this._init === InitState.OK) {
1113
+ // we're already in init state, we shouldn't handle this message
1114
+ utils.logger.warn("isValidInitAck, already in init'd state, so inquiry is invalid.");
1115
+ return false;
1116
+ }
1117
+ if (message.type !== EventType.INIT) {
1118
+ utils.logger.warn('isValidInitAck, invalid message type, expecting init');
1119
+ return false;
1120
+ }
1121
+ const {
1122
+ sessionId,
1123
+ nonce
1124
+ } = message.data;
1125
+ if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) {
1126
+ utils.logger.error('invalid init ack');
1127
+ return false;
1128
+ }
1129
+ if (sessionId !== this._sessionId || nonce !== this._initNonce) {
1130
+ utils.logger.error('invalid init ack match');
1131
+ return false;
1132
+ }
1133
+
1134
+ // all checks pass, its true
1135
+ return true;
1136
+ }
1137
+ init() {
1138
+ return new Promise((resolve, reject) => {
1139
+ // avoid re-init`ing, or if there is a transport which doesn't require
1140
+ // it, then it may set this._init to OK in its constructor.
1141
+ if (this._init === InitState.OK) {
1142
+ resolve();
1143
+ return;
1144
+ }
1145
+ if (this._init !== InitState.NIL || this._initCallback) {
1146
+ reject('transport init is in progress');
1147
+ return;
1148
+ }
1149
+
1150
+ // start init timeout, if we don't receive confirmation
1151
+ // from provider within this amount of time, then we timeout
1152
+ const initTimeout = setTimeout(() => {
1153
+ utils.logger.warn('transport init timed out');
1154
+ if (this._initCallback) {
1155
+ this._initCallback('transport init timed out');
1156
+ }
1157
+ }, PROVIDER_OPEN_TIMEOUT / 2);
1158
+
1159
+ // setup callback as we receive the init message async in the handleMessage function
1160
+ this._initCallback = error => {
1161
+ this._initCallback = undefined; // reset
1162
+ clearTimeout(initTimeout);
1163
+ if (error) {
1164
+ reject(error);
1165
+ } else {
1166
+ this._init = InitState.OK;
1167
+ resolve();
1168
+ }
1169
+ };
1170
+
1171
+ // send init request with random nonce to the provider, where we expect
1172
+ // for the provider to echo it back to us as complete handshake
1173
+ this._initNonce = `${performance.now()}`;
1174
+ this.sendMessage({
1175
+ idx: -1,
1176
+ type: EventType.INIT,
1177
+ data: {
1178
+ nonce: this._initNonce
1179
+ },
1180
+ clientVersion: core.VERSION
1181
+ });
1182
+ this._init = InitState.SENT_NONCE;
1183
+
1184
+ // NOTE: the promise will resolve in the _initCallback method
1185
+ // which will be called from either handleMessage or the initTimeout
1186
+ });
1187
+ }
1188
+ }
1189
+
1190
+ class ProxyMessageChannel {
1191
+ constructor() {
1192
+ this.app = void 0;
1193
+ this.wallet = void 0;
1194
+ const port1 = new ProxyMessageChannelPort();
1195
+ const port2 = new ProxyMessageChannelPort();
1196
+ port1.conn = port2;
1197
+ port2.conn = port1;
1198
+ this.app = port1;
1199
+ this.wallet = port2;
1200
+ }
1201
+ }
1202
+ class ProxyMessageChannelPort {
1203
+ constructor() {
1204
+ this.conn = void 0;
1205
+ this.events = new eventemitter2.EventEmitter2();
1206
+ // handle messages which hit this port
1207
+ this.handleMessage = message => {
1208
+ throw new Error('ProxyMessageChannelPort is not registered');
1209
+ };
1210
+ // send messages to the connected port
1211
+ this.sendMessage = message => {
1212
+ this.conn.handleMessage(message);
1213
+
1214
+ // trigger events
1215
+ if (message.type === 'open') {
1216
+ this.events.emit('open', message);
1217
+ }
1218
+ if (message.type === 'close') {
1219
+ this.events.emit('close', message);
1220
+ }
1221
+ if (message.type === 'connect') {
1222
+ this.events.emit('connect', message);
1223
+ }
1224
+ if (message.type === 'disconnect') {
1225
+ this.events.emit('disconnect', message);
1226
+ }
1227
+ };
1228
+ }
1229
+ on(event, fn) {
1230
+ this.events.on(event, fn);
1231
+ }
1232
+ once(event, fn) {
1233
+ this.events.once(event, fn);
1234
+ }
1235
+ }
1236
+
1237
+ class ProxyMessageProvider extends BaseProviderTransport {
1238
+ constructor(port) {
1239
+ super();
1240
+ this.port = void 0;
1241
+ this.register = () => {
1242
+ this.port.handleMessage = message => {
1243
+ this.handleMessage(message);
1244
+ };
1245
+ this.on('open', (...args) => {
1246
+ this.port.events.emit('open', ...args);
1247
+ });
1248
+ this.on('close', (...args) => {
1249
+ this.port.events.emit('close', ...args);
1250
+ });
1251
+ this.on('connect', (...args) => {
1252
+ this.port.events.emit('connect', ...args);
1253
+ });
1254
+ this.on('disconnect', (...args) => {
1255
+ this.port.events.emit('disconnect', ...args);
1256
+ });
1257
+ this._registered = true;
1258
+ };
1259
+ this.unregister = () => {
1260
+ this._registered = false;
1261
+ this.closeWallet();
1262
+ this.events.removeAllListeners();
1263
+ // @ts-ignore
1264
+ this.port.handleMessage = undefined;
1265
+ };
1266
+ this.openWallet = (path, intent, networkId) => {
1267
+ if (this.state === OpenState.CLOSED) {
1268
+ this.state = OpenState.OPENING;
1269
+ const sessionId = `${performance.now()}`;
1270
+ this._sessionId = sessionId;
1271
+ this.sendMessage({
1272
+ idx: -1,
1273
+ type: EventType.OPEN,
1274
+ data: {
1275
+ path,
1276
+ intent,
1277
+ networkId,
1278
+ sessionId
1279
+ },
1280
+ clientVersion: core.VERSION
1281
+ });
1282
+ }
1283
+ };
1284
+ this.state = OpenState.CLOSED;
1285
+ this.port = port;
1286
+ if (!port) {
1287
+ throw new Error('port argument cannot be empty');
1288
+ }
1289
+
1290
+ // disable init handshake for proxy-transport, we set it to OK, to
1291
+ // consider it in completed state.
1292
+ this._init = InitState.OK;
1293
+ }
1294
+ closeWallet() {
1295
+ this.sendMessage({
1296
+ idx: -1,
1297
+ type: EventType.CLOSE,
1298
+ data: null,
1299
+ clientVersion: core.VERSION
1300
+ });
1301
+ this.close();
1302
+ }
1303
+ sendMessage(message) {
1304
+ if (!message.idx) {
1305
+ throw new Error('message idx is empty');
1306
+ }
1307
+ this.port.sendMessage(message);
1308
+ }
1309
+ }
1310
+
1311
+ class ProxyMessageHandler extends BaseWalletTransport {
1312
+ constructor(walletRequestHandler, port) {
1313
+ super(walletRequestHandler);
1314
+ this.port = void 0;
1315
+ this.port = port;
1316
+ this._init = InitState.OK;
1317
+ }
1318
+ register() {
1319
+ this.port.handleMessage = message => {
1320
+ this.handleMessage(message);
1321
+ };
1322
+ this._registered = true;
1323
+ }
1324
+
1325
+ // note: we can't decide whether to restore the session within register(), because session info is
1326
+ // received asyncronously via EventType.OPEN after register() is executed.
1327
+ // And in the case of a redirect/reload, EventType.OPEN is not sent at all,
1328
+ // because the wallet is already open.
1329
+ //
1330
+ // call this method from wallet redirect hander when a session restore is needed
1331
+ async restoreSession() {
1332
+ const cachedSession = await this.getCachedTransportSession();
1333
+ if (cachedSession) {
1334
+ this.open(cachedSession);
1335
+ }
1336
+ }
1337
+ unregister() {
1338
+ // @ts-ignore
1339
+ this.port.handleMessage = undefined;
1340
+ this._registered = false;
1341
+ }
1342
+ sendMessage(message) {
1343
+ this.port.sendMessage(message);
1344
+ }
1345
+ }
1346
+
1347
+ /**
1348
+ * This will redirect console logs from Sequence.js & the wallet to the Unreal console, for debugging purposes.
1349
+ */
1350
+ function overrideLogs(side) {
1351
+ var _window$ue;
1352
+ if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport && !window.logsOverriddenForUnreal) {
1353
+ var _window$ue2;
1354
+ const t = (_window$ue2 = window.ue) == null ? void 0 : _window$ue2.sequencewallettransport;
1355
+ console.log = (...args) => {
1356
+ t.logfromjs(`${side}: ${stringify(args)}`);
1357
+ };
1358
+ console.warn = (...args) => {
1359
+ t.warnfromjs(`${side}: ${stringify(args)}`);
1360
+ };
1361
+ console.error = (...args) => {
1362
+ t.errorfromjs(`${side}: ${stringify(args)}`);
1363
+ };
1364
+ window.logsOverriddenForUnreal = true;
1365
+ }
1366
+ }
1367
+ function stringify(things) {
1368
+ return things.map(a => typeof a === 'object' ? a instanceof Error ? a.message : JSON.stringify(a, utils.bigintReplacer) : String(a)).join(' ');
1369
+ }
1370
+
1371
+ let registeredUnrealMessageProvider;
1372
+
1373
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1374
+
1375
+ /**
1376
+ * Initialized on dApp side
1377
+ */
1378
+ class UnrealMessageProvider extends BaseProviderTransport {
1379
+ constructor(walletAppURL) {
1380
+ super();
1381
+ this.walletURL = void 0;
1382
+ this.register = () => {
1383
+ var _window$ue;
1384
+ overrideLogs('dapp');
1385
+ if (registeredUnrealMessageProvider) {
1386
+ // overriding the registered message provider
1387
+ registeredUnrealMessageProvider.unregister();
1388
+ registeredUnrealMessageProvider = this;
1389
+ }
1390
+
1391
+ // listen for incoming messages from wallet
1392
+ if ((_window$ue = window.ue) != null && _window$ue.sequencewallettransport) {
1393
+ window.ue.sequencewallettransport.onmessagefromwallet = this.onUnrealCallback;
1394
+ }
1395
+ registeredUnrealMessageProvider = this;
1396
+ this._registered = true;
1397
+ console.log('registering transport!');
1398
+ };
1399
+ this.unregister = () => {
1400
+ var _window$ue2;
1401
+ this._registered = false;
1402
+ this.closeWallet();
1403
+
1404
+ // disable message listener
1405
+ if (registeredUnrealMessageProvider === this) {
1406
+ registeredUnrealMessageProvider = undefined;
1407
+ }
1408
+ if (((_window$ue2 = window.ue) == null || (_window$ue2 = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2.onmessagefromwallet) === this.onUnrealCallback) {
1409
+ delete window.ue.sequencewallettransport.onmessagefromwallet;
1410
+ }
1411
+
1412
+ // clear event listeners
1413
+ this.events.removeAllListeners();
1414
+ };
1415
+ this.openWallet = (path, intent, networkId) => {
1416
+ if (this.isOpened()) {
1417
+ // TODO focus wallet
1418
+ console.log('wallet already open!');
1419
+ return;
1420
+ }
1421
+ console.log('opening wallet!');
1422
+ // Instantiate new walletURL for this call
1423
+ const walletURL = new URL(this.walletURL.href);
1424
+ const windowSessionParams = new WindowSessionParams();
1425
+ if (path) {
1426
+ walletURL.pathname = path.toLowerCase();
1427
+ }
1428
+
1429
+ // Set session, intent and network id on walletURL
1430
+ this._init = InitState.NIL;
1431
+ this._sessionId = `${performance.now()}`;
1432
+ windowSessionParams.set('sid', this._sessionId);
1433
+ if (intent) {
1434
+ // encode intent as base64 url-encoded param
1435
+ windowSessionParams.set('intent', utils.base64EncodeObject(intent));
1436
+ }
1437
+ if (networkId) {
1438
+ windowSessionParams.set('net', `${networkId}`);
1439
+ }
1440
+ // serialize params
1441
+ walletURL.search = windowSessionParams.toString();
1442
+ console.log('opening wallet to', walletURL.href);
1443
+ window.open(walletURL.href);
1444
+ };
1445
+ // onmessage, receives ProviderMessageResponse from the wallet unreal transport
1446
+ this.onUnrealCallback = message => {
1447
+ if (!message) {
1448
+ throw new Error('ProviderMessage object is empty');
1449
+ }
1450
+
1451
+ // handle message with base message provider
1452
+ this.handleMessage(message);
1453
+ };
1454
+ this.walletURL = new URL(walletAppURL);
1455
+ }
1456
+ closeWallet() {
1457
+ this.close();
1458
+ }
1459
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1460
+ sendMessage(message) {
1461
+ var _window$ue3;
1462
+ const postedMessage = typeof message !== 'string' ? JSON.stringify(message, utils.bigintReplacer) : message;
1463
+ console.log('Sending message to wallet:', postedMessage);
1464
+ (_window$ue3 = window.ue) == null || (_window$ue3 = _window$ue3.sequencewallettransport) == null || _window$ue3.sendmessagetowallet(postedMessage);
1465
+ }
1466
+ }
1467
+
1468
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
1469
+
1470
+ /**
1471
+ * Initialized on Wallet side
1472
+ */
1473
+ class UnrealMessageHandler extends BaseWalletTransport {
1474
+ constructor(walletRequestHandler) {
1475
+ super(walletRequestHandler);
1476
+ // onmessage is called when (the wallet) receives request messages from the dapp
1477
+ // over the unreal json-messaging transport
1478
+ this.onMessageFromUnreal = request => {
1479
+ // Wallet always expects json-rpc request messages from a dapp
1480
+
1481
+ utils.logger.debug('RECEIVED MESSAGE', request);
1482
+
1483
+ // Handle message via the base transport
1484
+ this.handleMessage(request);
1485
+ };
1486
+ this.getUnrealTransportSession = windowParams => {
1487
+ const params = new WindowSessionParams(windowParams);
1488
+ return {
1489
+ sessionId: params.get('sid'),
1490
+ networkId: params.get('net'),
1491
+ intent: utils.base64DecodeObject(params.get('intent'))
1492
+ };
1493
+ };
1494
+ this._init = InitState.NIL;
1495
+ }
1496
+ async register(windowHref) {
1497
+ var _window$ue;
1498
+ if (((_window$ue = window.ue) == null ? void 0 : _window$ue.sequencewallettransport) === undefined) {
1499
+ return;
1500
+ }
1501
+ overrideLogs('wallet');
1502
+
1503
+ // record open details (sessionId + default network) from the window url
1504
+ const {
1505
+ search: rawParams
1506
+ } = new URL(windowHref || window.location.href);
1507
+ let session = this.getUnrealTransportSession(rawParams);
1508
+
1509
+ // provider should always include sid when opening a new window
1510
+ const isNewWindowSession = !!session.sessionId;
1511
+
1512
+ // attempt to restore previous session in the case of a redirect or window reload
1513
+ if (!isNewWindowSession) {
1514
+ session = await this.getCachedTransportSession();
1515
+ }
1516
+ if (!session) {
1517
+ utils.logger.error('unreal session is undefined');
1518
+ return;
1519
+ }
1520
+
1521
+ // listen for window-transport requests
1522
+ window.ue.sequencewallettransport.onmessagefromsequencejs = this.onMessageFromUnreal;
1523
+ this._registered = true;
1524
+
1525
+ // send open event to the app which opened us
1526
+ this.open(session).then(opened => {
1527
+ if (!opened) {
1528
+ var _session;
1529
+ const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
1530
+ utils.logger.error(err);
1531
+ this.notifyClose({
1532
+ message: err
1533
+ });
1534
+ window.close();
1535
+ }
1536
+ }).catch(e => {
1537
+ var _session2;
1538
+ const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
1539
+ utils.logger.error(err);
1540
+ this.notifyClose({
1541
+ message: err
1542
+ });
1543
+ window.close();
1544
+ });
1545
+ }
1546
+ unregister() {
1547
+ var _window$ue2;
1548
+ if (((_window$ue2 = window.ue) == null || (_window$ue2 = _window$ue2.sequencewallettransport) == null ? void 0 : _window$ue2.onmessagefromsequencejs) === this.onMessageFromUnreal) {
1549
+ delete window.ue.sequencewallettransport.onmessagefromsequencejs;
1550
+ }
1551
+ this._registered = false;
1552
+ }
1553
+ // sendMessage sends message to the dapp window
1554
+ sendMessage(message) {
1555
+ var _window$ue3;
1556
+ if (message.type !== EventType.INIT && this._init !== InitState.OK) {
1557
+ utils.logger.error('impossible state, should not be calling postMessage until inited');
1558
+ return;
1559
+ }
1560
+ // prepare payload
1561
+ const payload = JSON.stringify(message, utils.bigintReplacer);
1562
+
1563
+ // post-message to app.
1564
+ (_window$ue3 = window.ue) == null || (_window$ue3 = _window$ue3.sequencewallettransport) == null || _window$ue3.sendmessagetosequencejs(payload);
1565
+ }
1566
+ }
1567
+
1568
+ const CHANNEL_ID = 'sequence-extension-message-handler';
1569
+ class ExtensionMessageHandler extends BaseWalletTransport {
1570
+ constructor(walletRequestHandler, runtime) {
1571
+ super(walletRequestHandler);
1572
+ this.runtime = runtime;
1573
+ this.port = void 0;
1574
+ this._init = InitState.OK;
1575
+ }
1576
+ register() {
1577
+ this._registered = true;
1578
+ this.port = this.runtime.connect({
1579
+ name: CHANNEL_ID
1580
+ });
1581
+ }
1582
+ sendMessage(message) {
1583
+ utils.logger.info('[ExtensionMessageHandler send]', message);
1584
+ this.port.postMessage(message);
1585
+ }
1586
+ }
1587
+
1588
+ class ExtensionMessageProvider extends BaseProviderTransport {
1589
+ constructor(runtime) {
1590
+ super();
1591
+ this.register = () => {
1592
+ this._registered = true;
1593
+ };
1594
+ runtime.onConnect.addListener(port => {
1595
+ if (port.name === CHANNEL_ID) {
1596
+ this._init = InitState.OK;
1597
+ port.onMessage.addListener(message => {
1598
+ this.handleMessage(message);
1599
+ });
1600
+ }
1601
+ });
1602
+ }
1603
+ sendMessage(message) {
1604
+ //noop
1605
+ }
1606
+ unregister() {
1607
+ //noop
1608
+ }
1609
+ openWallet(path, intent, networkId) {
1610
+ //noop
1611
+ }
1612
+ closeWallet() {
1613
+ //noop
1614
+ }
1615
+ }
1616
+
1617
+ // to be used on injected window.ethereum EIP1193 proxy
1618
+ class BaseInjectedTransport extends eventemitter2.EventEmitter2 {
1619
+ constructor(stream) {
1620
+ var _this;
1621
+ super();
1622
+ _this = this;
1623
+ this.stream = stream;
1624
+ this.responseCallbacks = new Map();
1625
+ this._messageIdx = 0;
1626
+ this.nextMessageIdx = () => ++this._messageIdx;
1627
+ this.handleMessage = message => {
1628
+ if (!message.type || !message.data) {
1629
+ return;
1630
+ }
1631
+ utils.logger.info('[received message]', message);
1632
+ const requestIdx = message.idx;
1633
+ const responseCallback = this.responseCallbacks.get(requestIdx);
1634
+ if (requestIdx) {
1635
+ this.responseCallbacks.delete(requestIdx);
1636
+ }
1637
+ switch (message.type) {
1638
+ case EventType.MESSAGE:
1639
+ if (responseCallback) {
1640
+ this.emit(EventType.MESSAGE, message);
1641
+ responseCallback(message.data.error, message);
1642
+ } else {
1643
+ // NOTE: this would occur if 'idx' isn't set, which should never happen
1644
+ // or when we register two handler, or duplicate messages with the same idx are sent,
1645
+ // all of which should be prevented prior to getting to this point
1646
+ throw new Error('impossible state');
1647
+ }
1648
+ break;
1649
+ case EventType.DISCONNECT:
1650
+ case EventType.ACCOUNTS_CHANGED:
1651
+ case EventType.CHAIN_CHANGED:
1652
+ this.emit(message.type, message.data);
1653
+ break;
1654
+ default:
1655
+ console.error('unknown message type', message);
1656
+ break;
1657
+ }
1658
+ };
1659
+ this.sendMessageRequest = async function (message) {
1660
+ return new Promise((resolve, reject) => {
1661
+ if (!message.idx || message.idx <= 0) {
1662
+ reject(new Error('message idx not set'));
1663
+ }
1664
+ const responseCallback = (error, response) => {
1665
+ if (error) {
1666
+ reject(error);
1667
+ } else if (response) {
1668
+ resolve(response);
1669
+ } else {
1670
+ throw new Error('no valid response to return');
1671
+ }
1672
+ };
1673
+ const {
1674
+ idx
1675
+ } = message;
1676
+ if (!_this.responseCallbacks.get(idx)) {
1677
+ _this.responseCallbacks.set(idx, responseCallback);
1678
+ } else {
1679
+ reject(new Error('duplicate message idx, should never happen'));
1680
+ }
1681
+ _this.sendMessage(message);
1682
+ });
1683
+ };
1684
+ this.stream.on('data', this.handleMessage);
1685
+ }
1686
+ sendMessage(message) {
1687
+ if (!this.stream.writable) {
1688
+ console.error('window post message stream is not writable');
1689
+ }
1690
+ this.stream.write(message);
1691
+ }
1692
+ }
1693
+
1694
+ // ..
1695
+ let registeredWindowMessageProvider;
1696
+ class WindowMessageProvider extends BaseProviderTransport {
1697
+ constructor(walletAppURL, projectAccessKey) {
1698
+ super(projectAccessKey);
1699
+ this.walletURL = void 0;
1700
+ this.walletWindow = void 0;
1701
+ this.register = () => {
1702
+ if (registeredWindowMessageProvider) {
1703
+ // overriding the registered message provider
1704
+ registeredWindowMessageProvider.unregister();
1705
+ registeredWindowMessageProvider = this;
1706
+ }
1707
+
1708
+ // listen for incoming messages from wallet
1709
+ window.addEventListener('message', this.onWindowEvent);
1710
+ registeredWindowMessageProvider = this;
1711
+
1712
+ // open heartbeat
1713
+ this.on('open', () => {
1714
+ // Heartbeat to track if window closed
1715
+ const popup = this.walletWindow;
1716
+ const interval = setInterval(() => {
1717
+ if (popup && popup.closed) {
1718
+ clearInterval(interval);
1719
+ this.close();
1720
+ }
1721
+ }, 500);
1722
+ });
1723
+
1724
+ // close clean up
1725
+ this.on('close', () => {
1726
+ if (this.walletWindow) {
1727
+ this.walletWindow.close();
1728
+ this.walletWindow = null;
1729
+ }
1730
+ });
1731
+ this._registered = true;
1732
+ };
1733
+ this.unregister = () => {
1734
+ this._registered = false;
1735
+ this.closeWallet();
1736
+
1737
+ // disable message listener
1738
+ if (registeredWindowMessageProvider === this) {
1739
+ registeredWindowMessageProvider = undefined;
1740
+ }
1741
+ window.removeEventListener('message', this.onWindowEvent);
1742
+
1743
+ // clear event listeners
1744
+ this.events.removeAllListeners();
1745
+ };
1746
+ this.openWallet = (path, intent, networkId) => {
1747
+ if (this.walletWindow && this.isOpened()) {
1748
+ // TODO: update the location of window to path
1749
+ this.walletWindow.focus();
1750
+ return;
1751
+ }
1752
+
1753
+ // Instantiate new walletURL for this call
1754
+ const walletURL = new URL(this.walletURL.href);
1755
+ const windowSessionParams = new WindowSessionParams();
1756
+ if (path && path !== '') {
1757
+ walletURL.pathname = path.toLowerCase();
1758
+ }
1759
+
1760
+ // Set session, intent and network id on walletURL
1761
+ this._init = InitState.NIL;
1762
+ this._sessionId = `${performance.now()}`;
1763
+ windowSessionParams.set('sid', this._sessionId);
1764
+ if (intent) {
1765
+ // for the window-transport, we eagerly/optimistically set the origin host
1766
+ // when connecting to the wallet, however, this will be verified and enforced
1767
+ // on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
1768
+ if (intent.type === 'connect') {
1769
+ if (!intent.options) intent.options = {
1770
+ app: window.location.origin
1771
+ };
1772
+
1773
+ // skip setting origin host if we're in an browser extension execution context
1774
+ // allow origin that is passed in
1775
+ if (!isBrowserExtension() && !isUnityPlugin() && intent.options) {
1776
+ intent.options.origin = window.location.origin;
1777
+ }
1778
+ }
1779
+ // encode intent as base64 url-encoded param
1780
+ windowSessionParams.set('intent', utils.base64EncodeObject(intent));
1781
+ }
1782
+ if (networkId) {
1783
+ windowSessionParams.set('net', `${networkId}`);
1784
+ }
1785
+
1786
+ // Open popup window on center of the app window
1787
+ let windowSize;
1788
+ let windowPos;
1789
+ if (isBrowserExtension()) {
1790
+ windowSize = [450, 750];
1791
+ windowPos = [Math.abs(window.screen.width / 2 - windowSize[0] / 2), Math.abs(window.screen.height / 2 - windowSize[1] / 2)];
1792
+ } else {
1793
+ windowSize = [450, 750];
1794
+ windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1795
+ }
1796
+ const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`;
1797
+
1798
+ // serialize params
1799
+ walletURL.search = windowSessionParams.toString();
1800
+ this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures);
1801
+
1802
+ // TODO: move this somewhere else
1803
+ // TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
1804
+
1805
+ // Popup blocking detection and notice
1806
+ // let warned = false
1807
+ // const warnPopupBlocked = () => {
1808
+ // if (warned) return
1809
+ // warned = true
1810
+ // // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
1811
+ // throw new Error('popup is blocked')
1812
+ // }
1813
+
1814
+ // const popupCheck = setTimeout(() => {
1815
+ // if (!popup || popup.closed || typeof popup.closed === 'undefined') {
1816
+ // // popup is definitely blocked if we reach here.
1817
+ // warnPopupBlocked()
1818
+ // }
1819
+ // }, 1000)
1820
+
1821
+ // const popupBlocked = popup === null || popup === undefined
1822
+ // if (popupBlocked) {
1823
+ // warnPopupBlocked()
1824
+ // return
1825
+ // }
1826
+ };
1827
+ // onmessage, receives ProviderMessageResponse from the wallet post-message transport
1828
+ this.onWindowEvent = event => {
1829
+ // Security check, ensure message is coming from wallet origin url
1830
+ if (event.origin !== this.walletURL.origin) {
1831
+ // Safetly can skip events not from the wallet
1832
+ return;
1833
+ }
1834
+ let message;
1835
+ try {
1836
+ message = JSON.parse(event.data, utils.bigintReviver);
1837
+ } catch (err) {
1838
+ // event is not a ProviderMessage JSON object, skip
1839
+ return;
1840
+ }
1841
+ if (!message) {
1842
+ throw new Error('ProviderMessage object is empty');
1843
+ }
1844
+
1845
+ // handle message with base message provider
1846
+ this.handleMessage(message);
1847
+ };
1848
+ this.walletURL = new URL(walletAppURL);
1849
+ }
1850
+ closeWallet() {
1851
+ var _this$walletWindow;
1852
+ this.close();
1853
+ (_this$walletWindow = this.walletWindow) == null || _this$walletWindow.close();
1854
+ }
1855
+ sendMessage(message) {
1856
+ if (!this.walletWindow) {
1857
+ utils.logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
1858
+ return;
1859
+ }
1860
+ const postedMessage = typeof message !== 'string' ? JSON.stringify(message, utils.bigintReplacer) : message;
1861
+ this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
1862
+ }
1863
+ }
1864
+
1865
+ class WindowMessageHandler extends BaseWalletTransport {
1866
+ constructor(walletRequestHandler) {
1867
+ var _this;
1868
+ super(walletRequestHandler);
1869
+ _this = this;
1870
+ this.parentWindow = void 0;
1871
+ this._isPopup = false;
1872
+ // onmessage is called when (the wallet) receives request messages from the dapp
1873
+ // over the window post-messaging transport
1874
+ this.onWindowEvent = async function (event) {
1875
+ var _request, _request$origin;
1876
+ if (!event.origin || event.origin === '') {
1877
+ // skip same-origin or when event.origin is empty/undefined
1878
+ return;
1879
+ }
1880
+ if (_this.appOrigin && event.origin !== _this.appOrigin) {
1881
+ // skip message as not from expected app origin
1882
+ return;
1883
+ }
1884
+
1885
+ // Wallet always expects json-rpc request messages from a dapp
1886
+ let request;
1887
+ try {
1888
+ request = JSON.parse(event.data, utils.bigintReviver);
1889
+ } catch (err) {
1890
+ // event is not a ProviderMessage JSON object, skip
1891
+ return;
1892
+ }
1893
+
1894
+ // Set the origin on the request
1895
+ (_request$origin = (_request = request).origin) != null ? _request$origin : _request.origin = event.origin;
1896
+ utils.logger.debug('RECEIVED MESSAGE', request);
1897
+
1898
+ // Record event origin for valid init ack
1899
+ if (_this._init !== InitState.OK && _this.isValidInitAck(request)) {
1900
+ _this.appOrigin = event.origin;
1901
+ }
1902
+ if (_this._init === InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
1903
+ // impossible state
1904
+ utils.logger.error('impossible state, init.OK and appOrigin required');
1905
+ return;
1906
+ }
1907
+
1908
+ // Handle message via the base transport
1909
+ _this.handleMessage(request);
1910
+ };
1911
+ this.getWindowTransportSession = windowParams => {
1912
+ const params = new WindowSessionParams(windowParams);
1913
+ return {
1914
+ sessionId: params.get('sid'),
1915
+ networkId: params.get('net'),
1916
+ intent: utils.base64DecodeObject(params.get('intent'))
1917
+ };
1918
+ };
1919
+ this._init = InitState.NIL;
1920
+ }
1921
+ async register(windowHref) {
1922
+ const isPopup = parent.window.opener !== null;
1923
+ this._isPopup = isPopup;
1924
+ if (isPopup !== true) {
1925
+ return;
1926
+ }
1927
+
1928
+ // record open details (sessionId + default network) from the window url
1929
+ const {
1930
+ search: rawParams
1931
+ } = new URL(windowHref || window.location.href);
1932
+ let session = this.getWindowTransportSession(rawParams);
1933
+
1934
+ // provider should always include sid when opening a new window
1935
+ const isNewWindowSession = !!session.sessionId;
1936
+
1937
+ // attempt to restore previous session in the case of a redirect or window reload
1938
+ if (!isNewWindowSession) {
1939
+ session = await this.getCachedTransportSession();
1940
+ }
1941
+ if (!session) {
1942
+ utils.logger.error('window session is undefined');
1943
+ return;
1944
+ }
1945
+
1946
+ // record parent window instance for communication
1947
+ this.parentWindow = parent.window.opener;
1948
+
1949
+ // listen for window-transport requests
1950
+ window.addEventListener('message', this.onWindowEvent, false);
1951
+ this._registered = true;
1952
+
1953
+ // send open event to the app which opened us
1954
+ this.open(session).then(opened => {
1955
+ if (!opened) {
1956
+ var _session;
1957
+ const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
1958
+ utils.logger.error(err);
1959
+ this.notifyClose({
1960
+ message: err
1961
+ });
1962
+ window.close();
1963
+ }
1964
+ }).catch(e => {
1965
+ var _session2;
1966
+ const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
1967
+ utils.logger.error(err);
1968
+ this.notifyClose({
1969
+ message: err
1970
+ });
1971
+ window.close();
1972
+ });
1973
+ }
1974
+ unregister() {
1975
+ window.removeEventListener('message', this.onWindowEvent);
1976
+ this._registered = false;
1977
+ }
1978
+ // postMessage sends message to the dapp window
1979
+ sendMessage(message) {
1980
+ // prepare payload
1981
+ const payload = JSON.stringify(message, utils.bigintReplacer);
1982
+
1983
+ // post-message to app.
1984
+ // only for init requests, we send to '*' origin
1985
+ if (message.type === EventType.INIT) {
1986
+ this.postMessage(payload, true);
1987
+ } else {
1988
+ this.postMessage(payload);
1989
+ }
1990
+ }
1991
+ get isPopup() {
1992
+ return this._isPopup;
1993
+ }
1994
+ postMessage(message, init = false) {
1995
+ if (init !== true && this._init !== InitState.OK) {
1996
+ utils.logger.error('impossible state, should not be calling postMessage until inited');
1997
+ return;
1998
+ }
1999
+ if (init) {
2000
+ // init message transmission to global target -- for 'init' payloads only
2001
+ this.parentWindow.postMessage(message, '*');
2002
+ } else {
2003
+ // open message transmission
2004
+ if (this.appOrigin && this.appOrigin.length > 4) {
2005
+ // just above '.com'
2006
+ this.parentWindow.postMessage(message, this.appOrigin);
2007
+ } else {
2008
+ utils.logger.error('unable to postMessage as parentOrigin is invalid');
2009
+ }
2010
+ }
2011
+ }
2012
+ }
2013
+
2014
+ function isMuxTransportTemplate(obj) {
2015
+ return obj && typeof obj === 'object' && (obj.windowTransport && typeof obj.windowTransport === 'object' || obj.proxyTransport && typeof obj.proxyTransport === 'object' || obj.extensionTransport && typeof obj.extensionTransport === 'object' || obj.unrealTransport && typeof obj.unrealTransport === 'object') && (
2016
+ // One of the transports must be enabled
2017
+ obj.windowTransport && obj.windowTransport.enabled || obj.proxyTransport && obj.proxyTransport.enabled || obj.extensionTransport && obj.extensionTransport.enabled || obj.unrealTransport && obj.unrealTransport.enabled);
2018
+ }
2019
+ class MuxMessageProvider {
2020
+ constructor(messageProviders = []) {
2021
+ var _this = this;
2022
+ this.messageProviders = void 0;
2023
+ this.provider = void 0;
2024
+ this.register = () => {
2025
+ if (this.messageProviders.length === 1) {
2026
+ this.provider = this.messageProviders[0];
2027
+ this.provider.register();
2028
+ return;
2029
+ }
2030
+
2031
+ // REVIEW/NOTE: ........ this method does not work for the chrome-extension. The issue becomes
2032
+ // when the browser quits or restarts, the "open" event is never triggered. Perhaps the code here is fine,
2033
+ // or maybe its not. What should happen is when a dapp makes a request, it will call openWallet
2034
+ // below, in which case one of the events will register. So perhaps this is fine.
2035
+ this.messageProviders.forEach(m => {
2036
+ m.register();
2037
+ m.once('open', () => {
2038
+ // the first one to open is the winner, and others will be unregistered
2039
+ if (!this.provider) {
2040
+ this.provider = m;
2041
+
2042
+ // unregister other providers
2043
+ this.messageProviders.forEach(m => {
2044
+ if (this.provider !== m) {
2045
+ m.unregister();
2046
+ }
2047
+ });
2048
+ }
2049
+ });
2050
+ });
2051
+ };
2052
+ this.unregister = () => {
2053
+ this.messageProviders.forEach(m => m.unregister());
2054
+ this.provider = undefined;
2055
+ };
2056
+ this.openWallet = (path, intent, networkId) => {
2057
+ if (this.provider) {
2058
+ this.provider.openWallet(path, intent, networkId);
2059
+ return;
2060
+ }
2061
+ this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
2062
+ };
2063
+ this.sendMessageRequest = async function (message) {
2064
+ if (_this.provider) {
2065
+ return _this.provider.sendMessageRequest(message);
2066
+ }
2067
+ throw new Error('impossible state, wallet must be opened first');
2068
+ };
2069
+ this.waitUntilOpened = async function () {
2070
+ if (_this.provider) {
2071
+ return _this.provider.waitUntilOpened();
2072
+ }
2073
+ return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
2074
+ };
2075
+ this.waitUntilConnected = async function () {
2076
+ if (_this.provider) {
2077
+ return _this.provider.waitUntilConnected();
2078
+ }
2079
+ throw new Error('impossible state, wallet must be opened first');
2080
+ };
2081
+ this.messageProviders = messageProviders;
2082
+ this.provider = undefined;
2083
+ }
2084
+ static new(template, projectAccessKey) {
2085
+ var _template$windowTrans, _template$proxyTransp, _template$extensionTr, _template$unrealTrans;
2086
+ const muxMessageProvider = new MuxMessageProvider();
2087
+ if ((_template$windowTrans = template.windowTransport) != null && _template$windowTrans.enabled && typeof window === 'object' && template.walletAppURL) {
2088
+ const windowMessageProvider = new WindowMessageProvider(template.walletAppURL, projectAccessKey);
2089
+ muxMessageProvider.add(windowMessageProvider);
2090
+ }
2091
+ if ((_template$proxyTransp = template.proxyTransport) != null && _template$proxyTransp.enabled) {
2092
+ const proxyMessageProvider = new ProxyMessageProvider(template.proxyTransport.appPort);
2093
+ muxMessageProvider.add(proxyMessageProvider);
2094
+ }
2095
+ if ((_template$extensionTr = template.extensionTransport) != null && _template$extensionTr.enabled) {
2096
+ const extensionMessageProvider = new ExtensionMessageProvider(template.extensionTransport.runtime);
2097
+ muxMessageProvider.add(extensionMessageProvider);
2098
+
2099
+ // NOTE/REVIEW: see note in mux-message-provider
2100
+ //
2101
+ // We don't add the extensionMessageProvider here because we don't send requests to it anyways, we seem to
2102
+ // send all requests to the WindowMessageProvider anyways. By allowing it, if browser restarts, it will break
2103
+ // the entire extension because messageProvider.provider will be undefined. So this is a hack to fix it.
2104
+ }
2105
+ if ((_template$unrealTrans = template.unrealTransport) != null && _template$unrealTrans.enabled && template.windowTransport && template.walletAppURL) {
2106
+ const unrealMessageProvider = new UnrealMessageProvider(template.walletAppURL);
2107
+ muxMessageProvider.add(unrealMessageProvider);
2108
+ }
2109
+ muxMessageProvider.register();
2110
+ return muxMessageProvider;
2111
+ }
2112
+ add(...messageProviders) {
2113
+ this.messageProviders.push(...messageProviders);
2114
+ }
2115
+ closeWallet() {
2116
+ if (this.provider) {
2117
+ this.provider.closeWallet();
2118
+ }
2119
+ }
2120
+ isOpened() {
2121
+ if (this.provider) {
2122
+ return this.provider.isOpened();
2123
+ }
2124
+ return false;
2125
+ }
2126
+ isConnected() {
2127
+ if (this.provider) {
2128
+ return this.provider.isConnected();
2129
+ }
2130
+ return false;
2131
+ }
2132
+ on(event, fn) {
2133
+ if (this.provider) {
2134
+ this.provider.on(event, fn);
2135
+ return;
2136
+ }
2137
+ this.messageProviders.forEach(m => {
2138
+ m.on(event, fn);
2139
+ });
2140
+ }
2141
+ once(event, fn) {
2142
+ if (this.provider) {
2143
+ this.provider.once(event, fn);
2144
+ return;
2145
+ }
2146
+ this.messageProviders.forEach(m => {
2147
+ m.once(event, fn);
2148
+ });
2149
+ }
2150
+ emit(event, ...args) {
2151
+ if (this.provider) {
2152
+ return this.provider.emit(event, ...args);
2153
+ }
2154
+ for (let i = 0; i < this.messageProviders.length; i++) {
2155
+ this.messageProviders[i].emit(event, ...args);
2156
+ }
2157
+ return true;
2158
+ }
2159
+ request(request) {
2160
+ if (!this.provider) {
2161
+ throw new Error('impossible state, wallet must be opened first');
2162
+ }
2163
+ return this.provider.request(request);
2164
+ }
2165
+ sendMessage(message) {
2166
+ if (!message.idx || message.idx <= 0) {
2167
+ throw new Error('message idx is empty');
2168
+ }
2169
+ if (this.provider) {
2170
+ this.provider.sendMessage(message);
2171
+ } else {
2172
+ throw new Error('impossible state, wallet must be opened first');
2173
+ }
2174
+ }
2175
+ handleMessage(message) {
2176
+ if (this.provider) {
2177
+ this.provider.handleMessage(message);
2178
+ return;
2179
+ }
2180
+ throw new Error('impossible state, wallet must be opened first');
2181
+ }
2182
+ }
2183
+
2184
+ function toExtended(transactions) {
2185
+ if (transactions.length === 0) {
2186
+ throw new Error('No transaction provided');
2187
+ }
2188
+ const [first, ...rest] = transactions;
2189
+ return _extends({}, first, {
2190
+ auxiliary: rest
2191
+ });
2192
+ }
2193
+ function fromExtended(transaction) {
2194
+ return [transaction, ...(transaction.auxiliary || [])];
2195
+ }
2196
+
2197
+ const SIGNER_READY_TIMEOUT = 10000;
2198
+ class WalletRequestHandler {
2199
+ constructor(account, prompter, networks) {
2200
+ var _this = this;
2201
+ // signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
2202
+ // value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
2203
+ // a Signer value is the actually interface to a signed-in account
2204
+ this.account = void 0;
2205
+ this.signerReadyCallbacks = [];
2206
+ this.prompter = void 0;
2207
+ this.networks = void 0;
2208
+ this._openIntent = void 0;
2209
+ this._connectOptions = void 0;
2210
+ this.events = new eventemitter2.EventEmitter2();
2211
+ this.onConnectOptionsChange = undefined;
2212
+ this.promptConnect = async function (options) {
2213
+ if (!options && !_this._connectOptions) {
2214
+ // this is an unexpected state and should not happen
2215
+ throw new Error('prompter connect options are empty');
2216
+ }
2217
+ if (!_this.prompter) {
2218
+ // if prompter is null, we'll auto connect
2219
+ return _this.connect(options);
2220
+ }
2221
+ const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
2222
+ return {
2223
+ connected: false
2224
+ };
2225
+ });
2226
+ const connectDetails = promptConnectDetails;
2227
+ if (connectDetails.connected && !connectDetails.session) {
2228
+ connectDetails.session = await _this.walletSession(options == null ? void 0 : options.networkId);
2229
+ }
2230
+ return promptConnectDetails;
2231
+ };
2232
+ this.isSignedIn = async function () {
2233
+ await _this.signerReady();
2234
+ return !!_this.account;
2235
+ };
2236
+ this.getAccount = async function () {
2237
+ await _this.signerReady();
2238
+ if (_this.account === undefined) {
2239
+ throw new Error('signerReady failed resolve');
2240
+ }
2241
+ return _this.account;
2242
+ };
2243
+ this.account = account;
2244
+ this.prompter = prompter;
2245
+ this.networks = networks;
2246
+ }
2247
+ defaultChainId() {
2248
+ var _this$prompter$getDef, _this$prompter;
2249
+ return (_this$prompter$getDef = (_this$prompter = this.prompter) == null ? void 0 : _this$prompter.getDefaultChainId()) != null ? _this$prompter$getDef : this.networks[0].chainId;
2250
+ }
2251
+ async signIn(account, options = {}) {
2252
+ this.setAccount(account);
2253
+ const {
2254
+ connect,
2255
+ defaultNetworkId
2256
+ } = options;
2257
+
2258
+ // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
2259
+ // necessary auth request, and then notify the dapp of the 'connect' details.
2260
+ //
2261
+ // NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
2262
+ // we don't trigger the promptConnect flow, as we consider the user just authenticated
2263
+ // for this dapp, so its safe to authorize in the promptSignInConnect() which will directly
2264
+ // connect after signing in.
2265
+ //
2266
+ // NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
2267
+ if (connect) {
2268
+ const connectOptions = this._connectOptions;
2269
+ let connectDetails;
2270
+ if (this.prompter !== null) {
2271
+ var _this$prompter2;
2272
+ connectDetails = await ((_this$prompter2 = this.prompter) == null ? void 0 : _this$prompter2.promptSignInConnect(connectOptions));
2273
+ } else {
2274
+ connectDetails = await this.connect(connectOptions);
2275
+ }
2276
+ this.notifyConnect(connectDetails);
2277
+ if (!connectOptions || connectOptions.keepWalletOpened !== true) {
2278
+ this.notifyClose();
2279
+ }
2280
+ }
2281
+ if (defaultNetworkId && this.defaultChainId() !== defaultNetworkId) {
2282
+ var _this$prompter3;
2283
+ await ((_this$prompter3 = this.prompter) == null ? void 0 : _this$prompter3.promptChangeNetwork(defaultNetworkId));
2284
+ }
2285
+ }
2286
+ signOut() {
2287
+ if (this.account) {
2288
+ this.notifyDisconnect();
2289
+ }
2290
+
2291
+ // signed out state
2292
+ this.setAccount(null);
2293
+ }
2294
+ signerReset() {
2295
+ // resetting signer puts the wallet in an uninitialized state, which requires the app to
2296
+ // re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
2297
+ this.account = undefined;
2298
+ }
2299
+ signerReady(timeout = SIGNER_READY_TIMEOUT) {
2300
+ return new Promise((resolve, reject) => {
2301
+ if (this.account !== undefined) {
2302
+ resolve();
2303
+ } else {
2304
+ setTimeout(() => {
2305
+ if (this.account === undefined) {
2306
+ this.signerReadyCallbacks = [];
2307
+ reject(`signerReady timed out`);
2308
+ }
2309
+ }, timeout);
2310
+ this.signerReadyCallbacks.push(resolve);
2311
+ }
2312
+ });
2313
+ }
2314
+ async connect(options) {
2315
+ var _ref, _options$networkId;
2316
+ if (!this.account) {
2317
+ return {
2318
+ connected: false,
2319
+ chainId: '0x0',
2320
+ error: 'unable to connect without signed in account'
2321
+ };
2322
+ }
2323
+ const networkId = (_ref = (_options$networkId = options == null ? void 0 : options.networkId) != null ? _options$networkId : this.defaultChainId()) != null ? _ref : network.ChainId.MAINNET;
2324
+ const chainId = network.findSupportedNetwork(networkId).chainId;
2325
+ const connectDetails = {
2326
+ connected: true,
2327
+ chainId: ethers.ethers.toQuantity(chainId)
2328
+ };
2329
+ if (options && options.authorize) {
2330
+ // Perform ethauth eip712 request and construct the ConnectDetails response
2331
+ // including the auth proof
2332
+ const authOptions = {
2333
+ app: options.app,
2334
+ origin: options.origin,
2335
+ expiry: options.expiry,
2336
+ nonce: options.authorizeNonce
2337
+ };
2338
+ // if (typeof(options.authorize) === 'object') {
2339
+ // authOptions = { ...authOptions, ...options.authorize }
2340
+ // }
2341
+
2342
+ try {
2343
+ // TODO: Either implement account as a signer, or change signAuthorization to accept an account
2344
+ connectDetails.proof = await auth.signAuthorization(this.account, chainId, authOptions);
2345
+ } catch (err) {
2346
+ utils.logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
2347
+ return {
2348
+ connected: false,
2349
+ chainId: '0x0',
2350
+ error: `signAuthorization failed: ${err.message}`
2351
+ };
2352
+ }
2353
+ }
2354
+
2355
+ // Build session response for connect details
2356
+ connectDetails.session = this.walletSession(chainId);
2357
+ return connectDetails;
2358
+ }
2359
+ // sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
2360
+ // (aka, the signer in this instance) and then responds with a wrapped response of
2361
+ // ProviderMessageResponse to be sent over the transport
2362
+ async sendMessageRequest(message) {
2363
+ var _message$clientVersio;
2364
+ // Older versions of the client require the response to be jsonrpc wrapped
2365
+ const majorVersion = Number(((_message$clientVersio = message.clientVersion) == null ? void 0 : _message$clientVersio.split('.')[0]) || '0');
2366
+ const isJsonRpcResponse = majorVersion < 2;
2367
+ const jsonRpcResponse = {
2368
+ id: message.data.id,
2369
+ jsonrpc: '2.0',
2370
+ result: null,
2371
+ error: undefined
2372
+ };
2373
+ try {
2374
+ const result = await this.request({
2375
+ method: message.data.method,
2376
+ params: message.data.params,
2377
+ chainId: message.chainId,
2378
+ origin: message.origin,
2379
+ projectAccessKey: message.projectAccessKey
2380
+ });
2381
+ return _extends({}, message, {
2382
+ data: isJsonRpcResponse ? _extends({}, jsonRpcResponse, {
2383
+ result
2384
+ }) : result
2385
+ });
2386
+ } catch (error) {
2387
+ return _extends({}, message, {
2388
+ data: isJsonRpcResponse ? _extends({}, jsonRpcResponse, {
2389
+ error
2390
+ }) : {
2391
+ error
2392
+ }
2393
+ });
2394
+ }
2395
+ }
2396
+ async request(request) {
2397
+ await this.getAccount();
2398
+ try {
2399
+ var _this$account, _request$chainId;
2400
+ // only allow public json rpc method to the provider when user is not logged in, aka signer is not set
2401
+ if ((!this.account || this.account === null) && !permittedJsonRpcMethods.includes(request.method)) {
2402
+ // throw new Error(`not logged in. ${request.method} is unavailable`)
2403
+ throw ErrSignedInRequired;
2404
+ }
2405
+
2406
+ // wallet account
2407
+ const account = this.account;
2408
+ if (!account) {
2409
+ throw new Error('WalletRequestHandler: wallet account is not configured');
2410
+ }
2411
+
2412
+ // fetch the provider for the specific chain, or undefined will select defaultChain
2413
+ const provider = (_this$account = this.account) == null ? void 0 : _this$account.providerFor((_request$chainId = request.chainId) != null ? _request$chainId : this.defaultChainId());
2414
+ if (!provider) {
2415
+ throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${request.chainId}`);
2416
+ }
2417
+
2418
+ // NOTE: we cannot use `instanceof ethers.JsonRpcProvider` because despite that we know
2419
+ // its the proper type, its not resolving properly at runtime during the pupeteer e2e tests.
2420
+ // Switching to the type guard function below is safer and more reliable.
2421
+ //
2422
+ // const jsonRpcProvider = provider instanceof ethers.JsonRpcProvider ? provider : undefined
2423
+ const jsonRpcProvider = isJsonRpcProvider(provider) ? provider : undefined;
2424
+ switch (request.method) {
2425
+ case 'net_version':
2426
+ {
2427
+ if (!jsonRpcProvider) {
2428
+ throw new Error(`Account provider doesn't support send method`);
2429
+ }
2430
+ return await jsonRpcProvider.send('net_version', []);
2431
+ }
2432
+ case 'eth_chainId':
2433
+ {
2434
+ if (!jsonRpcProvider) {
2435
+ throw new Error(`Account provider doesn't support send method`);
2436
+ }
2437
+ return await jsonRpcProvider.send('eth_chainId', []);
2438
+ }
2439
+ case 'eth_accounts':
2440
+ {
2441
+ return [account.address];
2442
+ }
2443
+ case 'eth_getBalance':
2444
+ {
2445
+ const [accountAddress, blockTag] = request.params;
2446
+ const walletBalance = await provider.getBalance(accountAddress, blockTag);
2447
+ return utils.toHexString(walletBalance);
2448
+ }
2449
+ case 'sequence_sign':
2450
+ case 'personal_sign':
2451
+ case 'eth_sign':
2452
+ {
2453
+ // note: message from json-rpc input is in hex format
2454
+ let message;
2455
+
2456
+ // there is a difference in the order of the params:
2457
+ // sequence_sign, personal_sign: [data, address]
2458
+ // eth_sign: [address, data]
2459
+ switch (request.method) {
2460
+ case 'sequence_sign':
2461
+ case 'personal_sign':
2462
+ {
2463
+ const [data, _address] = request.params;
2464
+ message = data;
2465
+ break;
2466
+ }
2467
+ case 'eth_sign':
2468
+ {
2469
+ const [_address, data] = request.params;
2470
+ message = data;
2471
+ break;
2472
+ }
2473
+ }
2474
+ let sig = '';
2475
+
2476
+ // Message must be prefixed with "\x19Ethereum Signed Message:\n"
2477
+ // as defined by EIP-191
2478
+ const prefixedMessage = prefixEIP191Message(message);
2479
+
2480
+ // TODO:
2481
+ // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
2482
+ const sequenceVerified = request.method === 'sequence_sign';
2483
+ if (this.prompter === null) {
2484
+ var _request$chainId2;
2485
+ // prompter is null, so we'll sign from here
2486
+ sig = await account.signMessage(prefixedMessage, (_request$chainId2 = request.chainId) != null ? _request$chainId2 : this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore');
2487
+ } else {
2488
+ sig = await this.prompter.promptSignMessage({
2489
+ chainId: request.chainId,
2490
+ message: ethers.ethers.getBytes(prefixedMessage),
2491
+ eip6492: sequenceVerified
2492
+ }, request.origin, request.projectAccessKey);
2493
+ }
2494
+ if (sig && sig.length > 0) {
2495
+ return sig;
2496
+ } else {
2497
+ // The user has declined the request when value is null
2498
+ throw new Error('declined by user');
2499
+ }
2500
+ }
2501
+ case 'sequence_signTypedData_v4':
2502
+ case 'eth_signTypedData':
2503
+ case 'eth_signTypedData_v4':
2504
+ {
2505
+ // note: signingAddress from json-rpc input is in hex format, and typedDataObject
2506
+ // should be an object, but in some instances may be double string encoded
2507
+ const [signingAddress, typedDataObject] = request.params;
2508
+ let typedData = undefined;
2509
+ if (typeof typedDataObject === 'string') {
2510
+ try {
2511
+ typedData = JSON.parse(typedDataObject);
2512
+ } catch (e) {
2513
+ console.warn('walletRequestHandler: error parsing typedData', e);
2514
+ }
2515
+ } else {
2516
+ typedData = typedDataObject;
2517
+ }
2518
+ if (!typedData || !typedData.domain || !typedData.types || !typedData.message) {
2519
+ throw new Error('invalid typedData object');
2520
+ }
2521
+ let sig = '';
2522
+ const sequenceVerified = request.method === 'sequence_signTypedData_v4';
2523
+ if (this.prompter === null) {
2524
+ var _request$chainId3;
2525
+ // prompter is null, so we'll sign from here
2526
+ sig = await account.signTypedData(typedData.domain, typedData.types, typedData.message, (_request$chainId3 = request.chainId) != null ? _request$chainId3 : this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore');
2527
+ } else {
2528
+ sig = await this.prompter.promptSignMessage({
2529
+ chainId: request.chainId,
2530
+ typedData: typedData,
2531
+ eip6492: sequenceVerified
2532
+ }, request.origin, request.projectAccessKey);
2533
+ }
2534
+ if (sig && sig.length > 0) {
2535
+ return sig;
2536
+ } else {
2537
+ // The user has declined the request when value is null
2538
+ throw new Error('declined by user');
2539
+ }
2540
+ }
2541
+ case 'eth_sendTransaction':
2542
+ {
2543
+ // https://eth.wiki/json-rpc/API#eth_sendtransaction
2544
+ const transactionParams = fromExtended(request.params[0]).map(tx => {
2545
+ // eth_sendTransaction uses 'gas'
2546
+ // ethers and sequence use 'gasLimit'
2547
+ if ('gas' in tx && tx.gasLimit === undefined) {
2548
+ tx.gasLimit = tx.gas;
2549
+ delete tx.gas;
2550
+ }
2551
+ return tx;
2552
+ });
2553
+ validateTransactionRequest(account.address, transactionParams);
2554
+ let txnHash = '';
2555
+ if (this.prompter === null) {
2556
+ var _request$chainId4, _txnResponse$hash;
2557
+ // prompter is null, so we'll send from here
2558
+ const txnResponse = await account.sendTransaction(transactionParams, (_request$chainId4 = request.chainId) != null ? _request$chainId4 : this.defaultChainId());
2559
+ txnHash = (_txnResponse$hash = txnResponse == null ? void 0 : txnResponse.hash) != null ? _txnResponse$hash : '';
2560
+ } else {
2561
+ // prompt user to provide the response
2562
+ txnHash = await this.prompter.promptSendTransaction(transactionParams, request.chainId, request.origin, request.projectAccessKey);
2563
+ }
2564
+ if (txnHash) {
2565
+ return txnHash;
2566
+ } else {
2567
+ // The user has declined the request when value is null
2568
+ throw new Error('declined by user');
2569
+ }
2570
+ }
2571
+ case 'eth_signTransaction':
2572
+ {
2573
+ // https://eth.wiki/json-rpc/API#eth_signTransaction
2574
+ const [transaction] = request.params;
2575
+ const sender = ethers.ethers.getAddress(transaction.from);
2576
+ if (sender !== account.address) {
2577
+ throw new Error('sender address does not match wallet');
2578
+ }
2579
+ validateTransactionRequest(account.address, transaction);
2580
+ if (this.prompter === null) {
2581
+ var _request$chainId5;
2582
+ // The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
2583
+ // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
2584
+ //
2585
+ // TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
2586
+ // we will want to resolveProperties the big number values to hex strings
2587
+ return await account.signTransactions(transaction, (_request$chainId5 = request.chainId) != null ? _request$chainId5 : this.defaultChainId());
2588
+ } else {
2589
+ return await this.prompter.promptSignTransaction(transaction, request.chainId, request.origin, request.projectAccessKey);
2590
+ }
2591
+ }
2592
+ case 'eth_sendRawTransaction':
2593
+ {
2594
+ // NOTE: we're not using a prompter here as the transaction is already signed
2595
+ // and would have prompted the user upon signing.
2596
+
2597
+ // https://eth.wiki/json-rpc/API#eth_sendRawTransaction
2598
+ if (core.commons.transaction.isSignedTransactionBundle(request.params[0])) {
2599
+ const txChainId = Number(request.params[0].chainId);
2600
+ const tx = await account.relayer(txChainId).relay(request.params[0]);
2601
+ return tx.hash;
2602
+ } else {
2603
+ const tx = await provider.broadcastTransaction(request.params[0]);
2604
+ return tx.hash;
2605
+ }
2606
+ }
2607
+ case 'eth_getTransactionCount':
2608
+ {
2609
+ const address = ethers.ethers.getAddress(request.params[0]);
2610
+ const tag = request.params[1];
2611
+
2612
+ // TODO: Maybe we should fetch this data from the relayer or from the reader
2613
+ // but for now we keep it simple and just use the provider
2614
+
2615
+ const count = await provider.getTransactionCount(address, tag);
2616
+ return utils.toHexString(BigInt(count));
2617
+ }
2618
+ case 'eth_blockNumber':
2619
+ {
2620
+ return await provider.getBlockNumber();
2621
+ }
2622
+ case 'eth_getBlockByNumber':
2623
+ {
2624
+ return await provider.getBlock(request.params[0] /* , jsonRpcRequest.params[1] */);
2625
+ }
2626
+ case 'eth_getBlockByHash':
2627
+ {
2628
+ return await provider.getBlock(request.params[0] /* , jsonRpcRequest.params[1] */);
2629
+ }
2630
+ case 'eth_getTransactionByHash':
2631
+ {
2632
+ return await provider.getTransaction(request.params[0]);
2633
+ }
2634
+ case 'eth_call':
2635
+ {
2636
+ const [transactionObject, blockTag] = request.params;
2637
+ return await provider.call(_extends({}, transactionObject, {
2638
+ blockTag
2639
+ }));
2640
+ }
2641
+ case 'eth_getCode':
2642
+ {
2643
+ const [contractAddress, blockTag] = request.params;
2644
+ return await provider.getCode(contractAddress, blockTag);
2645
+ }
2646
+ case 'eth_estimateGas':
2647
+ {
2648
+ const [transactionObject] = request.params;
2649
+ return await provider.estimateGas(transactionObject);
2650
+ }
2651
+ case 'eth_gasPrice':
2652
+ {
2653
+ const feeData = await provider.getFeeData();
2654
+ return feeData.gasPrice === null ? null : utils.toHexString(feeData.gasPrice);
2655
+ }
2656
+ case 'wallet_switchEthereumChain':
2657
+ {
2658
+ const [switchParams] = request.params;
2659
+ if (!switchParams.chainId || switchParams.chainId.length === 0) {
2660
+ throw new Error('invalid chainId');
2661
+ }
2662
+ const chainId = BigInt(switchParams.chainId);
2663
+ this.setDefaultChainId(Number(chainId));
2664
+ return null; // success
2665
+ }
2666
+
2667
+ // smart wallet method
2668
+ case 'sequence_getWalletContext':
2669
+ {
2670
+ return account.contexts;
2671
+ }
2672
+
2673
+ // smart wallet method
2674
+ case 'sequence_getWalletConfig':
2675
+ {
2676
+ const [chainId] = request.params;
2677
+ if (chainId) {
2678
+ return [(await account.status(chainId)).onChain.config];
2679
+ } else {
2680
+ return await Promise.all(account.networks.map(async function (network) {
2681
+ const status = await account.status(network.chainId);
2682
+ return status.onChain.config;
2683
+ }));
2684
+ }
2685
+ }
2686
+
2687
+ // smart wallet method
2688
+ case 'sequence_getWalletState':
2689
+ {
2690
+ const [chainId] = request.params;
2691
+ // TODO: Add getWalletState to the Signer interface
2692
+ if (chainId) {
2693
+ return [getLegacyWalletState(chainId, await account.status(chainId))];
2694
+ } else {
2695
+ return await Promise.all(account.networks.map(async function (network) {
2696
+ const status = await account.status(network.chainId);
2697
+ return getLegacyWalletState(network.chainId, status);
2698
+ }));
2699
+ }
2700
+ }
2701
+
2702
+ // smart wallet method
2703
+ case 'sequence_getNetworks':
2704
+ {
2705
+ // NOTE: must ensure that the response result below returns clean serialized data, which is to omit
2706
+ // the provider and relayer objects and only return the urls so can be reinstantiated on dapp side.
2707
+ // This is handled by this.getNetworks() but noted here for future readers.
2708
+ return await this.getNetworks(true);
2709
+ }
2710
+ case 'sequence_isSequence':
2711
+ {
2712
+ return true;
2713
+ }
2714
+
2715
+ // smart wallet method
2716
+ case 'sequence_updateConfig':
2717
+ {
2718
+ throw new Error('sequence_updateConfig method is not allowed from a dapp');
2719
+ // NOTE: method is disabled as we don't need a dapp to request to update a config.
2720
+ // However, if we ever want this, we can enable it but must also use the prompter
2721
+ // for confirmation.
2722
+ //
2723
+ // const [newConfig] = request.params
2724
+ // return await signer.updateConfig(newConfig)
2725
+ }
2726
+
2727
+ // smart wallet method
2728
+ case 'sequence_publishConfig':
2729
+ {
2730
+ throw new Error('sequence_publishConfig method is not allowed from a dapp');
2731
+ }
2732
+
2733
+ // relayer method
2734
+ case 'sequence_gasRefundOptions':
2735
+ {
2736
+ // TODO
2737
+ break;
2738
+ }
2739
+
2740
+ // relayer method
2741
+ case 'sequence_getNonce':
2742
+ {
2743
+ // TODO
2744
+ break;
2745
+ }
2746
+
2747
+ // relayer method
2748
+ case 'sequence_relay':
2749
+ {
2750
+ // TODO
2751
+ break;
2752
+ }
2753
+
2754
+ // set default network of wallet
2755
+ case 'sequence_setDefaultNetwork':
2756
+ {
2757
+ const [defaultChainId] = request.params;
2758
+ if (!defaultChainId) {
2759
+ throw new Error('invalid request, method argument defaultChainId cannot be empty');
2760
+ }
2761
+ this.setDefaultChainId(defaultChainId);
2762
+ return await this.getNetworks(true);
2763
+ }
2764
+ default:
2765
+ {
2766
+ if (!jsonRpcProvider) {
2767
+ throw new Error(`Account provider doesn't support send method`);
2768
+ }
2769
+
2770
+ // NOTE: provider here will be chain-bound if chainId is provided
2771
+ return await jsonRpcProvider.send(request.method, request.params);
2772
+ }
2773
+ }
2774
+ } catch (err) {
2775
+ utils.logger.error(err);
2776
+ throw {
2777
+ message: typeof err == 'string' ? err : (err == null ? void 0 : err.message) || 'unkown error',
2778
+ code: 4001
2779
+ };
2780
+ }
2781
+ }
2782
+ on(event, fn) {
2783
+ this.events.on(event, fn);
2784
+ }
2785
+ once(event, fn) {
2786
+ this.events.once(event, fn);
2787
+ }
2788
+ async getAddress() {
2789
+ var _this$account$address, _this$account2;
2790
+ return (_this$account$address = (_this$account2 = this.account) == null ? void 0 : _this$account2.address) != null ? _this$account$address : '';
2791
+ }
2792
+ get openIntent() {
2793
+ return this._openIntent;
2794
+ }
2795
+ setOpenIntent(intent) {
2796
+ this._openIntent = intent;
2797
+ }
2798
+ get connectOptions() {
2799
+ return this._connectOptions;
2800
+ }
2801
+ setConnectOptions(options) {
2802
+ var _this$onConnectOption;
2803
+ this._connectOptions = options;
2804
+ (_this$onConnectOption = this.onConnectOptionsChange) == null || _this$onConnectOption.call(this, options);
2805
+ }
2806
+ async setDefaultChainId(chainId) {
2807
+ var _this$prompter4;
2808
+ await ((_this$prompter4 = this.prompter) == null ? void 0 : _this$prompter4.promptChangeNetwork(chainId));
2809
+ return this.defaultChainId();
2810
+ }
2811
+ async getNetworks(jsonRpcResponse) {
2812
+ if (!this.account) {
2813
+ utils.logger.warn('signer not set: getNetworks is returning an empty list');
2814
+ return [];
2815
+ }
2816
+ if (jsonRpcResponse) {
2817
+ // omit provider and relayer objects as they are not serializable
2818
+ return this.account.networks.map(n => {
2819
+ const network = _extends({}, n);
2820
+ network.provider = undefined;
2821
+ network.relayer = undefined;
2822
+ return network;
2823
+ });
2824
+ } else {
2825
+ return this.account.networks;
2826
+ }
2827
+ }
2828
+ walletSession(networkId) {
2829
+ if (!this.account) {
2830
+ return undefined;
2831
+ }
2832
+ const session = {
2833
+ walletContext: this.account.contexts,
2834
+ accountAddress: this.account.address,
2835
+ // The dapp shouldn't access the relayer directly, and the provider (as an object) is not serializable.
2836
+ networks: this.account.networks.map(n => _extends({}, n, {
2837
+ provider: undefined,
2838
+ relayer: undefined
2839
+ }))
2840
+ };
2841
+ if (networkId) {
2842
+ const network$1 = network.findNetworkConfig(session.networks, networkId);
2843
+ if (network$1) {
2844
+ var _session$networks;
2845
+ // Delete the isDefaultChain property from the session network
2846
+ (_session$networks = session.networks) == null || _session$networks.forEach(n => delete n.isDefaultChain);
2847
+
2848
+ // Add the isDefaultChain property to the network with the given networkId
2849
+ network$1.isDefaultChain = true;
2850
+ }
2851
+ }
2852
+ return session;
2853
+ }
2854
+ notifyConnect(connectDetails, origin) {
2855
+ var _connectDetails$sessi;
2856
+ console.log('emit connect', connectDetails);
2857
+ this.events.emit('connect', connectDetails);
2858
+ if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
2859
+ var _connectDetails$sessi2;
2860
+ this.events.emit('accountsChanged', [(_connectDetails$sessi2 = connectDetails.session) == null ? void 0 : _connectDetails$sessi2.accountAddress], origin);
2861
+ }
2862
+ }
2863
+ notifyDisconnect(origin) {
2864
+ this.events.emit('accountsChanged', [], origin);
2865
+ this.events.emit('disconnect', undefined, origin);
2866
+ }
2867
+ notifyChainChanged(chainId, origin) {
2868
+ this.events.emit('chainChanged', ethers.ethers.toQuantity(chainId), origin);
2869
+ }
2870
+ async notifyNetworks(networks) {
2871
+ const n = networks || (await this.getNetworks(true));
2872
+ this.events.emit('networks', n);
2873
+ if (n.length > 0) {
2874
+ const defaultNetwork = n.find(network => network.chainId === this.defaultChainId());
2875
+ if (defaultNetwork) {
2876
+ this.events.emit('chainChanged', ethers.ethers.toQuantity(defaultNetwork.chainId));
2877
+ }
2878
+ } else {
2879
+ this.events.emit('chainChanged', '0x0');
2880
+ }
2881
+ }
2882
+ async notifyWalletContext() {
2883
+ if (!this.account) {
2884
+ utils.logger.warn('signer not set: skipping to notify wallet context');
2885
+ return;
2886
+ }
2887
+ const walletContext = this.account.contexts;
2888
+ this.events.emit('walletContext', walletContext);
2889
+ }
2890
+ notifyClose(error) {
2891
+ this.events.emit('close', error);
2892
+ }
2893
+ setAccount(account) {
2894
+ this.account = account;
2895
+ if (account !== undefined) {
2896
+ for (let i = 0; i < this.signerReadyCallbacks.length; i++) {
2897
+ this.signerReadyCallbacks[i]();
2898
+ }
2899
+ this.signerReadyCallbacks = [];
2900
+ }
2901
+ }
2902
+ async handleConfirmWalletDeployPrompt(prompter, account, sequenceVerified, chainId, origin) {
2903
+ // check if wallet is deployed and up to date, if not, prompt user to deploy
2904
+ // if no chainId is provided, we'll assume the wallet is auth chain wallet and is up to date
2905
+ if (!chainId) {
2906
+ return true;
2907
+ }
2908
+ const skipsDeploy = status => {
2909
+ return status.canOnchainValidate || status.original.version === 2 && sequenceVerified;
2910
+ };
2911
+ const status = await account.status(chainId);
2912
+ if (skipsDeploy(status)) {
2913
+ return true;
2914
+ }
2915
+ const promptResult = await prompter.promptConfirmWalletDeploy(chainId, origin);
2916
+
2917
+ // if client returned true, check again to make sure wallet is deployed and up to date
2918
+ if (promptResult) {
2919
+ const status2 = await account.status(chainId);
2920
+ if (skipsDeploy(status2)) {
2921
+ return true;
2922
+ } else {
2923
+ utils.logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct');
2924
+ return false;
2925
+ }
2926
+ }
2927
+ return false;
2928
+ }
2929
+ }
2930
+ function getLegacyWalletState(chainId, status) {
2931
+ return {
2932
+ context: status.original.context,
2933
+ config: status.onChain.config,
2934
+ address: core.commons.context.addressOf(status.original.context, status.original.imageHash),
2935
+ chainId,
2936
+ deployed: status.onChain.deployed,
2937
+ imageHash: status.imageHash,
2938
+ lastImageHash: status.onChain.imageHash,
2939
+ published: true,
2940
+ status
2941
+ };
2942
+ }
2943
+ 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'];
2944
+ function isJsonRpcProvider(provider) {
2945
+ return provider && typeof provider === 'object' && typeof provider.send === 'function' && typeof provider.getNetwork === 'function';
2946
+ }
2947
+
2948
+ function _objectWithoutPropertiesLoose(r, e) {
2949
+ if (null == r) return {};
2950
+ var t = {};
2951
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
2952
+ if (e.includes(n)) continue;
2953
+ t[n] = r[n];
2954
+ }
2955
+ return t;
2956
+ }
2957
+
2958
+ class SequenceSigner {
2959
+ get utils() {
2960
+ return this.provider.utils;
2961
+ }
2962
+ constructor(client, provider) {
2963
+ this.client = client;
2964
+ this.provider = provider;
2965
+ this.singleNetworkSigners = {};
2966
+ this._isSequenceSigner = true;
2967
+ }
2968
+ async getAddress() {
2969
+ return this.client.getAddress();
2970
+ }
2971
+
2972
+ // This method shouldn't be used directly
2973
+ // it exists to maintain compatibility with ethers.Signer
2974
+ connect(provider) {
2975
+ if (!SequenceProvider.is(provider)) {
2976
+ throw new Error('SequenceSigner can only be connected to a SequenceProvider');
2977
+ }
2978
+ return new SequenceSigner(this.client, provider);
2979
+ }
2980
+ getSigner(chainId) {
2981
+ // The signer for the default network is this signer
2982
+ if (!chainId) {
2983
+ return this;
2984
+ }
2985
+ const useChainId = this.provider.toChainId(chainId);
2986
+ if (!this.singleNetworkSigners[useChainId]) {
2987
+ this.singleNetworkSigners[useChainId] = new SingleNetworkSequenceSigner(this.client, this.provider, useChainId);
2988
+ }
2989
+ return this.singleNetworkSigners[useChainId];
2990
+ }
2991
+
2992
+ /**
2993
+ * Resolves the chainId to use for the given request. If no chainId is provided,
2994
+ * it uses the chainId defined by the client (default chainId). This can be
2995
+ * overriden to build a single-network SequenceProvider.
2996
+ */
2997
+ useChainId(chainId) {
2998
+ return this.provider.toChainId(chainId) || this.client.getChainId();
2999
+ }
3000
+ async signMessage(message, options) {
3001
+ const {
3002
+ eip6492 = true
3003
+ } = options || {};
3004
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
3005
+ return this.client.signMessage(message, {
3006
+ eip6492,
3007
+ chainId
3008
+ });
3009
+ }
3010
+ async signTypedData(domain, types, message, options) {
3011
+ const {
3012
+ eip6492 = true
3013
+ } = options || {};
3014
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
3015
+ return this.client.signTypedData({
3016
+ domain,
3017
+ types,
3018
+ message
3019
+ }, {
3020
+ eip6492,
3021
+ chainId
3022
+ });
3023
+ }
3024
+ getProvider(chainId) {
3025
+ return this.provider.getProvider(chainId);
3026
+ }
3027
+ async sendTransaction(transaction, options) {
3028
+ const chainId = this.useChainId(options == null ? void 0 : options.chainId);
3029
+ const resolved = await resolveArrayProperties(transaction);
3030
+ const txHash = await this.client.sendTransaction(resolved, {
3031
+ chainId
3032
+ });
3033
+ const provider = this.getProvider(chainId);
3034
+ try {
3035
+ const result = await new Promise(resolve => {
3036
+ const check = async function check() {
3037
+ const tx = await provider.getTransaction(txHash);
3038
+ if (tx !== null) {
3039
+ return resolve(tx);
3040
+ }
3041
+ await provider.once('block', check);
3042
+ };
3043
+ check();
3044
+ });
3045
+ return result;
3046
+ } catch (err) {
3047
+ err.transactionHash = txHash;
3048
+ throw err;
3049
+ }
3050
+ }
3051
+ async getWalletConfig(chainId) {
3052
+ const useChainId = this.useChainId(chainId);
3053
+ return this.client.getOnchainWalletConfig({
3054
+ chainId: useChainId
3055
+ });
3056
+ }
3057
+ getNetworks() {
3058
+ return this.client.getNetworks();
3059
+ }
3060
+ async getBalance(blockTag, optionals) {
3061
+ const provider = this.getProvider(optionals == null ? void 0 : optionals.chainId);
3062
+ return provider.getBalance(this.getAddress(), blockTag);
3063
+ }
3064
+ async estimateGas(transaction, optionals) {
3065
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).estimateGas(transaction);
3066
+ }
3067
+ async call(transaction, optionals) {
3068
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).call(transaction);
3069
+ }
3070
+ getChainId() {
3071
+ return Promise.resolve(this.client.getChainId());
3072
+ }
3073
+ async getFeeData(optionals) {
3074
+ return this.getProvider(optionals == null ? void 0 : optionals.chainId).getFeeData();
3075
+ }
3076
+ async resolveName(name) {
3077
+ const res = await this.provider.resolveName(name);
3078
+
3079
+ // For some reason ethers.Signer expects this to return `string`
3080
+ // but ethers.Provider expects this to return `string | null`.
3081
+ // The signer doesn't have any other source of information, so we'll
3082
+ // fail if the provider doesn't return a result.
3083
+ if (res === null) {
3084
+ throw new Error(`ENS name not found: ${name}`);
3085
+ }
3086
+ return res;
3087
+ }
3088
+ _checkProvider(_operation) {
3089
+ // We always have a provider, so this is a noop
3090
+ }
3091
+ getNonce(_blockTag) {
3092
+ throw new Error('SequenceSigner does not support getNonce');
3093
+ }
3094
+ populateCall(_transaction) {
3095
+ throw new Error('SequenceSigner does not support populateCall');
3096
+ }
3097
+ populateTransaction(_transaction) {
3098
+ throw new Error('SequenceSigner does not support populateTransaction');
3099
+ }
3100
+ checkTransaction(_transaction) {
3101
+ throw new Error('SequenceSigner does not support checkTransaction');
3102
+ }
3103
+ getTransactionCount(_blockTag) {
3104
+ // We could try returning the sequence nonce here
3105
+ // but we aren't sure how ethers will use this nonce
3106
+ throw new Error('SequenceSigner does not support getTransactionCount');
3107
+ }
3108
+ signTransaction(_transaction) {
3109
+ // We could implement signTransaction/sendTransaction here
3110
+ // but first we need a way of serializing these signed transactions
3111
+ // and it could lead to more trouble, because the dapp could try to send this transaction
3112
+ // using a different provider, which would fail.
3113
+ throw new Error('SequenceWallet does not support signTransaction, use sendTransaction instead.');
3114
+ }
3115
+ static is(cand) {
3116
+ return cand && typeof cand === 'object' && cand._isSequenceSigner === true;
3117
+ }
3118
+ }
3119
+
3120
+ /**
3121
+ * This is the same provider, but it only allows a single network at a time.
3122
+ * the network defined by the constructor is the only one that can be used.
3123
+ *
3124
+ * Attempting to call any method with a different network will throw an error.
3125
+ * Attempting to change the network of this provider will throw an error.
3126
+ *
3127
+ * NOTICE: These networks won't support ENS unless they are the mainnet.
3128
+ */
3129
+ class SingleNetworkSequenceSigner extends SequenceSigner {
3130
+ constructor(client, provider, chainId) {
3131
+ super(client, provider.getProvider(chainId));
3132
+ this.chainId = chainId;
3133
+ this._isSingleNetworkSequenceSigner = true;
3134
+ }
3135
+ _useChainId(chainId) {
3136
+ const provided = this.provider.toChainId(chainId);
3137
+ if (provided && provided !== this.chainId) {
3138
+ throw new Error(`This signer only supports the network ${this.chainId}, but ${provided} was requested.`);
3139
+ }
3140
+ return provided || this.provider.toChainId(this.chainId);
3141
+ }
3142
+ useChainId(chainId) {
3143
+ return this._useChainId(chainId);
3144
+ }
3145
+ getChainId() {
3146
+ return Promise.resolve(this.provider.toChainId(this.chainId));
3147
+ }
3148
+
3149
+ /**
3150
+ * Override getProvider and getSigner so they always use `useChainId`
3151
+ * this way they can't return providers and signers that can switch networks,
3152
+ * or that don't match the chainId of this signer.
3153
+ */
3154
+ getProvider(chainId) {
3155
+ return super.getProvider(this._useChainId(chainId));
3156
+ }
3157
+ getSigner(chainId) {
3158
+ if (this._useChainId(chainId) !== this.chainId) {
3159
+ throw new Error(`Unreachable code`);
3160
+ }
3161
+ return this;
3162
+ }
3163
+ static is(cand) {
3164
+ return cand && typeof cand === 'object' && cand._isSingleNetworkSequenceSigner === true;
3165
+ }
3166
+ }
3167
+
3168
+ /**
3169
+ * This class is redundant with the SequenceSigner class, but it is here for now to
3170
+ * maintain compatibility with the old wallet API. Eventually we should move these
3171
+ * methods to the SequenceSigner class and deprecate this class.
3172
+ */
3173
+ class WalletUtils {
3174
+ constructor(signer) {
3175
+ this.signer = signer;
3176
+ if (SingleNetworkSequenceSigner.is(signer)) {
3177
+ throw new Error('WalletUtils does not support SingleNetworkSequenceSigner');
3178
+ }
3179
+ }
3180
+
3181
+ // Sign message on a specified chain, or DefaultChain by default
3182
+ signMessage(message, chainId, eip6492) {
3183
+ return this.signer.signMessage(message, {
3184
+ chainId,
3185
+ eip6492
3186
+ });
3187
+ }
3188
+
3189
+ // Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
3190
+ signTypedData(domain, types, message, chainId, eip6492) {
3191
+ return this.signer.signTypedData(domain, types, message, {
3192
+ chainId,
3193
+ eip6492
3194
+ });
3195
+ }
3196
+
3197
+ // Verify signature of a digest, one of a message, typedData or other
3198
+ async isValidSignature(address, digest, signature, chainId) {
3199
+ return isValidSignature(address, digest, signature, this.signer.getProvider(chainId));
3200
+ }
3201
+
3202
+ // Verify message signature
3203
+ async isValidMessageSignature(address, message, signature, chainId) {
3204
+ const provider = this.signer.getProvider(chainId);
3205
+ const prefixed = prefixEIP191Message(message);
3206
+ const digest = utils.encodeMessageDigest(prefixed);
3207
+ return isValidSignature(address, digest, signature, provider);
3208
+ }
3209
+
3210
+ // Verify typedData signature
3211
+ isValidTypedDataSignature(address, typedData, signature, chainId) {
3212
+ return this.isValidSignature(address, utils.encodeTypedDataDigest(typedData), signature, chainId);
3213
+ }
3214
+
3215
+ // sendTransaction()
3216
+ // sendTransactions()
3217
+
3218
+ // sendETH()
3219
+ // sendToken()
3220
+ // sendCoin() -- sugar for sendToken()
3221
+ // sendCollectible() -- sugar for sendToken()
3222
+ // callContract()
3223
+
3224
+ // transactionHistory()
3225
+ // getReceipt()
3226
+ // getLogs()
3227
+ // // ..
3228
+
3229
+ // validateSignature()
3230
+ // recoverWalletConfig()
3231
+ // recoverAddress()
3232
+ }
3233
+
3234
+ const _excluded = ["method"];
3235
+ const EIP1193EventTypes = ['connect', 'disconnect', 'chainChanged', 'accountsChanged'];
3236
+ class SequenceProvider extends ethers.ethers.AbstractProvider {
3237
+ constructor(client, providerFor, networks = network.allNetworks, options) {
3238
+ // We support a lot of networks
3239
+ // but we start with the default one
3240
+ super(client.getChainId(), options);
3241
+
3242
+ // Emit events as defined by EIP-1193
3243
+ this.client = client;
3244
+ this.providerFor = providerFor;
3245
+ this.networks = networks;
3246
+ this.options = options;
3247
+ this.singleNetworkProviders = {};
3248
+ this._isSequenceProvider = true;
3249
+ this.utils = void 0;
3250
+ this.signer = void 0;
3251
+ this.eip1193EventListeners = new Map();
3252
+ client.onConnect(details => {
3253
+ var _this$eip1193EventLis;
3254
+ //this.emit('connect', details)
3255
+ (_this$eip1193EventLis = this.eip1193EventListeners.get('connect')) == null || _this$eip1193EventLis.forEach(listener => listener(details));
3256
+ });
3257
+ client.onDisconnect(error => {
3258
+ var _this$eip1193EventLis2;
3259
+ //this.emit('disconnect', error)
3260
+ (_this$eip1193EventLis2 = this.eip1193EventListeners.get('disconnect')) == null || _this$eip1193EventLis2.forEach(listener => listener(error));
3261
+ });
3262
+ client.onDefaultChainIdChanged(chainId => {
3263
+ var _this$eip1193EventLis3;
3264
+ //this.emit('chainChanged', chainId)
3265
+ (_this$eip1193EventLis3 = this.eip1193EventListeners.get('chainChanged')) == null || _this$eip1193EventLis3.forEach(listener => listener(chainId));
3266
+ });
3267
+ client.onAccountsChanged(accounts => {
3268
+ var _this$eip1193EventLis4;
3269
+ //this.emit('accountsChanged', accounts)
3270
+ (_this$eip1193EventLis4 = this.eip1193EventListeners.get('accountsChanged')) == null || _this$eip1193EventLis4.forEach(listener => listener(accounts));
3271
+ });
3272
+
3273
+ // NOTICE: We don't emit 'open' and 'close' events
3274
+ // because these are handled by the library, and they
3275
+ // are not part of EIP-1193
3276
+
3277
+ // devs can still access them using
3278
+ // client.onOpen()
3279
+ // client.onClose()
3280
+
3281
+ // Create a Sequence signer too
3282
+ this.signer = new SequenceSigner(this.client, this);
3283
+
3284
+ // Create a utils instance
3285
+ this.utils = new WalletUtils(this.signer);
3286
+ }
3287
+ async on(event, listener) {
3288
+ if (EIP1193EventTypes.includes(event)) {
3289
+ const listeners = this.eip1193EventListeners.get(event) || new Set();
3290
+ listeners.add(listener);
3291
+ this.eip1193EventListeners.set(event, listeners);
3292
+ return this;
3293
+ }
3294
+ return super.on(event, listener);
3295
+ }
3296
+ async off(event, listener) {
3297
+ if (EIP1193EventTypes.includes(event)) {
3298
+ const listeners = this.eip1193EventListeners.get(event);
3299
+ if (listeners) {
3300
+ if (listener) {
3301
+ listeners.delete(listener);
3302
+ } else {
3303
+ listeners.clear();
3304
+ }
3305
+ }
3306
+ return this;
3307
+ }
3308
+ return super.off(event, listener);
3309
+ }
3310
+ getSigner(chainId) {
3311
+ return this.signer.getSigner(chainId);
3312
+ }
3313
+ connect(options) {
3314
+ return this.client.connect(options);
3315
+ }
3316
+ disconnect() {
3317
+ return this.client.disconnect();
3318
+ }
3319
+ isConnected() {
3320
+ return this.client.isConnected();
3321
+ }
3322
+ getSession() {
3323
+ return this.client.getSession();
3324
+ }
3325
+ listAccounts() {
3326
+ return [this.client.getAddress()];
3327
+ }
3328
+
3329
+ // @deprecated use getSigner() instead
3330
+ getAddress() {
3331
+ return this.client.getAddress();
3332
+ }
3333
+ getNetworks() {
3334
+ return this.client.getNetworks();
3335
+ }
3336
+ getChainId() {
3337
+ return this.client.getChainId();
3338
+ }
3339
+ setDefaultChainId(chainId) {
3340
+ return this.client.setDefaultChainId(this.toChainId(chainId));
3341
+ }
3342
+ isOpened() {
3343
+ return this.client.isOpened();
3344
+ }
3345
+ closeWallet() {
3346
+ return this.client.closeWallet();
3347
+ }
3348
+ getWalletContext() {
3349
+ return this.client.getWalletContext();
3350
+ }
3351
+
3352
+ // @deprecated use getSigner() instead
3353
+ async getWalletConfig(chainId) {
3354
+ const useChainId = await this.useChainId(chainId);
3355
+ return this.client.getOnchainWalletConfig({
3356
+ chainId: useChainId
3357
+ });
3358
+ }
3359
+ authorize(options) {
3360
+ // Just an alias for connect with authorize: true
3361
+ return this.client.connect(_extends({}, options, {
3362
+ authorize: true
3363
+ }));
3364
+ }
3365
+ async openWallet(path, intent) {
3366
+ await this.client.openWallet(path, intent);
3367
+ return true;
3368
+ }
3369
+ toChainId(chainId) {
3370
+ if (chainId === undefined) {
3371
+ return undefined;
3372
+ }
3373
+ const resolved = network.findNetworkConfig(this.networks, chainId);
3374
+ if (!resolved) {
3375
+ throw new Error(`Unsupported network ${chainId}`);
3376
+ }
3377
+ return resolved.chainId;
3378
+ }
3379
+
3380
+ /**
3381
+ * Resolves the chainId to use for the given request. If no chainId is provided,
3382
+ * it uses the chainId defined by the client (default chainId). This can be
3383
+ * overriden to build a single-network SequenceProvider.
3384
+ */
3385
+ async useChainId(chainId) {
3386
+ return this.toChainId(chainId) || this.client.getChainId();
3387
+ }
3388
+
3389
+ /**
3390
+ * This generates a provider that ONLY works for the given chainId.
3391
+ * the generated provider can't switch networks, and can't handle requests
3392
+ * for other networks.
3393
+ */
3394
+
3395
+ getProvider(chainId) {
3396
+ // The provider without a chainId is... this one
3397
+ if (!chainId) {
3398
+ return this;
3399
+ }
3400
+ const useChainId = this.toChainId(chainId);
3401
+ if (!this.singleNetworkProviders[useChainId]) {
3402
+ this.singleNetworkProviders[useChainId] = new SingleNetworkSequenceProvider(this.client, this.providerFor, useChainId, this.options);
3403
+ }
3404
+ return this.singleNetworkProviders[useChainId];
3405
+ }
3406
+
3407
+ /**
3408
+ * This returns a subprovider, this is a regular non-sequence provider that
3409
+ * can be used to fulfill read only requests on a given network.
3410
+ */
3411
+ async _getSubprovider(chainId) {
3412
+ const useChainId = await this.useChainId(chainId);
3413
+
3414
+ // Whoever implements providerFrom should memoize the generated provider
3415
+ // otherwise every instance of SequenceProvider will create a new subprovider
3416
+ const provider = this.providerFor(useChainId);
3417
+ if (!provider) {
3418
+ throw new Error(`Unsupported network ${useChainId}`);
3419
+ }
3420
+ return provider;
3421
+ }
3422
+ async _perform(req) {
3423
+ var _provider$getRpcReque;
3424
+ const {
3425
+ method
3426
+ } = req,
3427
+ args = _objectWithoutPropertiesLoose(req, _excluded);
3428
+ const provider = await this._getSubprovider();
3429
+ const prepared = (_provider$getRpcReque = provider.getRpcRequest(req)) != null ? _provider$getRpcReque : {
3430
+ method,
3431
+ args: Object.values(args)
3432
+ };
3433
+ if (!prepared) {
3434
+ throw new Error(`Unsupported method ${req.method}`);
3435
+ }
3436
+ return provider.send(prepared.method, prepared.args);
3437
+ }
3438
+ async perform(method, params) {
3439
+ // First we check if the method should be handled by the client
3440
+ if (method === 'eth_chainId') {
3441
+ return ethers.ethers.toQuantity(await this.useChainId());
3442
+ }
3443
+ if (method === 'eth_accounts') {
3444
+ return [this.client.getAddress()];
3445
+ }
3446
+ if (method === 'wallet_switchEthereumChain') {
3447
+ const args = params[0];
3448
+ const chainId = normalizeChainId(args);
3449
+ return this.setDefaultChainId(chainId);
3450
+ }
3451
+
3452
+ // Usually these methods aren't used by calling the provider
3453
+ // but to maximize compatibility we support them too.
3454
+ // The correct way of accessing these methods is by using .getSigner()
3455
+ if (method === 'eth_sendTransaction' || method === 'eth_sign' || method === 'eth_signTypedData' || method === 'eth_signTypedData_v4' || method === 'personal_sign' ||
3456
+ // These methods will use EIP-6492
3457
+ // but this is handled directly by the wallet
3458
+ method === 'sequence_sign' || method === 'sequence_signTypedData_v4') {
3459
+ // We pass the chainId to the client, if we don't pass one
3460
+ // the client will use its own default chainId
3461
+ return this.client.request({
3462
+ method,
3463
+ params,
3464
+ chainId: this.getChainId()
3465
+ });
3466
+ }
3467
+ return this._perform(_extends({
3468
+ method
3469
+ }, params));
3470
+ }
3471
+ send(method, params) {
3472
+ return this.perform(method, params);
3473
+ }
3474
+ request(request) {
3475
+ return this.perform(request.method, request.params);
3476
+ }
3477
+ async _detectNetwork() {
3478
+ const chainId = this.client.getChainId();
3479
+ const found = network.findNetworkConfig(this.networks, chainId);
3480
+ if (!found) {
3481
+ throw new Error(`Unknown network ${chainId}`);
3482
+ }
3483
+ const network$1 = new ethers.ethers.Network(found.name, found.chainId);
3484
+ return network$1;
3485
+ }
3486
+ async detectNetwork() {
3487
+ return this._detectNetwork();
3488
+ }
3489
+
3490
+ // Override most of the methods, so we add support for an optional chainId
3491
+ // argument, which is used to select the provider to use.
3492
+ //
3493
+ // NOTICE: We could use generics to avoid repeating the same code
3494
+ // but this would make the code harder to read, and it's not worth it
3495
+ // since we only have a few methods to override.
3496
+
3497
+ async waitForTransaction(transactionHash, confirmations, timeout, optionals) {
3498
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3499
+ return provider.waitForTransaction(transactionHash, confirmations, timeout);
3500
+ }
3501
+ async getBlockNumber(optionals) {
3502
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3503
+ return provider.getBlockNumber();
3504
+ }
3505
+ async getFeeData(optionals) {
3506
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3507
+ return provider.getFeeData();
3508
+ }
3509
+ async getBalance(addressOrName, blockTag, optionals) {
3510
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3511
+ return provider.getBalance(addressOrName, blockTag);
3512
+ }
3513
+ async getTransactionCount(addressOrName, blockTag, optionals) {
3514
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3515
+ return provider.getTransactionCount(addressOrName, blockTag);
3516
+ }
3517
+ async getCode(addressOrName, blockTag, optionals) {
3518
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3519
+ return provider.getCode(addressOrName, blockTag);
3520
+ }
3521
+ async getStorage(addressOrName, position, blockTag, optionals) {
3522
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3523
+ return provider.getStorage(addressOrName, position, blockTag);
3524
+ }
3525
+ async call(transaction, optionals) {
3526
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3527
+ return provider.call(transaction);
3528
+ }
3529
+ async estimateGas(transaction, optionals) {
3530
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3531
+ return provider.estimateGas(transaction);
3532
+ }
3533
+ async getBlock(blockHashOrBlockTag, prefetchTxs, optionals) {
3534
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3535
+ return provider.getBlock(blockHashOrBlockTag, prefetchTxs);
3536
+ }
3537
+ async getTransaction(transactionHash, optionals) {
3538
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3539
+ return provider.getTransaction(transactionHash);
3540
+ }
3541
+ async getLogs(filter, optionals) {
3542
+ const provider = await this._getSubprovider(optionals == null ? void 0 : optionals.chainId);
3543
+ return provider.getLogs(filter);
3544
+ }
3545
+
3546
+ // ENS methods
3547
+
3548
+ async supportsENS() {
3549
+ const networks = await this.getNetworks();
3550
+ return networks.some(n => n.chainId === 1);
3551
+ }
3552
+ async getResolver(name) {
3553
+ if (!(await this.supportsENS())) {
3554
+ return null;
3555
+ }
3556
+
3557
+ // Resolver is always on the chainId 1
3558
+ const provider = await this._getSubprovider(1);
3559
+ return provider.getResolver(name);
3560
+ }
3561
+ async resolveName(name) {
3562
+ if (ethers.ethers.isAddress(name)) {
3563
+ return name;
3564
+ }
3565
+ if (!(await this.supportsENS())) {
3566
+ return null;
3567
+ }
3568
+
3569
+ // Resolver is always on the chainId 1
3570
+ const provider = await this._getSubprovider(1);
3571
+ return provider.resolveName(name);
3572
+ }
3573
+ async lookupAddress(address) {
3574
+ if (!(await this.supportsENS())) {
3575
+ return null;
3576
+ }
3577
+
3578
+ // Resolver is always on the chainId 1
3579
+ const provider = await this._getSubprovider(1);
3580
+ return provider.lookupAddress(address);
3581
+ }
3582
+ async getAvatar(nameOrAddress) {
3583
+ if (!(await this.supportsENS())) {
3584
+ return null;
3585
+ }
3586
+ const provider = await this._getSubprovider(1);
3587
+ return provider.getAvatar(nameOrAddress);
3588
+ }
3589
+ }
3590
+ SequenceProvider.is = provider => {
3591
+ return provider && typeof provider === 'object' && provider._isSequenceProvider === true;
3592
+ };
3593
+ function normalizeChainId(chainId) {
3594
+ if (typeof chainId === 'object') return normalizeChainId(chainId.chainId);
3595
+ return Number(chainId);
3596
+ }
3597
+
3598
+ /**
3599
+ * This is the same provider, but it only allows a single network at a time.
3600
+ * the network defined by the constructor is the only one that can be used.
3601
+ *
3602
+ * Attempting to call any method with a different network will throw an error.
3603
+ * Attempting to change the network of this provider will throw an error.
3604
+ *
3605
+ * NOTICE: These networks won't support ENS unless they are the mainnet.
3606
+ */
3607
+ class SingleNetworkSequenceProvider extends SequenceProvider {
3608
+ constructor(client, providerFor, chainId, options) {
3609
+ super(client, providerFor, undefined, options);
3610
+ this.chainId = chainId;
3611
+ this._isSingleNetworkSequenceProvider = true;
3612
+ }
3613
+ _useChainId(chainId) {
3614
+ const provided = this.toChainId(chainId);
3615
+ if (provided && provided !== this.chainId) {
3616
+ throw new Error(`This provider only supports the network ${this.chainId}, but ${provided} was requested.`);
3617
+ }
3618
+ return provided || super.toChainId(this.chainId);
3619
+ }
3620
+ useChainId(chainId) {
3621
+ return Promise.resolve(this._useChainId(chainId));
3622
+ }
3623
+ getChainId() {
3624
+ return super.toChainId(this.chainId);
3625
+ }
3626
+ async getNetwork() {
3627
+ const networks = await this.client.getNetworks();
3628
+ const found = network.findNetworkConfig(networks, this.chainId);
3629
+ if (!found) {
3630
+ throw new Error(`Unsupported network ${this.chainId}`);
3631
+ }
3632
+ return new ethers.ethers.Network(found.name, found.chainId);
3633
+ }
3634
+
3635
+ /**
3636
+ * Override getProvider and getSigner so they always use `useChainId`
3637
+ * this way they can't return providers and signers that can switch networks,
3638
+ * or that don't match the chainId of this signer.
3639
+ */
3640
+ getProvider(chainId) {
3641
+ if (this._useChainId(chainId) !== this.chainId) {
3642
+ throw new Error(`Unreachable code`);
3643
+ }
3644
+ return this;
3645
+ }
3646
+ getSigner(chainId) {
3647
+ return super.getSigner(this._useChainId(chainId));
3648
+ }
3649
+ setDefaultChainId(_chainId) {
3650
+ throw new Error(`This provider only supports the network ${this.chainId}; use the parent provider to switch networks.`);
3651
+ }
3652
+ static is(cand) {
3653
+ return cand && typeof cand === 'object' && cand._isSingleNetworkSequenceProvider === true;
3654
+ }
3655
+ }
3656
+
3657
+ let AnalyticsEventType = /*#__PURE__*/function (AnalyticsEventType) {
3658
+ AnalyticsEventType[AnalyticsEventType["INIT"] = 0] = "INIT";
3659
+ AnalyticsEventType[AnalyticsEventType["VIEW"] = 1] = "VIEW";
3660
+ AnalyticsEventType[AnalyticsEventType["SIGN_MESSAGE_REQUEST"] = 2] = "SIGN_MESSAGE_REQUEST";
3661
+ AnalyticsEventType[AnalyticsEventType["SIGN_TYPED_DATA_REQUEST"] = 3] = "SIGN_TYPED_DATA_REQUEST";
3662
+ AnalyticsEventType[AnalyticsEventType["SEND_TRANSACTION_REQUEST"] = 4] = "SEND_TRANSACTION_REQUEST";
3663
+ return AnalyticsEventType;
3664
+ }({});
3665
+ // Analytics sub-class to add some custom helper methods
3666
+ class Analytics extends tracker.Databeat {}
3667
+
3668
+ // Setup analytics tracker
3669
+ const setupAnalytics = (projectAccessKey, server) => {
3670
+ if (!server) {
3671
+ server = 'https://nodes.sequence.app';
3672
+ }
3673
+
3674
+ // disable tracking if projectAccessKey is not set
3675
+ const noop = !projectAccessKey;
3676
+
3677
+ // auth
3678
+ const auth = {};
3679
+ if (projectAccessKey) {
3680
+ auth.headers = {
3681
+ 'X-Access-Key': projectAccessKey
3682
+ };
3683
+ }
3684
+ return new Analytics(server, auth, {
3685
+ noop: noop,
3686
+ defaultEnabled: true,
3687
+ privacy: {
3688
+ userIdHash: true,
3689
+ userAgentSalt: false
3690
+ },
3691
+ initProps: () => {
3692
+ if (!tracker.isBrowser()) {
3693
+ return {};
3694
+ } else {
3695
+ return {
3696
+ origin: window.location.origin
3697
+ };
3698
+ }
3699
+ }
3700
+ });
3701
+ };
3702
+
3703
+ /**
3704
+ * This session class is meant to persist the state of the wallet connection
3705
+ * whitin the dapp. This enables the client to retain the wallet address (and some more)
3706
+ * even if the user refreshes the page. Otherwise we would have to open the popup again.
3707
+ */
3708
+ class SequenceClientSession {
3709
+ constructor(store) {
3710
+ this.store = store;
3711
+ }
3712
+ connectedSession() {
3713
+ const session = this.getSession();
3714
+ if (session && session.accountAddress && session.walletContext && session.networks) {
3715
+ return {
3716
+ accountAddress: session.accountAddress,
3717
+ walletContext: session.walletContext,
3718
+ networks: session.networks
3719
+ };
3720
+ }
3721
+ throw new Error('Sequence session not connected');
3722
+ }
3723
+ hasSession() {
3724
+ var _this$getSession;
3725
+ return ((_this$getSession = this.getSession()) == null ? void 0 : _this$getSession.accountAddress) !== undefined;
3726
+ }
3727
+ setSession(session) {
3728
+ return this.store.setItem(SequenceClientSession.SESSION_LOCALSTORE_KEY, JSON.stringify(session));
3729
+ }
3730
+ getSession() {
3731
+ try {
3732
+ const session = this.store.getItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
3733
+ if (session) {
3734
+ return JSON.parse(session);
3735
+ }
3736
+ } catch (err) {
3737
+ console.error('Error parsing session', err);
3738
+ }
3739
+ return undefined;
3740
+ }
3741
+ async clearSession() {
3742
+ return this.store.removeItem(SequenceClientSession.SESSION_LOCALSTORE_KEY);
3743
+ }
3744
+ }
3745
+
3746
+ /**
3747
+ * The wallet webapp doesn't really care what's the "default chain" for the user.
3748
+ * so we don't even bother to send this information to the wallet. Instead, we
3749
+ * track it locally using storage, that way the data stays always in sync.
3750
+ */
3751
+ SequenceClientSession.SESSION_LOCALSTORE_KEY = '@sequence.session';
3752
+ class DefaultChainIdTracker {
3753
+ constructor(store, startingChainId = 1) {
3754
+ this.store = store;
3755
+ this.startingChainId = startingChainId;
3756
+ this.callbacks = [];
3757
+ store.onItemChange(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, value => {
3758
+ if (value) {
3759
+ const chainId = parseInt(value);
3760
+ this.callbacks.forEach(cb => cb(chainId));
3761
+ }
3762
+ });
3763
+ }
3764
+ onDefaultChainIdChanged(callback) {
3765
+ this.callbacks.push(callback);
3766
+ return () => {
3767
+ this.callbacks = this.callbacks.filter(cb => cb !== callback);
3768
+ };
3769
+ }
3770
+ setDefaultChainId(chainId) {
3771
+ if (chainId !== this.getDefaultChainId()) {
3772
+ this.store.setItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, chainId.toString());
3773
+ }
3774
+ }
3775
+ getDefaultChainId() {
3776
+ const read = this.store.getItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY);
3777
+ if (!read || read.length === 0) {
3778
+ return this.startingChainId;
3779
+ }
3780
+ return parseInt(read);
3781
+ }
3782
+ }
3783
+ DefaultChainIdTracker.SESSION_CHAIN_ID_KEY = '@sequence.session.defaultChainId';
3784
+ /**
3785
+ * This is a wallet client for sequence wallet-webapp. It connects using *some* transport
3786
+ * and it allows to perform all sequence specific (or write) operations related to the wallet.
3787
+ *s
3788
+ * It doesn't implement a full ethereum Provider, it doesn't include read-only methods.
3789
+ */
3790
+
3791
+ // TODO: rename Client to transport.. or something.. like SequenceTransport ..
3792
+ class SequenceClient {
3793
+ constructor(transport, store, options) {
3794
+ var _options$defaultEIP, _this$session$getSess;
3795
+ this.session = void 0;
3796
+ this.defaultChainId = void 0;
3797
+ this.callbacks = {};
3798
+ this.transport = void 0;
3799
+ this.defaultEIP6492 = void 0;
3800
+ this.projectAccessKey = void 0;
3801
+ this.analytics = void 0;
3802
+ if (isMuxTransportTemplate(transport)) {
3803
+ this.transport = MuxMessageProvider.new(transport, options == null ? void 0 : options.projectAccessKey);
3804
+ } else if (isProviderTransport(transport)) {
3805
+ this.transport = transport;
3806
+ } else {
3807
+ throw new Error('Invalid transport');
3808
+ }
3809
+ const defaultChainId = options == null ? void 0 : options.defaultChainId;
3810
+ this.defaultEIP6492 = (_options$defaultEIP = options == null ? void 0 : options.defaultEIP6492) != null ? _options$defaultEIP : false;
3811
+ this.session = new SequenceClientSession(store);
3812
+ this.defaultChainId = new DefaultChainIdTracker(store, defaultChainId);
3813
+ this.transport.on('accountsChanged', accounts => {
3814
+ var _this$callbacks$accou;
3815
+ if (accounts.length > 1) {
3816
+ console.warn('SequenceClient: wallet-webapp returned more than one account');
3817
+ }
3818
+ (_this$callbacks$accou = this.callbacks.accountsChanged) == null || _this$callbacks$accou.forEach(cb => cb(accounts));
3819
+ });
3820
+ this.transport.on('connect', response => {
3821
+ var _this$callbacks$conne;
3822
+ const chainIdHex = ethers.ethers.toQuantity(this.getChainId());
3823
+ (_this$callbacks$conne = this.callbacks.connect) == null || _this$callbacks$conne.forEach(cb => cb(_extends({}, response, {
3824
+ // Ignore the full connect response
3825
+ // use the chainId defined locally
3826
+ chainId: chainIdHex
3827
+ })));
3828
+ });
3829
+ this.transport.on('disconnect', (error, origin) => {
3830
+ var _this$callbacks$disco;
3831
+ (_this$callbacks$disco = this.callbacks.disconnect) == null || _this$callbacks$disco.forEach(cb => cb(error, origin));
3832
+ });
3833
+ this.transport.on('networks', networks => {
3834
+ var _this$callbacks$netwo;
3835
+ (_this$callbacks$netwo = this.callbacks.networks) == null || _this$callbacks$netwo.forEach(cb => cb(networks));
3836
+ });
3837
+ this.transport.on('walletContext', context => {
3838
+ var _this$callbacks$walle;
3839
+ (_this$callbacks$walle = this.callbacks.walletContext) == null || _this$callbacks$walle.forEach(cb => cb(context));
3840
+ });
3841
+ this.transport.on('open', info => {
3842
+ var _this$callbacks$open;
3843
+ (_this$callbacks$open = this.callbacks.open) == null || _this$callbacks$open.forEach(cb => cb(info));
3844
+ });
3845
+ this.transport.on('close', () => {
3846
+ var _this$callbacks$close;
3847
+ (_this$callbacks$close = this.callbacks.close) == null || _this$callbacks$close.forEach(cb => cb());
3848
+ });
3849
+ this.transport.on('chainChanged', (chainIdHex, origin) => {
3850
+ var _this$callbacks$chain;
3851
+ (_this$callbacks$chain = this.callbacks.chainChanged) == null || _this$callbacks$chain.forEach(cb => cb(chainIdHex, origin));
3852
+ });
3853
+
3854
+ // We don't listen for the transport chainChanged event
3855
+ // instead we handle it locally, so we listen for changes in the store
3856
+ this.defaultChainId.onDefaultChainIdChanged(chainId => {
3857
+ var _this$callbacks$chain2;
3858
+ const chainIdHex = ethers.ethers.toQuantity(chainId);
3859
+ (_this$callbacks$chain2 = this.callbacks.chainChanged) == null || _this$callbacks$chain2.forEach(cb => cb(chainIdHex));
3860
+ });
3861
+ if (options != null && options.projectAccessKey) {
3862
+ this.projectAccessKey = options.projectAccessKey;
3863
+ }
3864
+ if (this.projectAccessKey && options != null && options.analytics) {
3865
+ this.analytics = setupAnalytics(this.projectAccessKey);
3866
+ }
3867
+ if ((_this$session$getSess = this.session.getSession()) != null && _this$session$getSess.accountAddress) {
3868
+ var _this$analytics, _this$session$getSess2;
3869
+ (_this$analytics = this.analytics) == null || _this$analytics.identify((_this$session$getSess2 = this.session.getSession()) == null || (_this$session$getSess2 = _this$session$getSess2.accountAddress) == null ? void 0 : _this$session$getSess2.toLowerCase());
3870
+ }
3871
+ }
3872
+
3873
+ // Callbacks
3874
+
3875
+ registerCallback(eventName, callback) {
3876
+ if (!this.callbacks[eventName]) {
3877
+ this.callbacks[eventName] = [];
3878
+ }
3879
+ this.callbacks[eventName].push(callback);
3880
+ return () => {
3881
+ this.callbacks[eventName] = this.callbacks[eventName].filter(c => c !== callback);
3882
+ };
3883
+ }
3884
+
3885
+ // Individual callbacks lead to more idiomatic code
3886
+
3887
+ onOpen(callback) {
3888
+ return this.registerCallback('open', callback);
3889
+ }
3890
+ onClose(callback) {
3891
+ return this.registerCallback('close', callback);
3892
+ }
3893
+ onConnect(callback) {
3894
+ return this.registerCallback('connect', callback);
3895
+ }
3896
+ onDisconnect(callback) {
3897
+ return this.registerCallback('disconnect', callback);
3898
+ }
3899
+ onNetworks(callback) {
3900
+ return this.registerCallback('networks', callback);
3901
+ }
3902
+ onAccountsChanged(callback) {
3903
+ return this.registerCallback('accountsChanged', callback);
3904
+ }
3905
+
3906
+ // @deprecated
3907
+ onWalletContext(callback) {
3908
+ return this.registerCallback('walletContext', callback);
3909
+ }
3910
+ onChainChanged(callback) {
3911
+ return this.registerCallback('chainChanged', callback);
3912
+ }
3913
+ onDefaultChainIdChanged(callback) {
3914
+ return this.registerCallback('chainChanged', callback);
3915
+ }
3916
+ getChainId() {
3917
+ return this.defaultChainId.getDefaultChainId();
3918
+ }
3919
+ setDefaultChainId(chainId) {
3920
+ return this.defaultChainId.setDefaultChainId(chainId);
3921
+ }
3922
+
3923
+ // Proxy transport methods
3924
+
3925
+ async openWallet(path, intent) {
3926
+ this.transport.openWallet(path, intent, this.getChainId());
3927
+ await this.transport.waitUntilOpened();
3928
+ return this.isOpened();
3929
+ }
3930
+ closeWallet() {
3931
+ return this.transport.closeWallet();
3932
+ }
3933
+ isOpened() {
3934
+ return this.transport.isOpened();
3935
+ }
3936
+ isConnected() {
3937
+ return this.session.hasSession();
3938
+ }
3939
+ getSession() {
3940
+ return this.session.getSession();
3941
+ }
3942
+
3943
+ // Basic API
3944
+ getAddress() {
3945
+ const session = this.session.connectedSession();
3946
+ return session.accountAddress;
3947
+ }
3948
+ async connect(options) {
3949
+ if ((options == null ? void 0 : options.authorizeVersion) === undefined) {
3950
+ // Populate default authorize version if not provided
3951
+ options.authorizeVersion = 2;
3952
+ }
3953
+ if ((options == null ? void 0 : options.refresh) === true) {
3954
+ this.disconnect();
3955
+ }
3956
+ options.projectAccessKey = this.projectAccessKey;
3957
+ if (options) {
3958
+ if (options.authorize) {
3959
+ if (!options.app) {
3960
+ throw new Error(`connecting with 'authorize' option also requires 'app' to be set`);
3961
+ }
3962
+ if (options.authorizeVersion === undefined) {
3963
+ options.authorizeVersion = 2;
3964
+ }
3965
+ }
3966
+ }
3967
+ await this.openWallet(undefined, {
3968
+ type: 'connect',
3969
+ options: _extends({}, options, {
3970
+ networkId: this.getChainId(),
3971
+ clientVersion: core.VERSION
3972
+ })
3973
+ });
3974
+ const connectDetails = await this.transport.waitUntilConnected().catch(error => {
3975
+ if (error instanceof Error) {
3976
+ return {
3977
+ connected: false,
3978
+ error: error.message
3979
+ };
3980
+ } else {
3981
+ return {
3982
+ connected: false,
3983
+ error: JSON.stringify(error)
3984
+ };
3985
+ }
3986
+ });
3987
+
3988
+ // Normalize chainId into a decimal string
3989
+ // TODO: Remove this once wallet-webapp returns chainId as a string
3990
+ if (connectDetails.chainId) {
3991
+ connectDetails.chainId = BigInt(connectDetails.chainId).toString();
3992
+ }
3993
+ if (connectDetails.connected) {
3994
+ var _connectDetails$sessi;
3995
+ if (!connectDetails.session) {
3996
+ throw new Error('impossible state, connect response is missing session');
3997
+ }
3998
+ this.session.setSession(connectDetails.session);
3999
+ if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
4000
+ var _this$analytics2;
4001
+ (_this$analytics2 = this.analytics) == null || _this$analytics2.identify(connectDetails.session.accountAddress.toLowerCase());
4002
+ }
4003
+ }
4004
+ return connectDetails;
4005
+ }
4006
+ disconnect() {
4007
+ var _this$analytics3;
4008
+ if (this.isOpened()) {
4009
+ this.closeWallet();
4010
+ }
4011
+ (_this$analytics3 = this.analytics) == null || _this$analytics3.reset();
4012
+ return this.session.clearSession();
4013
+ }
4014
+
4015
+ // Higher level API
4016
+
4017
+ async request(request) {
4018
+ // Internally when sending requests we use `legacy_sign`
4019
+ // to avoid the default EIP6492 behavior overriding an explicit
4020
+ // "legacy sign" request, so we map the method here.
4021
+ request.method = this.mapSignMethod(request.method);
4022
+ const result = await this.transport.request(request);
4023
+
4024
+ // We may need to unwrap the response if it's a JSON-RPC response. ie. older universal wallet versions
4025
+ return unwrapJsonRpcResponse(result);
4026
+ }
4027
+ async getNetworks(pull) {
4028
+ const connectedSession = this.session.connectedSession();
4029
+ if (pull) {
4030
+ connectedSession.networks = await this.request({
4031
+ method: 'sequence_getNetworks'
4032
+ });
4033
+ this.session.setSession(connectedSession);
4034
+ }
4035
+ return connectedSession.networks;
4036
+ }
4037
+
4038
+ // NOTICE: `legacy_sign` will get overriden by `send`
4039
+ // it is done this way to ensure that:
4040
+ // - `send` handles `personal_sign` as a request for the default sign method
4041
+ // - explicit `personal_sign` is not replaced by `sequence_sign` (if default is EI6492)
4042
+ signMethod(options) {
4043
+ if ((options == null ? void 0 : options.eip6492) === undefined) {
4044
+ return 'personal_sign';
4045
+ }
4046
+ return options.eip6492 ? 'sequence_sign' : 'legacy_sign';
4047
+ }
4048
+ signTypedDataMethod(options) {
4049
+ if ((options == null ? void 0 : options.eip6492) === undefined) {
4050
+ return 'eth_signTypedData_v4';
4051
+ }
4052
+ return options.eip6492 ? 'sequence_signTypedData_v4' : 'legacy_signTypedData_v4';
4053
+ }
4054
+ mapSignMethod(method) {
4055
+ if (method === 'personal_sign') {
4056
+ if (this.defaultEIP6492) {
4057
+ return 'sequence_sign';
4058
+ } else {
4059
+ return 'personal_sign';
4060
+ }
4061
+ }
4062
+ if (method === 'eth_signTypedData_v4') {
4063
+ if (this.defaultEIP6492) {
4064
+ return 'sequence_signTypedData_v4';
4065
+ } else {
4066
+ return 'eth_signTypedData_v4';
4067
+ }
4068
+ }
4069
+ if (method === 'legacy_sign') {
4070
+ return 'personal_sign';
4071
+ }
4072
+ if (method === 'legacy_signTypedData_v4') {
4073
+ return 'eth_signTypedData_v4';
4074
+ }
4075
+ return method;
4076
+ }
4077
+ async signMessage(message, options) {
4078
+ var _this$analytics4;
4079
+ const method = this.signMethod(options);
4080
+ (_this$analytics4 = this.analytics) == null || _this$analytics4.track({
4081
+ event: 'SIGN_MESSAGE_REQUEST',
4082
+ props: {
4083
+ chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
4084
+ }
4085
+ });
4086
+ message = ethers.ethers.hexlify(messageToBytes(message));
4087
+
4088
+ // Address is ignored by the wallet webapp
4089
+ return this.request({
4090
+ method,
4091
+ params: [message, this.getAddress()],
4092
+ chainId: options == null ? void 0 : options.chainId
4093
+ });
4094
+ }
4095
+ async signTypedData(typedData, options) {
4096
+ var _this$analytics5;
4097
+ const method = this.signTypedDataMethod(options);
4098
+
4099
+ // TODO: Stop using ethers for this, this is the only place where we use it
4100
+ // and it makes the client depend on ethers.
4101
+ const encoded = ethers.ethers.TypedDataEncoder.getPayload(typedData.domain, typedData.types, typedData.message);
4102
+
4103
+ // The sign typed data will use one of the following chainIds, in order:
4104
+ // - The one provided in the options
4105
+ // - The one provided in the typedData.domain.chainId
4106
+ // - The default chainId
4107
+
4108
+ (_this$analytics5 = this.analytics) == null || _this$analytics5.track({
4109
+ event: 'SIGN_TYPED_DATA_REQUEST',
4110
+ props: {
4111
+ chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
4112
+ }
4113
+ });
4114
+ return this.request({
4115
+ method,
4116
+ params: [this.getAddress(), encoded],
4117
+ chainId: (options == null ? void 0 : options.chainId) || typedData.domain.chainId && Number(typedData.domain.chainId) || this.getChainId()
4118
+ });
4119
+ }
4120
+ async sendTransaction(tx, options) {
4121
+ var _this$analytics6;
4122
+ const sequenceTxs = Array.isArray(tx) ? tx : [tx];
4123
+ const extendedTxs = toExtended(sequenceTxs);
4124
+ (_this$analytics6 = this.analytics) == null || _this$analytics6.track({
4125
+ event: 'SEND_TRANSACTION_REQUEST',
4126
+ props: {
4127
+ chainId: `${(options == null ? void 0 : options.chainId) || this.getChainId()}`
4128
+ }
4129
+ });
4130
+ return this.request({
4131
+ method: 'eth_sendTransaction',
4132
+ params: [extendedTxs],
4133
+ chainId: options == null ? void 0 : options.chainId
4134
+ });
4135
+ }
4136
+ async getWalletContext() {
4137
+ return this.request({
4138
+ method: 'sequence_getWalletContext'
4139
+ });
4140
+ }
4141
+ async getOnchainWalletConfig(options) {
4142
+ // NOTICE: sequence_getWalletConfig sends the chainId as a param
4143
+ const res = await this.request({
4144
+ method: 'sequence_getWalletConfig',
4145
+ params: [(options == null ? void 0 : options.chainId) || this.getChainId()],
4146
+ chainId: options == null ? void 0 : options.chainId
4147
+ });
4148
+ return Array.isArray(res) ? res[0] : res;
4149
+ }
4150
+
4151
+ // NOTICE: We are leaving out all the "regular" methods os a tipical
4152
+ // JSON RPC Provider (eth_getBlockByNumber, eth_call, etc)
4153
+ // wallet-webapp does implement them, but this client is meant to be
4154
+ // exclusively used for Sequence specific methods
4155
+ }
4156
+
4157
+ // Unwrap a JsonRpcResponse result
4158
+ const unwrapJsonRpcResponse = response => {
4159
+ if (response && typeof response === 'object' && 'jsonrpc' in response && 'result' in response) {
4160
+ return response.result;
4161
+ }
4162
+ return response;
4163
+ };
4164
+
4165
+ const DefaultProviderConfig = {
4166
+ transports: {
4167
+ walletAppURL: 'https://sequence.app',
4168
+ windowTransport: {
4169
+ enabled: true
4170
+ },
4171
+ proxyTransport: {
4172
+ enabled: false
4173
+ }
4174
+ },
4175
+ defaultNetwork: 1,
4176
+ analytics: true
4177
+ };
4178
+ let sequenceWalletProvider;
4179
+
4180
+ /**
4181
+ * Initializes a wallet with the provided project access key and optional configuration.
4182
+ *
4183
+ * @param projectAccessKey - Access key for the project that can be obtained from Sequence Builder on sequence.build
4184
+ * @param partialConfig - Optional partial configuration for the wallet.
4185
+ * @returns The initialized wallet provider.
4186
+ * @throws Error if projectAccessKey is not provided, empty string or is not string.
4187
+ */
4188
+ const initWallet = (projectAccessKey, partialConfig) => {
4189
+ var _findNetworkConfig, _client$getSession;
4190
+ if (!projectAccessKey || typeof projectAccessKey !== 'string') {
4191
+ throw new Error('Please pass a projectAccessKey in initWallet.');
4192
+ }
4193
+ if (sequenceWalletProvider) {
4194
+ return sequenceWalletProvider;
4195
+ }
4196
+
4197
+ // Combine both the provided config and the default config
4198
+ const config = _extends({}, DefaultProviderConfig, partialConfig, {
4199
+ transports: _extends({}, DefaultProviderConfig.transports, partialConfig == null ? void 0 : partialConfig.transports)
4200
+ });
4201
+ let networks = [];
4202
+ const updateNetworks = (connectedNetworks = []) => {
4203
+ var _config$networks;
4204
+ networks = mergeNetworks(network.allNetworks, connectedNetworks, (_config$networks = config.networks) != null ? _config$networks : []);
4205
+
4206
+ // Append projectAccessKey to network rpcUrls
4207
+ networks = networks.map(network => {
4208
+ // Don't double-append in the case the user has already included their access key in the rpc URL
4209
+ if (network.rpcUrl.includes(projectAccessKey)) {
4210
+ return network;
4211
+ }
4212
+
4213
+ // XXX: This will probably break non-sequence RPC provider URLs.
4214
+ network.rpcUrl = network.rpcUrl + `/${projectAccessKey}`;
4215
+ return network;
4216
+ });
4217
+ };
4218
+ updateNetworks();
4219
+
4220
+ // This is the starting default network (as defined by the config)
4221
+ // it can be later be changed using `wallet_switchEthereumChain` or some
4222
+ // of the other methods on the provider.
4223
+ const defaultNetwork = config.defaultNetwork ? (_findNetworkConfig = network.findNetworkConfig(networks, config.defaultNetwork)) == null ? void 0 : _findNetworkConfig.chainId : undefined;
4224
+ if (!defaultNetwork && config.defaultNetwork) {
4225
+ throw new Error(`defaultNetwork not found for chainId: ${config.defaultNetwork}`);
4226
+ }
4227
+
4228
+ // Generate ItemStore
4229
+ const itemStore = config.localStorage || useBestStore();
4230
+
4231
+ // Create client, provider and return signer
4232
+ const client = new SequenceClient(config.transports, itemStore, {
4233
+ defaultChainId: defaultNetwork,
4234
+ defaultEIP6492: config.defaultEIP6492,
4235
+ projectAccessKey: projectAccessKey,
4236
+ analytics: config.analytics
4237
+ });
4238
+ updateNetworks((_client$getSession = client.getSession()) == null ? void 0 : _client$getSession.networks);
4239
+ client.onConnect(ev => {
4240
+ var _ev$session;
4241
+ updateNetworks((_ev$session = ev.session) == null ? void 0 : _ev$session.networks);
4242
+ });
4243
+ const rpcProviders = {};
4244
+
4245
+ // This builds a "public rpc" on demand, we build them on demand because we don't want to
4246
+ // generate a bunch of providers for networks that aren't used.
4247
+ const providerForChainId = chainId => {
4248
+ const network$1 = network.findNetworkConfig(networks, chainId);
4249
+ if (!network$1) {
4250
+ throw new Error(`no network config found for chainId: ${chainId}`);
4251
+ }
4252
+ const {
4253
+ rpcUrl
4254
+ } = network$1;
4255
+
4256
+ // Cache providers by rpc url
4257
+ if (!rpcProviders[rpcUrl]) {
4258
+ rpcProviders[rpcUrl] = new network.JsonRpcProvider(rpcUrl, {
4259
+ middlewares: [network.loggingProviderMiddleware, network.exceptionProviderMiddleware, new network.CachedProvider()]
4260
+ }, {
4261
+ cacheTimeout: -1
4262
+ });
4263
+ }
4264
+ return rpcProviders[rpcUrl];
4265
+ };
4266
+ sequenceWalletProvider = new SequenceProvider(client, providerForChainId);
4267
+ return sequenceWalletProvider;
4268
+ };
4269
+ const unregisterWallet = () => {
4270
+ if (!sequenceWalletProvider) return;
4271
+ sequenceWalletProvider.client.closeWallet();
4272
+ sequenceWalletProvider.client.transport.unregister();
4273
+ sequenceWalletProvider = undefined;
4274
+ };
4275
+ const getWallet = () => {
4276
+ if (!sequenceWalletProvider) {
4277
+ throw new Error('Wallet has not been initialized, call sequence.initWallet(config) first.');
4278
+ }
4279
+ return sequenceWalletProvider;
4280
+ };
4281
+
4282
+ // allNetworks <- connectedNetworks <- config.networks
4283
+ const mergeNetworks = (...networks) => {
4284
+ const networkMap = new Map();
4285
+ for (const network of networks.flat()) {
4286
+ if (network.chainId && network.rpcUrl) {
4287
+ const existingNetwork = networkMap.get(network.chainId);
4288
+ networkMap.set(network.chainId, _extends({}, existingNetwork, network));
4289
+ }
4290
+ }
4291
+ return Array.from(networkMap.values());
4292
+ };
4293
+
4294
+ exports.Analytics = Analytics;
4295
+ exports.AnalyticsEventType = AnalyticsEventType;
4296
+ exports.BaseInjectedTransport = BaseInjectedTransport;
4297
+ exports.BaseProviderTransport = BaseProviderTransport;
4298
+ exports.BaseWalletTransport = BaseWalletTransport;
4299
+ exports.CHANNEL_ID = CHANNEL_ID;
4300
+ exports.DefaultChainIdTracker = DefaultChainIdTracker;
4301
+ exports.DefaultProviderConfig = DefaultProviderConfig;
4302
+ exports.ErrSignedInRequired = ErrSignedInRequired;
4303
+ exports.EventType = EventType;
4304
+ exports.ExtensionMessageHandler = ExtensionMessageHandler;
4305
+ exports.ExtensionMessageProvider = ExtensionMessageProvider;
4306
+ exports.InitState = InitState;
4307
+ exports.LocalStorage = LocalStorage;
4308
+ exports.MemoryItemStore = MemoryItemStore;
4309
+ exports.MuxMessageProvider = MuxMessageProvider;
4310
+ exports.OpenState = OpenState;
4311
+ exports.PROVIDER_OPEN_TIMEOUT = PROVIDER_OPEN_TIMEOUT;
4312
+ exports.ProviderError = ProviderError;
4313
+ exports.ProxyMessageChannel = ProxyMessageChannel;
4314
+ exports.ProxyMessageChannelPort = ProxyMessageChannelPort;
4315
+ exports.ProxyMessageHandler = ProxyMessageHandler;
4316
+ exports.ProxyMessageProvider = ProxyMessageProvider;
4317
+ exports.SequenceClient = SequenceClient;
4318
+ exports.SequenceClientSession = SequenceClientSession;
4319
+ exports.SequenceProvider = SequenceProvider;
4320
+ exports.SequenceSigner = SequenceSigner;
4321
+ exports.SingleNetworkSequenceProvider = SingleNetworkSequenceProvider;
4322
+ exports.SingleNetworkSequenceSigner = SingleNetworkSequenceSigner;
4323
+ exports.UnrealMessageHandler = UnrealMessageHandler;
4324
+ exports.UnrealMessageProvider = UnrealMessageProvider;
4325
+ exports.WalletRequestHandler = WalletRequestHandler;
4326
+ exports.WindowMessageHandler = WindowMessageHandler;
4327
+ exports.WindowMessageProvider = WindowMessageProvider;
4328
+ exports.WindowSessionParams = WindowSessionParams;
4329
+ exports.getWallet = getWallet;
4330
+ exports.initWallet = initWallet;
4331
+ exports.isBrowserExtension = isBrowserExtension;
4332
+ exports.isMuxTransportTemplate = isMuxTransportTemplate;
4333
+ exports.isProviderTransport = isProviderTransport;
4334
+ exports.isUnityPlugin = isUnityPlugin;
4335
+ exports.isValidMessageSignature = isValidMessageSignature;
4336
+ exports.isValidSignature = isValidSignature;
4337
+ exports.isValidTypedDataSignature = isValidTypedDataSignature;
4338
+ exports.isWalletUpToDate = isWalletUpToDate;
4339
+ exports.messageToBytes = messageToBytes;
4340
+ exports.nextMessageIdx = nextMessageIdx;
4341
+ exports.prefixEIP191Message = prefixEIP191Message;
4342
+ exports.resolveArrayProperties = resolveArrayProperties;
4343
+ exports.setupAnalytics = setupAnalytics;
4344
+ exports.trimEIP191Prefix = trimEIP191Prefix;
4345
+ exports.unregisterWallet = unregisterWallet;
4346
+ exports.useBestStore = useBestStore;
4347
+ exports.validateTransactionRequest = validateTransactionRequest;