@0xsequence/provider 2.3.17 → 2.3.19

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