@0xsequence/provider 0.0.0-20220614152529 → 0.0.0-20220630211555
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/0xsequence-provider.cjs.dev.js +2158 -2140
- package/dist/0xsequence-provider.cjs.prod.js +2158 -2140
- package/dist/0xsequence-provider.esm.js +2171 -2147
- package/dist/declarations/src/provider.d.ts +13 -8
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +3 -1
- package/dist/declarations/src/utils/index.d.ts +3 -3
- package/dist/declarations/src/utils.d.ts +3 -19
- package/dist/declarations/src/wallet.d.ts +7 -10
- package/package.json +8 -8
- package/src/provider.ts +69 -36
- package/src/transports/base-wallet-transport.ts +1 -1
- package/src/transports/wallet-request-handler.ts +85 -33
- package/src/transports/window-transport/window-message-provider.ts +2 -0
- package/src/types.ts +0 -1
- package/src/utils/index.ts +61 -30
- package/src/utils.ts +17 -103
- package/src/wallet.ts +20 -34
|
@@ -5,12 +5,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var eventemitter2 = require('eventemitter2');
|
|
6
6
|
var utils = require('@0xsequence/utils');
|
|
7
7
|
var ethers = require('ethers');
|
|
8
|
-
var config = require('@0xsequence/config');
|
|
9
|
-
var providers = require('@ethersproject/providers');
|
|
10
8
|
var network = require('@0xsequence/network');
|
|
9
|
+
var config = require('@0xsequence/config');
|
|
11
10
|
var wallet = require('@0xsequence/wallet');
|
|
12
11
|
var transactions = require('@0xsequence/transactions');
|
|
13
12
|
var auth = require('@0xsequence/auth');
|
|
13
|
+
var providers = require('@ethersproject/providers');
|
|
14
14
|
|
|
15
15
|
function _extends() {
|
|
16
16
|
_extends = Object.assign || function (target) {
|
|
@@ -478,629 +478,569 @@ class BaseProviderTransport {
|
|
|
478
478
|
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
// value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
|
|
485
|
-
// a Signer value is the actually interface to a signed-in account
|
|
486
|
-
constructor(_signer, prompter, mainnetNetworks, testnetNetworks = []) {
|
|
487
|
-
var _this = this;
|
|
481
|
+
const recoverWalletConfig = async (address, digest, signature, chainId, provider, configTracker, walletContext) => {
|
|
482
|
+
const subDigest = utils.packMessageData(address, chainId, digest);
|
|
483
|
+
const config$1 = await wallet.recoverConfig(subDigest, signature, provider, walletContext || network.sequenceContext, ethers.ethers.BigNumber.from(chainId).toNumber(), true, configTracker);
|
|
488
484
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
this.prompter = void 0;
|
|
492
|
-
this.mainnetNetworks = void 0;
|
|
493
|
-
this.testnetNetworks = void 0;
|
|
494
|
-
this._openIntent = void 0;
|
|
495
|
-
this._connectOptions = void 0;
|
|
496
|
-
this._defaultNetworkId = void 0;
|
|
497
|
-
this._chainId = void 0;
|
|
498
|
-
this.events = new eventemitter2.EventEmitter2();
|
|
485
|
+
if (walletContext) {
|
|
486
|
+
const recoveredWalletAddress = config.addressOf(config$1, walletContext);
|
|
499
487
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
488
|
+
if (config$1.address && config$1.address !== recoveredWalletAddress) {
|
|
489
|
+
throw new Error('recovered address does not match the WalletConfig address, check the WalletContext');
|
|
490
|
+
} else {
|
|
491
|
+
config$1.address = recoveredWalletAddress;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
505
494
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
495
|
+
return config$1;
|
|
496
|
+
};
|
|
497
|
+
const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:';
|
|
498
|
+
class LocalStorage {
|
|
499
|
+
constructor() {}
|
|
510
500
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
501
|
+
static getInstance() {
|
|
502
|
+
if (!LocalStorage._instance) {
|
|
503
|
+
LocalStorage._instance = {
|
|
504
|
+
getItem: key => Promise.resolve(window.localStorage.getItem(key)),
|
|
505
|
+
setItem: (key, value) => Promise.resolve(window.localStorage.setItem(key, value)),
|
|
506
|
+
removeItem: key => Promise.resolve(window.localStorage.removeItem(key))
|
|
507
|
+
};
|
|
508
|
+
}
|
|
517
509
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
510
|
+
return this._instance;
|
|
511
|
+
}
|
|
521
512
|
|
|
522
|
-
|
|
523
|
-
|
|
513
|
+
static use(instance) {
|
|
514
|
+
LocalStorage._instance = instance;
|
|
515
|
+
}
|
|
524
516
|
|
|
525
|
-
|
|
526
|
-
const response = {
|
|
527
|
-
jsonrpc: '2.0',
|
|
528
|
-
id: request.id,
|
|
529
|
-
result: null
|
|
530
|
-
};
|
|
531
|
-
await _this.getSigner();
|
|
517
|
+
} // window.localstorage helper
|
|
532
518
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
519
|
+
LocalStorage._instance = void 0;
|
|
520
|
+
class LocalStore {
|
|
521
|
+
constructor(key, def) {
|
|
522
|
+
this.def = def;
|
|
523
|
+
this.key = void 0;
|
|
524
|
+
this.key = key;
|
|
525
|
+
}
|
|
539
526
|
|
|
527
|
+
async get() {
|
|
528
|
+
const val = await LocalStorage.getInstance().getItem(this.key);
|
|
540
529
|
|
|
541
|
-
|
|
542
|
-
|
|
530
|
+
if (val === null) {
|
|
531
|
+
return this.def;
|
|
532
|
+
}
|
|
543
533
|
|
|
544
|
-
|
|
545
|
-
|
|
534
|
+
try {
|
|
535
|
+
return JSON.parse(val);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
console.error(err);
|
|
538
|
+
}
|
|
546
539
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
{
|
|
550
|
-
const result = await provider.send('net_version', []);
|
|
551
|
-
response.result = result;
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
554
542
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
response.result = result;
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
543
|
+
set(val) {
|
|
544
|
+
val ? LocalStorage.getInstance().setItem(this.key, JSON.stringify(val)) : LocalStorage.getInstance().removeItem(this.key);
|
|
545
|
+
}
|
|
561
546
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
response.result = [walletAddress];
|
|
566
|
-
break;
|
|
567
|
-
}
|
|
547
|
+
del() {
|
|
548
|
+
LocalStorage.getInstance().removeItem(this.key);
|
|
549
|
+
}
|
|
568
550
|
|
|
569
|
-
|
|
570
|
-
{
|
|
571
|
-
const [accountAddress, blockTag] = request.params;
|
|
572
|
-
const walletBalance = await provider.getBalance(accountAddress, blockTag);
|
|
573
|
-
response.result = walletBalance.toHexString();
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
551
|
+
}
|
|
576
552
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
553
|
+
const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession';
|
|
554
|
+
class BaseWalletTransport {
|
|
555
|
+
// appOrigin identifies the dapp's origin which opened the app. A transport
|
|
556
|
+
// will auto-detect and set this value if it can. This is determined
|
|
557
|
+
// as the parent app/window which opened the wallet.
|
|
558
|
+
constructor(walletRequestHandler) {
|
|
559
|
+
var _this = this;
|
|
584
560
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
561
|
+
this.walletRequestHandler = void 0;
|
|
562
|
+
this._sessionId = void 0;
|
|
563
|
+
this._registered = void 0;
|
|
564
|
+
this._init = void 0;
|
|
565
|
+
this._initNonce = void 0;
|
|
566
|
+
this._initCallback = void 0;
|
|
567
|
+
this.appOrigin = void 0;
|
|
592
568
|
|
|
593
|
-
|
|
594
|
-
|
|
569
|
+
this.sendAsync = async function (request, callback, chainId) {
|
|
570
|
+
throw new Error('abstract method');
|
|
571
|
+
};
|
|
595
572
|
|
|
596
|
-
|
|
597
|
-
|
|
573
|
+
this.handleMessage = async function (message) {
|
|
574
|
+
const request = message; // ensure initial handshake is complete before accepting
|
|
575
|
+
// other kinds of messages.
|
|
598
576
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
577
|
+
if (_this._init !== exports.InitState.OK) {
|
|
578
|
+
if (request.type === exports.EventType.INIT) {
|
|
579
|
+
if (_this.isValidInitAck(message)) {
|
|
580
|
+
// successful init
|
|
581
|
+
if (_this._initCallback) _this._initCallback();
|
|
582
|
+
} else {
|
|
583
|
+
// failed init
|
|
584
|
+
if (_this._initCallback) _this._initCallback('invalid init');
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
604
588
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
589
|
+
return;
|
|
590
|
+
} // ensure signer is ready to handle requests
|
|
591
|
+
// if (this.walletRequestHandler.getSigner() === undefined) {
|
|
592
|
+
// await this.walletRequestHandler.signerReady()
|
|
593
|
+
// }
|
|
594
|
+
// handle request
|
|
612
595
|
|
|
613
|
-
if (sig && sig.length > 0) {
|
|
614
|
-
response.result = sig;
|
|
615
|
-
} else {
|
|
616
|
-
// The user has declined the request when value is null
|
|
617
|
-
throw new Error('declined by user');
|
|
618
|
-
}
|
|
619
596
|
|
|
620
|
-
|
|
621
|
-
|
|
597
|
+
switch (request.type) {
|
|
598
|
+
case exports.EventType.OPEN:
|
|
599
|
+
{
|
|
600
|
+
if (_this._init !== exports.InitState.OK) return;
|
|
601
|
+
const session = {
|
|
602
|
+
sessionId: request.data.sessionId,
|
|
603
|
+
intent: request.data.intent,
|
|
604
|
+
networkId: request.data.networkId
|
|
605
|
+
};
|
|
606
|
+
await _this.open(session);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
622
609
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// note: signingAddress from json-rpc input is in hex format, and typedDataObject
|
|
627
|
-
// should be an object, but in some instances may be double string encoded
|
|
628
|
-
const [signingAddress, typedDataObject] = request.params;
|
|
629
|
-
let typedData = undefined;
|
|
610
|
+
case exports.EventType.CLOSE:
|
|
611
|
+
{
|
|
612
|
+
if (_this._init !== exports.InitState.OK) return; // noop. just here to capture the message so event emitters may be notified
|
|
630
613
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
typedData = JSON.parse(typedDataObject);
|
|
634
|
-
} catch (e) {}
|
|
635
|
-
} else {
|
|
636
|
-
typedData = typedDataObject;
|
|
637
|
-
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
638
616
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
617
|
+
case exports.EventType.MESSAGE:
|
|
618
|
+
{
|
|
619
|
+
const response = await _this.walletRequestHandler.sendMessageRequest(request);
|
|
642
620
|
|
|
643
|
-
|
|
621
|
+
_this.sendMessage(response);
|
|
644
622
|
|
|
645
|
-
|
|
646
|
-
// prompter is null, so we'll sign from here
|
|
647
|
-
sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId);
|
|
648
|
-
} else {
|
|
649
|
-
const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
|
|
623
|
+
if (response.data.error) ;
|
|
650
624
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
chainId: chainId,
|
|
654
|
-
typedData: typedData
|
|
655
|
-
}, _this.connectOptions);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
658
627
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
628
|
+
default:
|
|
629
|
+
{
|
|
630
|
+
utils.logger.error(`unexpected payload type ${request.type}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
};
|
|
668
634
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
const [transactionParams] = request.params;
|
|
673
|
-
let txnHash = '';
|
|
635
|
+
this.sendMessageRequest = async function (message) {
|
|
636
|
+
return _this.walletRequestHandler.sendMessageRequest(message);
|
|
637
|
+
};
|
|
674
638
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
639
|
+
this.open = async function ({
|
|
640
|
+
sessionId,
|
|
641
|
+
intent,
|
|
642
|
+
networkId
|
|
643
|
+
}) {
|
|
644
|
+
if (sessionId) {
|
|
645
|
+
_this._sessionId = utils.sanitizeNumberString(sessionId); // persist transport session in localstorage for restoring after redirect/reload
|
|
683
646
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
647
|
+
_this.saveTransportSession({
|
|
648
|
+
sessionId,
|
|
649
|
+
intent,
|
|
650
|
+
networkId
|
|
651
|
+
});
|
|
652
|
+
}
|
|
690
653
|
|
|
691
|
-
|
|
692
|
-
|
|
654
|
+
_this.walletRequestHandler.setOpenIntent(intent); // init handshake for certain transports, before we can open the communication.
|
|
655
|
+
//
|
|
656
|
+
// for example, with the window-transport, we have to exchange messages to determine the
|
|
657
|
+
// origin host of the dapp.
|
|
693
658
|
|
|
694
|
-
case 'eth_signTransaction':
|
|
695
|
-
{
|
|
696
|
-
// https://eth.wiki/json-rpc/API#eth_signTransaction
|
|
697
|
-
const [transaction] = request.params;
|
|
698
|
-
const sender = ethers.ethers.utils.getAddress(transaction.from);
|
|
699
659
|
|
|
700
|
-
|
|
701
|
-
throw new Error('sender address does not match wallet');
|
|
702
|
-
}
|
|
660
|
+
await _this.init(); // Prepare connect options from intent
|
|
703
661
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
// we will want to resolveProperties the bignumber values to hex strings
|
|
710
|
-
response.result = await signer.signTransactions(transaction, chainId);
|
|
711
|
-
} else {
|
|
712
|
-
response.result = await _this.prompter.promptSignTransaction(transaction, chainId, _this.connectOptions);
|
|
713
|
-
}
|
|
662
|
+
if (intent && intent.type === 'connect' && intent.options) {
|
|
663
|
+
const connectOptions = intent.options;
|
|
664
|
+
const authorizeOptions = connectOptions; // overlapping types
|
|
665
|
+
// Sanity/integrity check the intent payload, and set authorization origin
|
|
666
|
+
// if its been determined as part of the init handshake from earlier.
|
|
714
667
|
|
|
715
|
-
|
|
668
|
+
if (_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
|
|
669
|
+
if (!isBrowserExtension()) {
|
|
670
|
+
if (authorizeOptions.origin !== _this.appOrigin) {
|
|
671
|
+
throw new Error('origin is invalid');
|
|
716
672
|
}
|
|
673
|
+
}
|
|
674
|
+
} else if (!_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
|
|
675
|
+
// ie. when we can't determine the origin in our transport, but dapp provides it to us.
|
|
676
|
+
// we just sanitize the origin host.
|
|
677
|
+
connectOptions.origin = utils.sanitizeHost(authorizeOptions.origin);
|
|
678
|
+
} else if (_this.appOrigin) {
|
|
679
|
+
// ie. when we auto-determine the origin such as in window-transport
|
|
680
|
+
connectOptions.origin = _this.appOrigin;
|
|
681
|
+
}
|
|
717
682
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
// https://eth.wiki/json-rpc/API#eth_sendRawTransaction
|
|
723
|
-
if (transactions.isSignedTransactions(request.params[0])) {
|
|
724
|
-
const txChainId = ethers.BigNumber.from(request.params[0].chainId).toNumber();
|
|
725
|
-
const tx = await (await signer.getRelayer(txChainId)).relay(request.params[0]);
|
|
726
|
-
response.result = (await tx).hash;
|
|
727
|
-
} else {
|
|
728
|
-
const tx = await provider.sendTransaction(request.params[0]);
|
|
729
|
-
response.result = tx.hash;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
break;
|
|
733
|
-
}
|
|
683
|
+
if (connectOptions.app) {
|
|
684
|
+
connectOptions.app = utils.sanitizeAlphanumeric(connectOptions.app);
|
|
685
|
+
} // Set connect options on the walletRequestHandler as our primary
|
|
686
|
+
// wallet controller
|
|
734
687
|
|
|
735
|
-
case 'eth_getTransactionCount':
|
|
736
|
-
{
|
|
737
|
-
const address = ethers.ethers.utils.getAddress(request.params[0]);
|
|
738
|
-
const tag = request.params[1];
|
|
739
|
-
const walletAddress = ethers.ethers.utils.getAddress(await signer.getAddress());
|
|
740
688
|
|
|
741
|
-
|
|
742
|
-
const count = await signer.getTransactionCount(tag);
|
|
743
|
-
response.result = ethers.ethers.BigNumber.from(count).toHexString();
|
|
744
|
-
} else {
|
|
745
|
-
const count = await provider.getTransactionCount(address, tag);
|
|
746
|
-
response.result = ethers.ethers.BigNumber.from(count).toHexString();
|
|
747
|
-
}
|
|
689
|
+
_this.walletRequestHandler.setConnectOptions(connectOptions);
|
|
748
690
|
|
|
749
|
-
|
|
750
|
-
|
|
691
|
+
if (connectOptions.networkId) {
|
|
692
|
+
networkId = connectOptions.networkId;
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
_this.walletRequestHandler.setConnectOptions(undefined);
|
|
696
|
+
} // ensure signer is ready
|
|
751
697
|
|
|
752
|
-
case 'eth_blockNumber':
|
|
753
|
-
{
|
|
754
|
-
response.result = await provider.getBlockNumber();
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
698
|
|
|
758
|
-
|
|
759
|
-
{
|
|
760
|
-
response.result = await provider.getBlock(request.params[0]
|
|
761
|
-
/* , jsonRpcRequest.params[1] */
|
|
762
|
-
);
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
699
|
+
await _this.walletRequestHandler.getSigner(); // Notify open and proceed to prompt for connection if intended
|
|
765
700
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
break;
|
|
772
|
-
}
|
|
701
|
+
if (!(await _this.walletRequestHandler.isSignedIn())) {
|
|
702
|
+
// open wallet without a specific connected chainId, as the user is not signed in
|
|
703
|
+
_this.notifyOpen({
|
|
704
|
+
sessionId: _this._sessionId
|
|
705
|
+
});
|
|
773
706
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
}
|
|
707
|
+
return true;
|
|
708
|
+
} else {
|
|
709
|
+
// Set default network, in case of error chainId will be undefined or 0
|
|
710
|
+
let chainId = undefined;
|
|
779
711
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
712
|
+
try {
|
|
713
|
+
if (networkId) {
|
|
714
|
+
chainId = await _this.walletRequestHandler.setDefaultNetwork(networkId, false);
|
|
715
|
+
} else {
|
|
716
|
+
chainId = await _this.walletRequestHandler.getChainId();
|
|
717
|
+
}
|
|
718
|
+
} catch (err) {
|
|
719
|
+
console.error(err);
|
|
720
|
+
} // Failed to set default network on open -- quit + close
|
|
786
721
|
|
|
787
|
-
case 'eth_getCode':
|
|
788
|
-
{
|
|
789
|
-
const [contractAddress, blockTag] = request.params;
|
|
790
|
-
response.result = await provider.getCode(contractAddress, blockTag);
|
|
791
|
-
break;
|
|
792
|
-
}
|
|
793
722
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}
|
|
723
|
+
if (!chainId || chainId <= 0) {
|
|
724
|
+
_this.notifyOpen({
|
|
725
|
+
sessionId: _this._sessionId,
|
|
726
|
+
error: `failed to open wallet on network`
|
|
727
|
+
});
|
|
800
728
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
break;
|
|
806
|
-
}
|
|
729
|
+
return false;
|
|
730
|
+
} // prompt user with a connect request. the options will be used as previously set above.
|
|
731
|
+
// upon success, the walletRequestHandler will notify the dapp with the ConnectDetails.
|
|
732
|
+
// upon cancellation by user, the walletRequestHandler will throw an error
|
|
807
733
|
|
|
808
|
-
case 'wallet_switchEthereumChain':
|
|
809
|
-
{
|
|
810
|
-
const [switchParams] = request.params;
|
|
811
734
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
735
|
+
if (intent && intent.type === 'connect') {
|
|
736
|
+
// notify wallet is opened, without session details
|
|
737
|
+
_this.notifyOpen({
|
|
738
|
+
sessionId: _this._sessionId
|
|
739
|
+
});
|
|
815
740
|
|
|
816
|
-
|
|
741
|
+
try {
|
|
742
|
+
const connectDetails = await _this.walletRequestHandler.promptConnect(intent.options);
|
|
817
743
|
|
|
818
|
-
|
|
744
|
+
if (connectDetails.connected) {
|
|
745
|
+
_this.walletRequestHandler.notifyConnect(connectDetails);
|
|
746
|
+
}
|
|
747
|
+
} catch (err) {
|
|
748
|
+
utils.logger.warn('promptConnect not connected:', err);
|
|
749
|
+
} finally {
|
|
750
|
+
// auto-close by default, unless intent is to keep open
|
|
751
|
+
if (!intent.options || intent.options.keepWalletOpened !== true) {
|
|
752
|
+
_this.notifyClose();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
} else {
|
|
756
|
+
// user is already connected, notify session details.
|
|
757
|
+
// TODO: in future, keep list if 'connected' dapps / sessions in the session
|
|
758
|
+
// controller, and only sync with allowed apps
|
|
759
|
+
_this.notifyOpen({
|
|
760
|
+
sessionId: _this._sessionId,
|
|
761
|
+
chainId: `${chainId}`,
|
|
762
|
+
session: await _this.walletRequestHandler.walletSession()
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
819
766
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
767
|
+
return true;
|
|
768
|
+
};
|
|
823
769
|
|
|
824
|
-
|
|
770
|
+
this.saveTransportSession = session => {
|
|
771
|
+
LocalStorage.getInstance().setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
|
|
772
|
+
};
|
|
825
773
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
// smart wallet method
|
|
774
|
+
this.getCachedTransportSession = async function () {
|
|
775
|
+
const session = await LocalStorage.getInstance().getItem(TRANSPORT_SESSION_LS_KEY);
|
|
829
776
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
777
|
+
try {
|
|
778
|
+
return session ? JSON.parse(session) : null;
|
|
779
|
+
} catch (err) {
|
|
780
|
+
console.error(`unable to parse transport session: ${session}`);
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
};
|
|
836
784
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
break;
|
|
842
|
-
}
|
|
843
|
-
// smart wallet method
|
|
785
|
+
this.walletRequestHandler = walletRequestHandler;
|
|
786
|
+
this._init = exports.InitState.NIL;
|
|
787
|
+
this.walletRequestHandler.on('connect', connectDetails => {
|
|
788
|
+
if (!this.registered) return; // means user has logged in and wallet is connected to the app
|
|
844
789
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
break;
|
|
850
|
-
}
|
|
851
|
-
// smart wallet method
|
|
790
|
+
this.notifyConnect(connectDetails);
|
|
791
|
+
});
|
|
792
|
+
this.walletRequestHandler.on('disconnect', error => {
|
|
793
|
+
if (!this.registered) return; // means user has logged out the app / disconnected wallet from the app
|
|
852
794
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
795
|
+
this.notifyDisconnect(error);
|
|
796
|
+
});
|
|
797
|
+
this.walletRequestHandler.on('accountsChanged', (accounts, origin) => {
|
|
798
|
+
if (!this.registered) return;
|
|
799
|
+
this.notifyAccountsChanged(accounts, origin);
|
|
800
|
+
});
|
|
801
|
+
this.walletRequestHandler.on('networks', networks => {
|
|
802
|
+
if (!this.registered) return;
|
|
803
|
+
this.notifyNetworks(networks);
|
|
862
804
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
805
|
+
if (!networks || networks.length === 0) {
|
|
806
|
+
this.notifyChainChanged('0x0');
|
|
807
|
+
} else {
|
|
808
|
+
this.notifyChainChanged(ethers.ethers.utils.hexlify(networks.find(network => network.isDefaultChain).chainId));
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
this.walletRequestHandler.on('walletContext', walletContext => {
|
|
812
|
+
if (!this.registered || !walletContext) return;
|
|
813
|
+
this.notifyWalletContext(walletContext);
|
|
814
|
+
});
|
|
815
|
+
this.walletRequestHandler.on('close', error => {
|
|
816
|
+
if (!this.registered) return;
|
|
817
|
+
this.notifyClose(error);
|
|
818
|
+
});
|
|
819
|
+
}
|
|
871
820
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
821
|
+
get registered() {
|
|
822
|
+
return this._registered;
|
|
823
|
+
}
|
|
875
824
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
break;
|
|
880
|
-
}
|
|
881
|
-
// relayer method
|
|
825
|
+
register() {
|
|
826
|
+
throw new Error('abstract method');
|
|
827
|
+
}
|
|
882
828
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
break;
|
|
887
|
-
}
|
|
888
|
-
// relayer method
|
|
829
|
+
unregister() {
|
|
830
|
+
throw new Error('abstract method');
|
|
831
|
+
}
|
|
889
832
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
break;
|
|
894
|
-
}
|
|
895
|
-
// relayer method
|
|
833
|
+
sendMessage(message) {
|
|
834
|
+
throw new Error('abstract method');
|
|
835
|
+
}
|
|
896
836
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
837
|
+
notifyOpen(openInfo) {
|
|
838
|
+
const {
|
|
839
|
+
chainId,
|
|
840
|
+
sessionId,
|
|
841
|
+
session,
|
|
842
|
+
error
|
|
843
|
+
} = openInfo;
|
|
844
|
+
this.sendMessage({
|
|
845
|
+
idx: -1,
|
|
846
|
+
type: exports.EventType.OPEN,
|
|
847
|
+
data: {
|
|
848
|
+
chainId,
|
|
849
|
+
sessionId,
|
|
850
|
+
session,
|
|
851
|
+
error
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
903
855
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
856
|
+
notifyClose(error) {
|
|
857
|
+
this.sendMessage({
|
|
858
|
+
idx: -1,
|
|
859
|
+
type: exports.EventType.CLOSE,
|
|
860
|
+
data: error ? {
|
|
861
|
+
error
|
|
862
|
+
} : null
|
|
863
|
+
});
|
|
864
|
+
}
|
|
910
865
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
866
|
+
notifyConnect(connectDetails) {
|
|
867
|
+
this.sendMessage({
|
|
868
|
+
idx: -1,
|
|
869
|
+
type: exports.EventType.CONNECT,
|
|
870
|
+
data: connectDetails
|
|
871
|
+
});
|
|
872
|
+
}
|
|
914
873
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
874
|
+
notifyDisconnect(error) {
|
|
875
|
+
this.sendMessage({
|
|
876
|
+
idx: -1,
|
|
877
|
+
type: exports.EventType.DISCONNECT,
|
|
878
|
+
data: error ? {
|
|
879
|
+
error
|
|
880
|
+
} : null
|
|
881
|
+
});
|
|
882
|
+
}
|
|
918
883
|
|
|
919
|
-
|
|
884
|
+
notifyAccountsChanged(accounts, origin) {
|
|
885
|
+
this.sendMessage({
|
|
886
|
+
idx: -1,
|
|
887
|
+
type: exports.EventType.ACCOUNTS_CHANGED,
|
|
888
|
+
data: accounts,
|
|
889
|
+
origin: origin
|
|
890
|
+
});
|
|
891
|
+
}
|
|
920
892
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
893
|
+
notifyChainChanged(chainIdHex) {
|
|
894
|
+
this.sendMessage({
|
|
895
|
+
idx: -1,
|
|
896
|
+
type: exports.EventType.CHAIN_CHANGED,
|
|
897
|
+
data: chainIdHex
|
|
898
|
+
});
|
|
899
|
+
}
|
|
924
900
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
901
|
+
notifyNetworks(networks) {
|
|
902
|
+
this.sendMessage({
|
|
903
|
+
idx: -1,
|
|
904
|
+
type: exports.EventType.NETWORKS,
|
|
905
|
+
data: networks
|
|
906
|
+
});
|
|
907
|
+
}
|
|
928
908
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
} catch (err) {
|
|
937
|
-
utils.logger.error(err); // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#rpc-errors
|
|
909
|
+
notifyWalletContext(walletContext) {
|
|
910
|
+
this.sendMessage({
|
|
911
|
+
idx: -1,
|
|
912
|
+
type: exports.EventType.WALLET_CONTEXT,
|
|
913
|
+
data: walletContext
|
|
914
|
+
});
|
|
915
|
+
}
|
|
938
916
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
917
|
+
isValidInitAck(message) {
|
|
918
|
+
if (this._init === exports.InitState.OK) {
|
|
919
|
+
// we're already in init state, we shouldn't handle this message
|
|
920
|
+
utils.logger.warn("isValidInitAck, already in init'd state, so inquiry is invalid.");
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
944
923
|
|
|
945
|
-
|
|
946
|
-
|
|
924
|
+
if (message.type !== exports.EventType.INIT) {
|
|
925
|
+
utils.logger.warn('isValidInitAck, invalid message type, expecting init');
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
947
928
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
};
|
|
929
|
+
const {
|
|
930
|
+
sessionId,
|
|
931
|
+
nonce
|
|
932
|
+
} = message.data;
|
|
952
933
|
|
|
953
|
-
|
|
954
|
-
|
|
934
|
+
if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) {
|
|
935
|
+
utils.logger.error('invalid init ack');
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
955
938
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
939
|
+
if (sessionId !== this._sessionId || nonce !== this._initNonce) {
|
|
940
|
+
utils.logger.error('invalid init ack match');
|
|
941
|
+
return false;
|
|
942
|
+
} // all checks pass, its true
|
|
959
943
|
|
|
960
|
-
return _this.signer;
|
|
961
|
-
};
|
|
962
944
|
|
|
963
|
-
|
|
964
|
-
this.prompter = prompter;
|
|
965
|
-
this.mainnetNetworks = mainnetNetworks;
|
|
966
|
-
this.testnetNetworks = testnetNetworks;
|
|
945
|
+
return true;
|
|
967
946
|
}
|
|
968
947
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
948
|
+
init() {
|
|
949
|
+
return new Promise((resolve, reject) => {
|
|
950
|
+
// avoid re-init`ing, or if there is a transport which doesn't require
|
|
951
|
+
// it, then it may set this._init to OK in its constructor.
|
|
952
|
+
if (this._init === exports.InitState.OK) {
|
|
953
|
+
resolve();
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
977
956
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
957
|
+
if (this._init !== exports.InitState.NIL || this._initCallback) {
|
|
958
|
+
reject('transport init is in progress');
|
|
959
|
+
return;
|
|
960
|
+
} // start init timeout, if we don't receive confirmation
|
|
961
|
+
// from provider within this amount of time, then we timeout
|
|
981
962
|
|
|
982
|
-
if (testnetNetworks && testnetNetworks.length > 0) {
|
|
983
|
-
this.testnetNetworks = testnetNetworks;
|
|
984
|
-
}
|
|
985
963
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
}
|
|
964
|
+
const initTimeout = setTimeout(() => {
|
|
965
|
+
utils.logger.warn('transport init timed out');
|
|
989
966
|
|
|
990
|
-
|
|
967
|
+
if (this._initCallback) {
|
|
968
|
+
this._initCallback('transport init timed out');
|
|
969
|
+
}
|
|
970
|
+
}, PROVIDER_OPEN_TIMEOUT / 2); // setup callback as we receive the init message async in the handleMessage function
|
|
991
971
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`);
|
|
995
|
-
}
|
|
996
|
-
} // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
|
|
997
|
-
// necessary auth request, and then notify the dapp of the 'connect' details.
|
|
998
|
-
//
|
|
999
|
-
// NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
|
|
1000
|
-
// we don't trigger the promptConnect flow, as we consider the user just authenticated
|
|
1001
|
-
// for this dapp, so its safe to authorize in the connect() method without the prompt.
|
|
1002
|
-
//
|
|
1003
|
-
// NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
|
|
972
|
+
this._initCallback = error => {
|
|
973
|
+
this._initCallback = undefined; // reset
|
|
1004
974
|
|
|
975
|
+
clearTimeout(initTimeout);
|
|
1005
976
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
977
|
+
if (error) {
|
|
978
|
+
reject(error);
|
|
979
|
+
} else {
|
|
980
|
+
this._init = exports.InitState.OK;
|
|
981
|
+
resolve();
|
|
982
|
+
}
|
|
983
|
+
}; // send init request with random nonce to the provider, where we expect
|
|
984
|
+
// for the provider to echo it back to us as complete handshake
|
|
1010
985
|
|
|
1011
|
-
if (!connectOptions || connectOptions.keepWalletOpened !== true) {
|
|
1012
|
-
this.notifyClose();
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
986
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
987
|
+
this._initNonce = `${performance.now()}`;
|
|
988
|
+
this.sendMessage({
|
|
989
|
+
idx: -1,
|
|
990
|
+
type: exports.EventType.INIT,
|
|
991
|
+
data: {
|
|
992
|
+
nonce: this._initNonce
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
this._init = exports.InitState.SENT_NONCE; // NOTE: the promise will resolve in the _initCallback method
|
|
996
|
+
// which will be called from either handleMessage or the initTimeout
|
|
997
|
+
});
|
|
1020
998
|
}
|
|
1021
999
|
|
|
1022
|
-
|
|
1023
|
-
// resetting signer puts the wallet in an uninitialized state, which requires the app to
|
|
1024
|
-
// re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
|
|
1025
|
-
this.signer = undefined;
|
|
1026
|
-
}
|
|
1000
|
+
}
|
|
1027
1001
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
}, timeout);
|
|
1039
|
-
this.signerReadyCallbacks.push(resolve);
|
|
1040
|
-
}
|
|
1041
|
-
});
|
|
1002
|
+
class ProxyMessageChannel {
|
|
1003
|
+
constructor() {
|
|
1004
|
+
this.app = void 0;
|
|
1005
|
+
this.wallet = void 0;
|
|
1006
|
+
const port1 = new ProxyMessageChannelPort();
|
|
1007
|
+
const port2 = new ProxyMessageChannelPort();
|
|
1008
|
+
port1.conn = port2;
|
|
1009
|
+
port2.conn = port1;
|
|
1010
|
+
this.app = port1;
|
|
1011
|
+
this.wallet = port2;
|
|
1042
1012
|
}
|
|
1043
1013
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
error: 'unable to connect without signed in account'
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1014
|
+
}
|
|
1015
|
+
class ProxyMessageChannelPort {
|
|
1016
|
+
constructor() {
|
|
1017
|
+
this.conn = void 0;
|
|
1018
|
+
this.events = new eventemitter2.EventEmitter2();
|
|
1052
1019
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
chainId: ethers.ethers.utils.hexlify(await this.getChainId())
|
|
1020
|
+
this.handleMessage = message => {
|
|
1021
|
+
throw new Error('ProxyMessageChannelPort is not registered');
|
|
1056
1022
|
};
|
|
1057
1023
|
|
|
1058
|
-
|
|
1059
|
-
//
|
|
1060
|
-
// including the auth proof
|
|
1061
|
-
const authOptions = {
|
|
1062
|
-
app: options.app,
|
|
1063
|
-
origin: options.origin,
|
|
1064
|
-
expiry: options.expiry
|
|
1065
|
-
}; // if (typeof(options.authorize) === 'object') {
|
|
1066
|
-
// authOptions = { ...authOptions, ...options.authorize }
|
|
1067
|
-
// }
|
|
1068
|
-
|
|
1069
|
-
try {
|
|
1070
|
-
connectDetails.proof = await auth.signAuthorization(this.signer, authOptions);
|
|
1071
|
-
} catch (err) {
|
|
1072
|
-
utils.logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
|
|
1073
|
-
return {
|
|
1074
|
-
connected: false,
|
|
1075
|
-
chainId: '0x0',
|
|
1076
|
-
error: `signAuthorization failed: ${err.message}`
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
} // Build session response for connect details
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
connectDetails.session = await this.walletSession();
|
|
1083
|
-
return connectDetails;
|
|
1084
|
-
}
|
|
1024
|
+
this.sendMessage = message => {
|
|
1025
|
+
this.conn.handleMessage(message); // trigger events
|
|
1085
1026
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
sendMessageRequest(message) {
|
|
1090
|
-
return new Promise(resolve => {
|
|
1091
|
-
this.sendAsync(message.data, (error, response) => {
|
|
1092
|
-
// TODO: if response includes data.error, why do we need a separate error argument here?
|
|
1093
|
-
const responseMessage = _extends({}, message, {
|
|
1094
|
-
data: response
|
|
1095
|
-
}); // NOTE: we always resolve here, are the sendAsync call will wrap any exceptions
|
|
1096
|
-
// in the error field of the response to ensure we send back to the user
|
|
1027
|
+
if (message.type === 'open') {
|
|
1028
|
+
this.events.emit('open', message);
|
|
1029
|
+
}
|
|
1097
1030
|
|
|
1031
|
+
if (message.type === 'close') {
|
|
1032
|
+
this.events.emit('close', message);
|
|
1033
|
+
}
|
|
1098
1034
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
} // sendAsync implements the JsonRpcHandler interface for sending JsonRpcRequests to the wallet
|
|
1035
|
+
if (message.type === 'connect') {
|
|
1036
|
+
this.events.emit('connect', message);
|
|
1037
|
+
}
|
|
1103
1038
|
|
|
1039
|
+
if (message.type === 'disconnect') {
|
|
1040
|
+
this.events.emit('disconnect', message);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1104
1044
|
|
|
1105
1045
|
on(event, fn) {
|
|
1106
1046
|
this.events.on(event, fn);
|
|
@@ -1110,2063 +1050,2157 @@ class WalletRequestHandler {
|
|
|
1110
1050
|
this.events.once(event, fn);
|
|
1111
1051
|
}
|
|
1112
1052
|
|
|
1113
|
-
|
|
1114
|
-
if (!this.signer) {
|
|
1115
|
-
return '';
|
|
1116
|
-
} else {
|
|
1117
|
-
return this.signer.getAddress();
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1053
|
+
}
|
|
1120
1054
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
if (this._chainId) return this._chainId; // memoized
|
|
1055
|
+
class ProxyMessageProvider extends BaseProviderTransport {
|
|
1056
|
+
constructor(port) {
|
|
1057
|
+
super();
|
|
1058
|
+
this.port = void 0;
|
|
1126
1059
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1060
|
+
this.register = () => {
|
|
1061
|
+
this.port.handleMessage = message => {
|
|
1062
|
+
this.handleMessage(message);
|
|
1063
|
+
};
|
|
1131
1064
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1065
|
+
this.on('open', (...args) => {
|
|
1066
|
+
this.port.events.emit('open', ...args);
|
|
1067
|
+
});
|
|
1068
|
+
this.on('close', (...args) => {
|
|
1069
|
+
this.port.events.emit('close', ...args);
|
|
1070
|
+
});
|
|
1071
|
+
this.on('connect', (...args) => {
|
|
1072
|
+
this.port.events.emit('connect', ...args);
|
|
1073
|
+
});
|
|
1074
|
+
this.on('disconnect', (...args) => {
|
|
1075
|
+
this.port.events.emit('disconnect', ...args);
|
|
1076
|
+
});
|
|
1077
|
+
this._registered = true;
|
|
1078
|
+
};
|
|
1135
1079
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1080
|
+
this.unregister = () => {
|
|
1081
|
+
this._registered = false;
|
|
1082
|
+
this.closeWallet();
|
|
1083
|
+
this.events.removeAllListeners(); // @ts-ignore
|
|
1139
1084
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
}
|
|
1085
|
+
this.port.handleMessage = undefined;
|
|
1086
|
+
};
|
|
1143
1087
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1088
|
+
this.openWallet = (path, intent, networkId) => {
|
|
1089
|
+
if (this.state === exports.OpenState.CLOSED) {
|
|
1090
|
+
this.state = exports.OpenState.OPENING;
|
|
1091
|
+
const sessionId = `${performance.now()}`;
|
|
1092
|
+
this._sessionId = sessionId;
|
|
1093
|
+
this.sendMessage({
|
|
1094
|
+
idx: -1,
|
|
1095
|
+
type: exports.EventType.OPEN,
|
|
1096
|
+
data: {
|
|
1097
|
+
path,
|
|
1098
|
+
intent,
|
|
1099
|
+
networkId,
|
|
1100
|
+
sessionId
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1147
1105
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1106
|
+
this.state = exports.OpenState.CLOSED;
|
|
1107
|
+
this.port = port;
|
|
1151
1108
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1109
|
+
if (!port) {
|
|
1110
|
+
throw new Error('port argument cannot be empty');
|
|
1111
|
+
} // disable init handshake for proxy-transport, we set it to OK, to
|
|
1112
|
+
// consider it in completed state.
|
|
1156
1113
|
|
|
1157
|
-
if (this.signer && this.signer.setNetworks) {
|
|
1158
|
-
const defaultChainId = this.signer.setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId);
|
|
1159
1114
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
}
|
|
1115
|
+
this._init = exports.InitState.OK;
|
|
1116
|
+
}
|
|
1163
1117
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1118
|
+
closeWallet() {
|
|
1119
|
+
this.sendMessage({
|
|
1120
|
+
idx: -1,
|
|
1121
|
+
type: exports.EventType.CLOSE,
|
|
1122
|
+
data: null
|
|
1123
|
+
});
|
|
1124
|
+
this.close();
|
|
1168
1125
|
}
|
|
1169
1126
|
|
|
1170
|
-
|
|
1171
|
-
if (!
|
|
1172
|
-
|
|
1173
|
-
return [];
|
|
1127
|
+
sendMessage(message) {
|
|
1128
|
+
if (!message.idx) {
|
|
1129
|
+
throw new Error('message idx is empty');
|
|
1174
1130
|
}
|
|
1175
1131
|
|
|
1176
|
-
|
|
1132
|
+
this.port.sendMessage(message);
|
|
1133
|
+
}
|
|
1177
1134
|
|
|
1178
|
-
|
|
1179
|
-
// omit provider and relayer objects as they are not serializable
|
|
1180
|
-
return networks.map(n => {
|
|
1181
|
-
const network = _extends({}, n);
|
|
1135
|
+
}
|
|
1182
1136
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1137
|
+
class ProxyMessageHandler extends BaseWalletTransport {
|
|
1138
|
+
constructor(walletRequestHandler, port) {
|
|
1139
|
+
super(walletRequestHandler);
|
|
1140
|
+
this.port = void 0;
|
|
1141
|
+
this.port = port;
|
|
1142
|
+
this._init = exports.InitState.OK;
|
|
1190
1143
|
}
|
|
1191
1144
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
accountAddress: await this.signer.getAddress(),
|
|
1196
|
-
networks: await this.getNetworks(true)
|
|
1145
|
+
register() {
|
|
1146
|
+
this.port.handleMessage = message => {
|
|
1147
|
+
this.handleMessage(message);
|
|
1197
1148
|
};
|
|
1198
|
-
}
|
|
1199
1149
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1150
|
+
this._registered = true;
|
|
1151
|
+
} // note: we can't decide whether to restore the session within register(), because session info is
|
|
1152
|
+
// received asyncronously via EventType.OPEN after register() is executed.
|
|
1153
|
+
// And in the case of a redirect/reload, EventType.OPEN is not sent at all,
|
|
1154
|
+
// because the wallet is already open.
|
|
1155
|
+
//
|
|
1156
|
+
// call this method from wallet redirect hander when a session restore is needed
|
|
1202
1157
|
|
|
1203
|
-
this.events.emit('connect', connectDetails);
|
|
1204
1158
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1159
|
+
async restoreSession() {
|
|
1160
|
+
const cachedSession = await this.getCachedTransportSession();
|
|
1207
1161
|
|
|
1208
|
-
|
|
1162
|
+
if (cachedSession) {
|
|
1163
|
+
this.open(cachedSession);
|
|
1209
1164
|
}
|
|
1210
1165
|
}
|
|
1211
1166
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
this.
|
|
1167
|
+
unregister() {
|
|
1168
|
+
// @ts-ignore
|
|
1169
|
+
this.port.handleMessage = undefined;
|
|
1170
|
+
this._registered = false;
|
|
1215
1171
|
}
|
|
1216
1172
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
this.events.emit('networks', n);
|
|
1220
|
-
|
|
1221
|
-
if (n.length > 0) {
|
|
1222
|
-
const defaultNetwork = n.find(network => network.isDefaultChain);
|
|
1223
|
-
|
|
1224
|
-
if (defaultNetwork) {
|
|
1225
|
-
this.events.emit('chainChanged', ethers.ethers.utils.hexlify(defaultNetwork.chainId));
|
|
1226
|
-
}
|
|
1227
|
-
} else {
|
|
1228
|
-
this.events.emit('chainChanged', '0x0');
|
|
1229
|
-
}
|
|
1173
|
+
sendMessage(message) {
|
|
1174
|
+
this.port.sendMessage(message);
|
|
1230
1175
|
}
|
|
1231
1176
|
|
|
1232
|
-
|
|
1233
|
-
if (!this.signer) {
|
|
1234
|
-
utils.logger.warn('signer not set: skipping to notify wallet context');
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1177
|
+
}
|
|
1237
1178
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1179
|
+
class MuxMessageProvider {
|
|
1180
|
+
constructor(...messageProviders) {
|
|
1181
|
+
var _this = this;
|
|
1241
1182
|
|
|
1242
|
-
|
|
1243
|
-
this.
|
|
1244
|
-
}
|
|
1183
|
+
this.messageProviders = void 0;
|
|
1184
|
+
this.provider = void 0;
|
|
1245
1185
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1186
|
+
this.register = () => {
|
|
1187
|
+
if (this.messageProviders.length === 1) {
|
|
1188
|
+
this.provider = this.messageProviders[0];
|
|
1189
|
+
this.provider.register();
|
|
1190
|
+
return;
|
|
1191
|
+
} // REVIEW/NOTE: ........ this method does not work for the chrome-extension. The issue becomes
|
|
1192
|
+
// when the browser quits or restarts, the "open" event is never triggered. Perhaps the code here is fine,
|
|
1193
|
+
// or maybe its not. What should happen is when a dapp makes a request, it will call openWallet
|
|
1194
|
+
// below, in which case one of the events will register. So perhaps this is fine.
|
|
1248
1195
|
|
|
1249
|
-
if (signer !== undefined) {
|
|
1250
|
-
for (let i = 0; i < this.signerReadyCallbacks.length; i++) {
|
|
1251
|
-
this.signerReadyCallbacks[i]();
|
|
1252
|
-
}
|
|
1253
1196
|
|
|
1254
|
-
this.
|
|
1255
|
-
|
|
1256
|
-
|
|
1197
|
+
this.messageProviders.forEach(m => {
|
|
1198
|
+
m.register();
|
|
1199
|
+
m.once('open', () => {
|
|
1200
|
+
// the first one to open is the winner, and others will be unregistered
|
|
1201
|
+
if (!this.provider) {
|
|
1202
|
+
this.provider = m; // unregister other providers
|
|
1257
1203
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1204
|
+
this.messageProviders.forEach(m => {
|
|
1205
|
+
if (this.provider !== m) {
|
|
1206
|
+
m.unregister();
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
this.unregister = () => {
|
|
1215
|
+
this.messageProviders.forEach(m => m.unregister());
|
|
1216
|
+
this.provider = undefined;
|
|
1217
|
+
};
|
|
1264
1218
|
|
|
1265
|
-
|
|
1219
|
+
this.openWallet = (path, intent, networkId) => {
|
|
1220
|
+
if (this.provider) {
|
|
1221
|
+
this.provider.openWallet(path, intent, networkId);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1266
1224
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
}
|
|
1225
|
+
this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
|
|
1226
|
+
};
|
|
1270
1227
|
|
|
1271
|
-
|
|
1228
|
+
this.sendAsync = async function (request, callback, chainId) {
|
|
1229
|
+
if (_this.provider) {
|
|
1230
|
+
_this.provider.sendAsync(request, callback, chainId);
|
|
1272
1231
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1275
1234
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1235
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
this.sendMessageRequest = async function (message) {
|
|
1239
|
+
if (_this.provider) {
|
|
1240
|
+
return _this.provider.sendMessageRequest(message);
|
|
1281
1241
|
}
|
|
1282
|
-
}
|
|
1283
1242
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1243
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
1244
|
+
};
|
|
1286
1245
|
|
|
1287
|
-
|
|
1288
|
-
|
|
1246
|
+
this.waitUntilOpened = async function () {
|
|
1247
|
+
if (_this.provider) {
|
|
1248
|
+
return _this.provider.waitUntilOpened();
|
|
1249
|
+
}
|
|
1289
1250
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
return isSequenceProvider(cand);
|
|
1293
|
-
}
|
|
1251
|
+
return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
|
|
1252
|
+
};
|
|
1294
1253
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
this._sender = void 0;
|
|
1300
|
-
this._isSequenceProvider = void 0;
|
|
1301
|
-
this._defaultChainId = void 0;
|
|
1302
|
-
this._sender = sender;
|
|
1303
|
-
this._isSequenceProvider = true;
|
|
1304
|
-
this._defaultChainId = network.maybeChainId(defaultChainId);
|
|
1305
|
-
}
|
|
1254
|
+
this.waitUntilConnected = async function () {
|
|
1255
|
+
if (_this.provider) {
|
|
1256
|
+
return _this.provider.waitUntilConnected();
|
|
1257
|
+
}
|
|
1306
1258
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
}
|
|
1259
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
1260
|
+
};
|
|
1310
1261
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1262
|
+
this.messageProviders = messageProviders;
|
|
1263
|
+
this.provider = undefined;
|
|
1313
1264
|
}
|
|
1314
1265
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1266
|
+
add(...messageProviders) {
|
|
1267
|
+
this.messageProviders.push(...messageProviders);
|
|
1317
1268
|
}
|
|
1318
1269
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1270
|
+
closeWallet() {
|
|
1271
|
+
if (this.provider) {
|
|
1272
|
+
this.provider.closeWallet();
|
|
1273
|
+
}
|
|
1321
1274
|
}
|
|
1322
1275
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
const chainId = ethers.ethers.BigNumber.from(result).toNumber();
|
|
1327
|
-
|
|
1328
|
-
if (this._defaultChainId && this._defaultChainId !== chainId) {
|
|
1329
|
-
throw new Error(`provider chainId (${chainId}) does not match provider-bound chainId ${this._defaultChainId}`);
|
|
1276
|
+
isOpened() {
|
|
1277
|
+
if (this.provider) {
|
|
1278
|
+
return this.provider.isOpened();
|
|
1330
1279
|
}
|
|
1331
1280
|
|
|
1332
|
-
return
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
}
|
|
1336
|
-
function isSequenceProvider(provider) {
|
|
1337
|
-
const cand = provider;
|
|
1338
|
-
return cand && cand.send !== undefined && cand._isSequenceProvider === true;
|
|
1339
|
-
}
|
|
1340
|
-
class LocalWeb3Provider extends Web3Provider {
|
|
1341
|
-
constructor(signer, networks) {
|
|
1342
|
-
const walletRequestHandler = new WalletRequestHandler(signer, null, networks || []);
|
|
1343
|
-
super(walletRequestHandler);
|
|
1281
|
+
return false;
|
|
1344
1282
|
}
|
|
1345
1283
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
this.provider = void 0;
|
|
1351
|
-
this.defaultChainId = void 0;
|
|
1352
|
-
this._address = void 0;
|
|
1353
|
-
this._index = void 0;
|
|
1354
|
-
this._context = void 0;
|
|
1355
|
-
this._networks = void 0;
|
|
1356
|
-
this._providers = {};
|
|
1357
|
-
this.provider = provider;
|
|
1358
|
-
this.defaultChainId = defaultChainId;
|
|
1359
|
-
} // memoized
|
|
1360
|
-
|
|
1284
|
+
isConnected() {
|
|
1285
|
+
if (this.provider) {
|
|
1286
|
+
return this.provider.isConnected();
|
|
1287
|
+
}
|
|
1361
1288
|
|
|
1362
|
-
|
|
1363
|
-
// ethers AbstractSigner methods
|
|
1364
|
-
//
|
|
1365
|
-
async getAddress() {
|
|
1366
|
-
if (this._address) return this._address;
|
|
1367
|
-
const accounts = await this.provider.send('eth_accounts', []);
|
|
1368
|
-
this._address = accounts[0];
|
|
1369
|
-
this._index = 0;
|
|
1370
|
-
return ethers.ethers.utils.getAddress(this._address);
|
|
1289
|
+
return false;
|
|
1371
1290
|
}
|
|
1372
1291
|
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1292
|
+
on(event, fn) {
|
|
1293
|
+
if (this.provider) {
|
|
1294
|
+
this.provider.on(event, fn);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
this.messageProviders.forEach(m => {
|
|
1299
|
+
m.on(event, fn);
|
|
1300
|
+
});
|
|
1376
1301
|
}
|
|
1377
1302
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
// getProvider returns a Web3Provider instance for the current chain. Note that this method
|
|
1384
|
-
// and signer is bound to a particular chain to prevent misuse. If you'd like a provider
|
|
1385
|
-
// for a specific chain, try getSender(chainId), or wallet.getProvider(chainId).
|
|
1303
|
+
once(event, fn) {
|
|
1304
|
+
if (this.provider) {
|
|
1305
|
+
this.provider.once(event, fn);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1386
1308
|
|
|
1309
|
+
this.messageProviders.forEach(m => {
|
|
1310
|
+
m.once(event, fn);
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1387
1313
|
|
|
1388
|
-
|
|
1389
|
-
if (
|
|
1390
|
-
|
|
1314
|
+
emit(event, ...args) {
|
|
1315
|
+
if (this.provider) {
|
|
1316
|
+
return this.provider.emit(event, ...args);
|
|
1317
|
+
}
|
|
1391
1318
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
}
|
|
1319
|
+
for (let i = 0; i < this.messageProviders.length; i++) {
|
|
1320
|
+
this.messageProviders[i].emit(event, ...args);
|
|
1395
1321
|
}
|
|
1396
1322
|
|
|
1397
|
-
return
|
|
1398
|
-
}
|
|
1399
|
-
// the of sequence wallet, this will bring up the wallet window whenever using it, as the json-rpc
|
|
1400
|
-
// requests are sent to the window transport. Therefore, for anything non-signing related
|
|
1401
|
-
// you can write a higher-order JsonRpcRouter sender to route to the public provider endpoints
|
|
1402
|
-
// as we do in the WalletProvider.
|
|
1403
|
-
//
|
|
1404
|
-
// This method is primarily utilized internally when routing requests to a particular chainId.
|
|
1323
|
+
return true;
|
|
1324
|
+
}
|
|
1405
1325
|
|
|
1326
|
+
sendMessage(message) {
|
|
1327
|
+
if (!message.idx || message.idx <= 0) {
|
|
1328
|
+
throw new Error('message idx is empty');
|
|
1329
|
+
}
|
|
1406
1330
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1331
|
+
if (this.provider) {
|
|
1332
|
+
this.provider.sendMessage(message);
|
|
1333
|
+
} else {
|
|
1334
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
1410
1335
|
}
|
|
1336
|
+
}
|
|
1411
1337
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1338
|
+
handleMessage(message) {
|
|
1339
|
+
if (this.provider) {
|
|
1340
|
+
this.provider.handleMessage(message);
|
|
1341
|
+
return;
|
|
1414
1342
|
}
|
|
1415
1343
|
|
|
1416
|
-
|
|
1344
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
1417
1345
|
}
|
|
1418
1346
|
|
|
1419
|
-
|
|
1420
|
-
// TODO: JsonRpcRelayer ......? or, Web3Relayer.. or SequenceRelayer?
|
|
1421
|
-
// sequence_estimateGasLimits
|
|
1422
|
-
// sequence_gasRefundOptions
|
|
1423
|
-
// sequence_getNonce
|
|
1424
|
-
// sequence_relay
|
|
1425
|
-
throw new Error('TODO');
|
|
1426
|
-
}
|
|
1347
|
+
}
|
|
1427
1348
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1349
|
+
let registeredWindowMessageProvider;
|
|
1350
|
+
class WindowMessageProvider extends BaseProviderTransport {
|
|
1351
|
+
constructor(walletAppURL) {
|
|
1352
|
+
super();
|
|
1353
|
+
this.walletURL = void 0;
|
|
1354
|
+
this.walletWindow = void 0;
|
|
1432
1355
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1356
|
+
this.register = () => {
|
|
1357
|
+
/* eslint-disable */
|
|
1358
|
+
if (registeredWindowMessageProvider) {
|
|
1359
|
+
// overriding the registered message provider
|
|
1360
|
+
registeredWindowMessageProvider.unregister();
|
|
1361
|
+
registeredWindowMessageProvider = this;
|
|
1362
|
+
} // listen for incoming messages from wallet
|
|
1435
1363
|
|
|
1436
|
-
async getWalletConfig(chainId) {
|
|
1437
|
-
return await this.provider.send('sequence_getWalletConfig', [network.maybeChainId(chainId)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
1438
|
-
}
|
|
1439
1364
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1365
|
+
window.addEventListener('message', this.onWindowEvent);
|
|
1366
|
+
registeredWindowMessageProvider = this;
|
|
1367
|
+
/* eslint-enable */
|
|
1368
|
+
// open heartbeat
|
|
1443
1369
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1370
|
+
this.on('open', () => {
|
|
1371
|
+
// Heartbeat to track if window closed
|
|
1372
|
+
const popup = this.walletWindow;
|
|
1373
|
+
const interval = setInterval(() => {
|
|
1374
|
+
if (popup && popup.closed) {
|
|
1375
|
+
clearInterval(interval);
|
|
1376
|
+
this.close();
|
|
1377
|
+
}
|
|
1378
|
+
}, 500);
|
|
1379
|
+
}); // close clean up
|
|
1448
1380
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1381
|
+
this.on('close', () => {
|
|
1382
|
+
if (this.walletWindow) {
|
|
1383
|
+
this.walletWindow.close();
|
|
1384
|
+
this.walletWindow = null;
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
this._registered = true;
|
|
1388
|
+
};
|
|
1451
1389
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1390
|
+
this.unregister = () => {
|
|
1391
|
+
this._registered = false;
|
|
1392
|
+
this.closeWallet(); // disable message listener
|
|
1455
1393
|
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1394
|
+
if (registeredWindowMessageProvider === this) {
|
|
1395
|
+
registeredWindowMessageProvider = undefined;
|
|
1396
|
+
}
|
|
1459
1397
|
|
|
1460
|
-
|
|
1398
|
+
window.removeEventListener('message', this.onWindowEvent); // clear event listeners
|
|
1461
1399
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
}
|
|
1400
|
+
this.events.removeAllListeners();
|
|
1401
|
+
};
|
|
1465
1402
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1403
|
+
this.openWallet = (path, intent, networkId) => {
|
|
1404
|
+
if (this.walletWindow && this.isOpened()) {
|
|
1405
|
+
// TODO: update the location of window to path
|
|
1406
|
+
this.walletWindow.focus();
|
|
1407
|
+
return;
|
|
1408
|
+
} // Instantiate new walletURL for this call
|
|
1469
1409
|
|
|
1470
1410
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
const data = typeof message === 'string' ? ethers.ethers.utils.toUtf8Bytes(message) : message;
|
|
1474
|
-
const address = await this.getAddress(); // NOTE: as of ethers v5.5, it switched to using personal_sign, see
|
|
1475
|
-
// https://github.com/ethers-io/ethers.js/pull/1542 and see
|
|
1476
|
-
// https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info.
|
|
1411
|
+
const walletURL = new URL(this.walletURL.href);
|
|
1412
|
+
const windowSessionParams = new WindowSessionParams();
|
|
1477
1413
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1414
|
+
if (path && path !== '') {
|
|
1415
|
+
walletURL.pathname = path.toLowerCase();
|
|
1416
|
+
} // Set session, intent and network id on walletURL
|
|
1481
1417
|
|
|
1482
1418
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
// return this.provider.resolveName(name)
|
|
1487
|
-
// })
|
|
1488
|
-
return await this.provider.send('eth_signTypedData_v4', [await this.getAddress(), ethers.ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
1489
|
-
} // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with
|
|
1490
|
-
// multi-chain support.
|
|
1419
|
+
this._init = exports.InitState.NIL;
|
|
1420
|
+
this._sessionId = `${performance.now()}`;
|
|
1421
|
+
windowSessionParams.set('sid', this._sessionId);
|
|
1491
1422
|
|
|
1423
|
+
if (intent) {
|
|
1424
|
+
// for the window-transport, we eagerly/optimistically set the origin host
|
|
1425
|
+
// when connecting to the wallet, however, this will be verified and enforced
|
|
1426
|
+
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
|
|
1427
|
+
if (intent.type === 'connect') {
|
|
1428
|
+
if (!intent.options) intent.options = {}; // skip setting origin host if we're in an browser extension execution context
|
|
1429
|
+
// allow origin that is passed in
|
|
1492
1430
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
const tx = this.sendUncheckedTransaction(transaction, chainId).then(hash => {
|
|
1496
|
-
return ethers.ethers.utils.poll(() => {
|
|
1497
|
-
return provider.getTransaction(hash).then(tx => {
|
|
1498
|
-
if (tx === null) {
|
|
1499
|
-
return undefined;
|
|
1431
|
+
if (!isBrowserExtension()) {
|
|
1432
|
+
intent.options.origin = window.location.origin;
|
|
1500
1433
|
}
|
|
1434
|
+
} // encode intent as base64 url-encoded param
|
|
1501
1435
|
|
|
1502
|
-
return provider._wrapTransaction(tx, hash);
|
|
1503
|
-
});
|
|
1504
|
-
}, {
|
|
1505
|
-
onceBlock: this.provider
|
|
1506
|
-
}).catch(error => {
|
|
1507
|
-
error.transactionHash = hash;
|
|
1508
|
-
throw error;
|
|
1509
|
-
});
|
|
1510
|
-
}); // @ts-ignore
|
|
1511
|
-
|
|
1512
|
-
return tx;
|
|
1513
|
-
} // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to
|
|
1514
|
-
// send multiple transaction as a single payload and just one on-chain transaction.
|
|
1515
1436
|
|
|
1437
|
+
windowSessionParams.set('intent', utils.base64EncodeObject(intent));
|
|
1438
|
+
}
|
|
1516
1439
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1440
|
+
if (networkId) {
|
|
1441
|
+
windowSessionParams.set('net', `${networkId}`);
|
|
1442
|
+
} // Open popup window on center of the app window
|
|
1519
1443
|
|
|
1520
|
-
if (!batch || batch.length === 0) {
|
|
1521
|
-
throw new Error('cannot send empty batch');
|
|
1522
|
-
} // sendTransactionBatch only accepts TransactionRequest, not TransactionResponses
|
|
1523
1444
|
|
|
1445
|
+
let windowSize;
|
|
1446
|
+
let windowPos;
|
|
1524
1447
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1448
|
+
if (isBrowserExtension()) {
|
|
1449
|
+
windowSize = [450, 750];
|
|
1450
|
+
windowPos = [Math.abs(window.screen.width / 2 - windowSize[0] / 2), Math.abs(window.screen.height / 2 - windowSize[1] / 2)];
|
|
1451
|
+
} else {
|
|
1452
|
+
windowSize = [450, 750];
|
|
1453
|
+
windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
|
|
1454
|
+
}
|
|
1528
1455
|
|
|
1529
|
-
|
|
1456
|
+
const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`; // serialize params
|
|
1530
1457
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1458
|
+
walletURL.search = windowSessionParams.toString();
|
|
1459
|
+
this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures); // TODO: move this somewhere else
|
|
1460
|
+
// TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
|
|
1461
|
+
// Popup blocking detection and notice
|
|
1462
|
+
// let warned = false
|
|
1463
|
+
// const warnPopupBlocked = () => {
|
|
1464
|
+
// if (warned) return
|
|
1465
|
+
// warned = true
|
|
1466
|
+
// // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
|
|
1467
|
+
// throw new Error('popup is blocked')
|
|
1468
|
+
// }
|
|
1469
|
+
// const popupCheck = setTimeout(() => {
|
|
1470
|
+
// if (!popup || popup.closed || typeof popup.closed === 'undefined') {
|
|
1471
|
+
// // popup is definitely blocked if we reach here.
|
|
1472
|
+
// warnPopupBlocked()
|
|
1473
|
+
// }
|
|
1474
|
+
// }, 1000)
|
|
1475
|
+
// const popupBlocked = popup === null || popup === undefined
|
|
1476
|
+
// if (popupBlocked) {
|
|
1477
|
+
// warnPopupBlocked()
|
|
1478
|
+
// return
|
|
1479
|
+
// }
|
|
1480
|
+
};
|
|
1534
1481
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1482
|
+
this.onWindowEvent = event => {
|
|
1483
|
+
// Security check, ensure message is coming from wallet origin url
|
|
1484
|
+
if (event.origin !== this.walletURL.origin) {
|
|
1485
|
+
// Safetly can skip events not from the wallet
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1537
1488
|
|
|
1538
|
-
|
|
1539
|
-
transaction = utils.shallowCopy(transaction); // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over
|
|
1540
|
-
// the wire.. see sendUncheckedTransaction and resolveProperties
|
|
1489
|
+
let message;
|
|
1541
1490
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1491
|
+
try {
|
|
1492
|
+
message = JSON.parse(event.data);
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
// event is not a ProviderMessage JSON object, skip
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1544
1497
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
} // updateConfig..
|
|
1549
|
-
// NOTE: this is not supported by the remote wallet by default.
|
|
1498
|
+
if (!message) {
|
|
1499
|
+
throw new Error('ProviderMessage object is empty');
|
|
1500
|
+
} // handle message with base message provider
|
|
1550
1501
|
|
|
1551
1502
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
|
|
1503
|
+
this.handleMessage(message);
|
|
1504
|
+
};
|
|
1555
1505
|
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
}
|
|
1506
|
+
this.walletURL = new URL(walletAppURL);
|
|
1507
|
+
}
|
|
1559
1508
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
} // publishConfig..
|
|
1563
|
-
// NOTE: this is not supported by the remote wallet by default.
|
|
1509
|
+
closeWallet() {
|
|
1510
|
+
var _this$walletWindow;
|
|
1564
1511
|
|
|
1512
|
+
this.close();
|
|
1513
|
+
(_this$walletWindow = this.walletWindow) == null ? void 0 : _this$walletWindow.close();
|
|
1514
|
+
} // onmessage, receives ProviderMessageResponse from the wallet post-message transport
|
|
1565
1515
|
|
|
1566
|
-
async publishConfig() {
|
|
1567
|
-
const provider = await this.getSender(this.defaultChainId);
|
|
1568
|
-
const tx = await provider.send('sequence_publishConfig', []);
|
|
1569
1516
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1517
|
+
sendMessage(message) {
|
|
1518
|
+
if (!this.walletWindow) {
|
|
1519
|
+
utils.logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
|
|
1520
|
+
return;
|
|
1572
1521
|
}
|
|
1573
1522
|
|
|
1574
|
-
|
|
1523
|
+
const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message;
|
|
1524
|
+
this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
|
|
1575
1525
|
}
|
|
1576
1526
|
|
|
1577
|
-
|
|
1578
|
-
const provider = await this.getSender(network.maybeChainId(chainId));
|
|
1579
|
-
const walletCode = await provider.getCode(await this.getAddress());
|
|
1580
|
-
return !!walletCode && walletCode !== '0x';
|
|
1581
|
-
} //
|
|
1582
|
-
// ethers JsonRpcSigner methods
|
|
1583
|
-
//
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
async _legacySignMessage(message, chainId, allSigners) {
|
|
1587
|
-
const provider = await this.getSender(network.maybeChainId(chainId) || this.defaultChainId);
|
|
1588
|
-
const data = typeof message === 'string' ? ethers.ethers.utils.toUtf8Bytes(message) : message;
|
|
1589
|
-
const address = await this.getAddress(); // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
|
|
1590
|
-
// NOTE: ethers since 5.5 has switched to using personal_sign, we should review, etc.
|
|
1591
|
-
|
|
1592
|
-
return await provider.send('eth_sign', [address, ethers.ethers.utils.hexlify(data)]);
|
|
1593
|
-
}
|
|
1527
|
+
}
|
|
1594
1528
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1529
|
+
class WindowMessageHandler extends BaseWalletTransport {
|
|
1530
|
+
constructor(walletRequestHandler) {
|
|
1531
|
+
var _this;
|
|
1598
1532
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
// TODO: alternatively/one day, we could write a provider middleware to eth_estimateGas
|
|
1604
|
-
// and send it to our relayer url instead for estimation..
|
|
1605
|
-
//
|
|
1606
|
-
// if (!transaction.gasLimit) {
|
|
1607
|
-
// const estimate = shallowCopy(transaction)
|
|
1608
|
-
// estimate.from = fromAddress
|
|
1609
|
-
// transaction.gasLimit = this.provider.estimateGas(estimate)
|
|
1610
|
-
// }
|
|
1533
|
+
super(walletRequestHandler);
|
|
1534
|
+
_this = this;
|
|
1535
|
+
this.parentWindow = void 0;
|
|
1536
|
+
this._isPopup = false;
|
|
1611
1537
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
}).then(({
|
|
1617
|
-
tx,
|
|
1618
|
-
sender
|
|
1619
|
-
}) => {
|
|
1620
|
-
if (tx.from != null) {
|
|
1621
|
-
if (ethers.ethers.utils.getAddress(tx.from) !== sender) {
|
|
1622
|
-
// logger.throwArgumentError("from address mismatch", "transaction", transaction)
|
|
1623
|
-
throw new Error(`from address mismatch for transaction ${transaction}`);
|
|
1624
|
-
}
|
|
1625
|
-
} else {
|
|
1626
|
-
tx.from = sender;
|
|
1538
|
+
this.onWindowEvent = async function (event) {
|
|
1539
|
+
if (!event.origin || event.origin === '') {
|
|
1540
|
+
// skip same-origin or when event.origin is empty/undefined
|
|
1541
|
+
return;
|
|
1627
1542
|
}
|
|
1628
1543
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
return
|
|
1632
|
-
}
|
|
1633
|
-
// return checkError("sendTransaction", error, hexTx)
|
|
1634
|
-
throw new Error(`sendTransaction ${error}`);
|
|
1635
|
-
});
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
connectUnchecked() {
|
|
1640
|
-
throw new Error('connectUnchecked is unsupported');
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
async unlock(password) {
|
|
1644
|
-
const address = await this.getAddress();
|
|
1645
|
-
return this.provider.send('personal_unlockAccount', [address, password, null]);
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
} // NOTE: method has been copied + modified from ethers.js JsonRpcProvider
|
|
1649
|
-
// Convert an ethers.js transaction into a JSON-RPC transaction
|
|
1544
|
+
if (_this.appOrigin && event.origin !== _this.appOrigin) {
|
|
1545
|
+
// skip message as not from expected app origin
|
|
1546
|
+
return;
|
|
1547
|
+
} // Wallet always expects json-rpc request messages from a dapp
|
|
1650
1548
|
|
|
1651
|
-
const allowedTransactionKeys = {
|
|
1652
|
-
chainId: true,
|
|
1653
|
-
data: true,
|
|
1654
|
-
gasLimit: true,
|
|
1655
|
-
gasPrice: true,
|
|
1656
|
-
nonce: true,
|
|
1657
|
-
to: true,
|
|
1658
|
-
value: true,
|
|
1659
|
-
from: true,
|
|
1660
|
-
auxiliary: true,
|
|
1661
|
-
expiration: true,
|
|
1662
|
-
afterNonce: true,
|
|
1663
|
-
delegateCall: true,
|
|
1664
|
-
revertOnError: true
|
|
1665
|
-
};
|
|
1666
1549
|
|
|
1667
|
-
|
|
1668
|
-
// Check only allowed properties are given
|
|
1669
|
-
const allowed = utils.shallowCopy(allowedTransactionKeys);
|
|
1550
|
+
let request;
|
|
1670
1551
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1552
|
+
try {
|
|
1553
|
+
request = JSON.parse(event.data);
|
|
1554
|
+
} catch (err) {
|
|
1555
|
+
// event is not a ProviderMessage JSON object, skip
|
|
1556
|
+
return;
|
|
1675
1557
|
}
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
ethers.ethers.utils.checkProperties(transaction, allowed);
|
|
1680
|
-
const result = {} // Some nodes (INFURA ropsten; INFURA mainnet is fine) do not like leading zeros.
|
|
1681
|
-
;
|
|
1682
|
-
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(key => {
|
|
1683
|
-
if (!transaction[key]) {
|
|
1684
|
-
return;
|
|
1685
|
-
}
|
|
1686
1558
|
|
|
1687
|
-
|
|
1559
|
+
utils.logger.debug('RECEIVED MESSAGE', request); // Record event origin for valid init ack
|
|
1688
1560
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1561
|
+
if (_this._init !== exports.InitState.OK && _this.isValidInitAck(request)) {
|
|
1562
|
+
_this.appOrigin = event.origin;
|
|
1563
|
+
}
|
|
1692
1564
|
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
}
|
|
1565
|
+
if (_this._init === exports.InitState.OK && (!_this.appOrigin || _this.appOrigin.length < 8)) {
|
|
1566
|
+
// impossible state
|
|
1567
|
+
utils.logger.error('impossible state, init.OK and appOrigin required');
|
|
1568
|
+
return;
|
|
1569
|
+
} // Handle message via the base transport
|
|
1699
1570
|
|
|
1700
|
-
result[key] = ethers.ethers.utils.hexlify(transaction[key]);
|
|
1701
|
-
});
|
|
1702
|
-
const auxiliary = transaction['auxiliary'];
|
|
1703
1571
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
auxiliary.forEach(a => {
|
|
1707
|
-
result['auxiliary'].push(hexlifyTransaction(a));
|
|
1708
|
-
});
|
|
1709
|
-
}
|
|
1572
|
+
_this.handleMessage(request);
|
|
1573
|
+
};
|
|
1710
1574
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1575
|
+
this.getWindowTransportSession = windowParams => {
|
|
1576
|
+
const params = new WindowSessionParams(windowParams);
|
|
1577
|
+
return {
|
|
1578
|
+
sessionId: params.get('sid'),
|
|
1579
|
+
networkId: params.get('net'),
|
|
1580
|
+
intent: utils.base64DecodeObject(params.get('intent'))
|
|
1581
|
+
};
|
|
1582
|
+
};
|
|
1713
1583
|
|
|
1714
|
-
|
|
1715
|
-
const messageToBytes = message => {
|
|
1716
|
-
if (ethers.ethers.utils.isBytes(message) || ethers.ethers.utils.isHexString(message)) {
|
|
1717
|
-
return ethers.ethers.utils.arrayify(message);
|
|
1584
|
+
this._init = exports.InitState.NIL;
|
|
1718
1585
|
}
|
|
1719
1586
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const messageBytes = messageToBytes(message);
|
|
1724
|
-
return ethers.ethers.utils.concat([eip191prefix, ethers.ethers.utils.toUtf8Bytes(String(messageBytes.length)), messageBytes]);
|
|
1725
|
-
};
|
|
1726
|
-
const isValidSignature = async (address, digest, sig, provider, chainId, walletContext) => {
|
|
1727
|
-
if (!chainId) {
|
|
1728
|
-
var _await$provider$getNe;
|
|
1587
|
+
async register(windowHref) {
|
|
1588
|
+
const isPopup = parent.window.opener !== null;
|
|
1589
|
+
this._isPopup = isPopup;
|
|
1729
1590
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1591
|
+
if (isPopup !== true) {
|
|
1592
|
+
return;
|
|
1593
|
+
} // record open details (sessionId + default network) from the window url
|
|
1732
1594
|
|
|
1733
|
-
if (!walletContext && Web3Provider.isSequenceProvider(provider)) {
|
|
1734
|
-
walletContext = await provider.getSigner().getWalletContext();
|
|
1735
|
-
}
|
|
1736
1595
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
return isValidSignature(address, digest, signature, provider, chainId, walletContext);
|
|
1743
|
-
};
|
|
1744
|
-
const isValidTypedDataSignature = (address, typedData, signature, provider, chainId, walletContext) => {
|
|
1745
|
-
return isValidSignature(address, utils.encodeTypedDataDigest(typedData), signature, provider, chainId, walletContext);
|
|
1746
|
-
};
|
|
1747
|
-
const recoverWalletConfig = async (address, digest, signature, chainId, walletContext) => {
|
|
1748
|
-
const subDigest = utils.packMessageData(address, chainId, digest);
|
|
1749
|
-
const config$1 = await wallet.recoverConfig(subDigest, signature);
|
|
1596
|
+
const {
|
|
1597
|
+
pathname,
|
|
1598
|
+
search: rawParams
|
|
1599
|
+
} = new URL(windowHref || window.location.href);
|
|
1600
|
+
let session = this.getWindowTransportSession(rawParams); // provider should always include sid when opening a new window
|
|
1750
1601
|
|
|
1751
|
-
|
|
1752
|
-
const recoveredWalletAddress = config.addressOf(config$1, walletContext);
|
|
1602
|
+
const isNewWindowSession = !!session.sessionId; // attempt to restore previous session in the case of a redirect or window reload
|
|
1753
1603
|
|
|
1754
|
-
if (
|
|
1755
|
-
|
|
1756
|
-
} else {
|
|
1757
|
-
config$1.address = recoveredWalletAddress;
|
|
1604
|
+
if (!isNewWindowSession) {
|
|
1605
|
+
session = await this.getCachedTransportSession();
|
|
1758
1606
|
}
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
return config$1;
|
|
1762
|
-
};
|
|
1763
|
-
const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:';
|
|
1764
|
-
/**
|
|
1765
|
-
* Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
|
|
1766
|
-
*
|
|
1767
|
-
* @param {Signer} signer
|
|
1768
|
-
* @param {number} chainId
|
|
1769
|
-
* @return {Promise<boolean>} Promise that returns true if the wallet is up to date, false otherwise
|
|
1770
|
-
*/
|
|
1771
1607
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1608
|
+
if (!session) {
|
|
1609
|
+
utils.logger.error('window session is undefined');
|
|
1610
|
+
return;
|
|
1611
|
+
} // record parent window instance for communication
|
|
1774
1612
|
|
|
1775
|
-
const walletState = await signer.getWalletState();
|
|
1776
|
-
const networks = await signer.getNetworks();
|
|
1777
|
-
const walletStateForRequiredChain = walletState.find(state => state.chainId === chainId);
|
|
1778
1613
|
|
|
1779
|
-
|
|
1780
|
-
throw new Error(`WalletRequestHandler: could not find wallet state for chainId ${chainId}`);
|
|
1781
|
-
}
|
|
1614
|
+
this.parentWindow = parent.window.opener; // listen for window-transport requests
|
|
1782
1615
|
|
|
1783
|
-
|
|
1616
|
+
window.addEventListener('message', this.onWindowEvent, false);
|
|
1617
|
+
this._registered = true; // send open event to the app which opened us
|
|
1784
1618
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1619
|
+
this.open(session).then(opened => {
|
|
1620
|
+
if (!opened) {
|
|
1621
|
+
var _session;
|
|
1788
1622
|
|
|
1789
|
-
|
|
1623
|
+
const err = `failed to open to network ${(_session = session) == null ? void 0 : _session.networkId}`;
|
|
1624
|
+
utils.logger.error(err);
|
|
1625
|
+
this.notifyClose({
|
|
1626
|
+
message: err
|
|
1627
|
+
});
|
|
1628
|
+
window.close();
|
|
1629
|
+
}
|
|
1630
|
+
}).catch(e => {
|
|
1631
|
+
var _session2;
|
|
1790
1632
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1633
|
+
const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
|
|
1634
|
+
utils.logger.error(err);
|
|
1635
|
+
this.notifyClose({
|
|
1636
|
+
message: err
|
|
1637
|
+
});
|
|
1638
|
+
window.close();
|
|
1639
|
+
});
|
|
1793
1640
|
}
|
|
1794
1641
|
|
|
1795
|
-
|
|
1796
|
-
|
|
1642
|
+
unregister() {
|
|
1643
|
+
window.removeEventListener('message', this.onWindowEvent);
|
|
1644
|
+
this._registered = false;
|
|
1645
|
+
} // onmessage is called when (the wallet) receives request messages from the dapp
|
|
1646
|
+
// over the window post-messaging transport
|
|
1797
1647
|
|
|
1798
|
-
if (!authChainConfig) {
|
|
1799
|
-
throw new Error(`isWalletUpToDate util: could not get auth chain config`);
|
|
1800
|
-
}
|
|
1801
1648
|
|
|
1802
|
-
|
|
1649
|
+
// postMessage sends message to the dapp window
|
|
1650
|
+
sendMessage(message) {
|
|
1651
|
+
// prepare payload
|
|
1652
|
+
const payload = JSON.stringify(message); // post-message to app.
|
|
1653
|
+
// only for init requests, we send to '*' origin
|
|
1803
1654
|
|
|
1804
|
-
|
|
1805
|
-
|
|
1655
|
+
if (message.type === exports.EventType.INIT) {
|
|
1656
|
+
this.postMessage(payload, true);
|
|
1657
|
+
} else {
|
|
1658
|
+
this.postMessage(payload);
|
|
1659
|
+
}
|
|
1806
1660
|
}
|
|
1807
1661
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
}
|
|
1811
|
-
class LocalStorage {
|
|
1812
|
-
constructor() {}
|
|
1662
|
+
get isPopup() {
|
|
1663
|
+
return this._isPopup;
|
|
1664
|
+
}
|
|
1813
1665
|
|
|
1814
|
-
|
|
1815
|
-
if (
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
setItem: (key, value) => Promise.resolve(window.localStorage.setItem(key, value)),
|
|
1819
|
-
removeItem: key => Promise.resolve(window.localStorage.removeItem(key))
|
|
1820
|
-
};
|
|
1666
|
+
postMessage(message, init = false) {
|
|
1667
|
+
if (init !== true && this._init !== exports.InitState.OK) {
|
|
1668
|
+
utils.logger.error('impossible state, should not be calling postMessage until inited');
|
|
1669
|
+
return;
|
|
1821
1670
|
}
|
|
1822
1671
|
|
|
1823
|
-
|
|
1672
|
+
if (init) {
|
|
1673
|
+
// init message transmission to global target -- for 'init' payloads only
|
|
1674
|
+
this.parentWindow.postMessage(message, '*');
|
|
1675
|
+
} else {
|
|
1676
|
+
// open message transmission
|
|
1677
|
+
if (this.appOrigin && this.appOrigin.length > 4) {
|
|
1678
|
+
// just above '.com'
|
|
1679
|
+
this.parentWindow.postMessage(message, this.appOrigin);
|
|
1680
|
+
} else {
|
|
1681
|
+
utils.logger.error('unable to postMessage as parentOrigin is invalid');
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1824
1684
|
}
|
|
1825
1685
|
|
|
1826
|
-
|
|
1827
|
-
LocalStorage._instance = instance;
|
|
1828
|
-
}
|
|
1686
|
+
}
|
|
1829
1687
|
|
|
1830
|
-
|
|
1688
|
+
const SIGNER_READY_TIMEOUT = 10000;
|
|
1689
|
+
class WalletRequestHandler {
|
|
1690
|
+
// signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined
|
|
1691
|
+
// value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course
|
|
1692
|
+
// a Signer value is the actually interface to a signed-in account
|
|
1693
|
+
constructor(_signer, prompter, mainnetNetworks, testnetNetworks = []) {
|
|
1694
|
+
var _this = this;
|
|
1831
1695
|
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
this.
|
|
1836
|
-
this.
|
|
1837
|
-
this.
|
|
1838
|
-
|
|
1696
|
+
this.signer = void 0;
|
|
1697
|
+
this.signerReadyCallbacks = [];
|
|
1698
|
+
this.prompter = void 0;
|
|
1699
|
+
this.mainnetNetworks = void 0;
|
|
1700
|
+
this.testnetNetworks = void 0;
|
|
1701
|
+
this._openIntent = void 0;
|
|
1702
|
+
this._connectOptions = void 0;
|
|
1703
|
+
this._defaultNetworkId = void 0;
|
|
1704
|
+
this._chainId = void 0;
|
|
1705
|
+
this.events = new eventemitter2.EventEmitter2();
|
|
1839
1706
|
|
|
1840
|
-
|
|
1841
|
-
|
|
1707
|
+
this.promptConnect = async function (options) {
|
|
1708
|
+
if (!options && !_this._connectOptions) {
|
|
1709
|
+
// this is an unexpected state and should not happen
|
|
1710
|
+
throw new Error('prompter connect options are empty');
|
|
1711
|
+
}
|
|
1842
1712
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1713
|
+
if (!_this.prompter) {
|
|
1714
|
+
// if prompter is null, we'll auto connect
|
|
1715
|
+
return _this.connect(options);
|
|
1716
|
+
}
|
|
1846
1717
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1718
|
+
const promptConnectDetails = await _this.prompter.promptConnect(options || _this._connectOptions).catch(_ => {
|
|
1719
|
+
return {
|
|
1720
|
+
connected: false
|
|
1721
|
+
};
|
|
1722
|
+
});
|
|
1723
|
+
const connectDetails = promptConnectDetails;
|
|
1852
1724
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1725
|
+
if (connectDetails.connected && !connectDetails.session) {
|
|
1726
|
+
connectDetails.session = await _this.walletSession();
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
return promptConnectDetails;
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
this.sendAsync = async function (request, callback, chainId) {
|
|
1733
|
+
var _request$params, _request$params2, _request$params3, _request$params4, _request$params6;
|
|
1855
1734
|
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1735
|
+
const response = {
|
|
1736
|
+
jsonrpc: '2.0',
|
|
1737
|
+
id: request.id,
|
|
1738
|
+
result: null
|
|
1739
|
+
};
|
|
1740
|
+
await _this.getSigner();
|
|
1859
1741
|
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1742
|
+
try {
|
|
1743
|
+
// only allow public json rpc method to the provider when user is not logged in, aka signer is not set
|
|
1744
|
+
if ((!_this.signer || _this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) {
|
|
1745
|
+
// throw new Error(`not logged in. ${request.method} is unavailable`)
|
|
1746
|
+
throw ErrSignedInRequired;
|
|
1747
|
+
} // wallet signer
|
|
1863
1748
|
|
|
1864
|
-
}
|
|
1865
1749
|
|
|
1866
|
-
const
|
|
1867
|
-
|
|
1868
|
-
// appOrigin identifies the dapp's origin which opened the app. A transport
|
|
1869
|
-
// will auto-detect and set this value if it can. This is determined
|
|
1870
|
-
// as the parent app/window which opened the wallet.
|
|
1871
|
-
constructor(walletRequestHandler) {
|
|
1872
|
-
var _this = this;
|
|
1750
|
+
const signer = _this.signer;
|
|
1751
|
+
if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured'); // fetch the provider for the specific chain, or undefined will select defaultChain
|
|
1873
1752
|
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
this._registered = void 0;
|
|
1877
|
-
this._init = void 0;
|
|
1878
|
-
this._initNonce = void 0;
|
|
1879
|
-
this._initCallback = void 0;
|
|
1880
|
-
this.appOrigin = void 0;
|
|
1753
|
+
const provider = await signer.getProvider(chainId);
|
|
1754
|
+
if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`);
|
|
1881
1755
|
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1756
|
+
switch (request.method) {
|
|
1757
|
+
case 'net_version':
|
|
1758
|
+
{
|
|
1759
|
+
const result = await provider.send('net_version', []);
|
|
1760
|
+
response.result = result;
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1885
1763
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1764
|
+
case 'eth_chainId':
|
|
1765
|
+
{
|
|
1766
|
+
const result = await provider.send('eth_chainId', []);
|
|
1767
|
+
response.result = result;
|
|
1768
|
+
break;
|
|
1769
|
+
}
|
|
1889
1770
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
// failed init
|
|
1897
|
-
if (_this._initCallback) _this._initCallback('invalid init');
|
|
1898
|
-
return;
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1771
|
+
case 'eth_accounts':
|
|
1772
|
+
{
|
|
1773
|
+
const walletAddress = await signer.getAddress();
|
|
1774
|
+
response.result = [walletAddress];
|
|
1775
|
+
break;
|
|
1776
|
+
}
|
|
1901
1777
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1778
|
+
case 'eth_getBalance':
|
|
1779
|
+
{
|
|
1780
|
+
const [accountAddress, blockTag] = request.params;
|
|
1781
|
+
const walletBalance = await provider.getBalance(accountAddress, blockTag);
|
|
1782
|
+
response.result = walletBalance.toHexString();
|
|
1783
|
+
break;
|
|
1784
|
+
}
|
|
1908
1785
|
|
|
1786
|
+
case 'eth_sign':
|
|
1787
|
+
case 'personal_sign':
|
|
1788
|
+
case 'sequence_sign':
|
|
1789
|
+
{
|
|
1790
|
+
// note: message from json-rpc input is in hex format
|
|
1791
|
+
let message;
|
|
1792
|
+
let options; // there is a difference in the order of the params:
|
|
1793
|
+
// eth_sign: [address, data]
|
|
1794
|
+
// personal_sign: [data, address]
|
|
1795
|
+
// sequence_sign: [data, address, options]
|
|
1909
1796
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
const session = {
|
|
1915
|
-
sessionId: request.data.sessionId,
|
|
1916
|
-
intent: request.data.intent,
|
|
1917
|
-
networkId: request.data.networkId
|
|
1918
|
-
};
|
|
1919
|
-
await _this.open(session);
|
|
1920
|
-
return;
|
|
1921
|
-
}
|
|
1797
|
+
switch (request.method) {
|
|
1798
|
+
case 'eth_sign':
|
|
1799
|
+
message = (_request$params = request.params) == null ? void 0 : _request$params[1];
|
|
1800
|
+
break;
|
|
1922
1801
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1802
|
+
case 'personal_sign':
|
|
1803
|
+
message = (_request$params2 = request.params) == null ? void 0 : _request$params2[0];
|
|
1804
|
+
break;
|
|
1926
1805
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1806
|
+
case 'sequence_sign':
|
|
1807
|
+
message = (_request$params3 = request.params) == null ? void 0 : _request$params3[0];
|
|
1808
|
+
options = (_request$params4 = request.params) == null ? void 0 : _request$params4[2];
|
|
1809
|
+
break;
|
|
1810
|
+
}
|
|
1929
1811
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
const response = await _this.walletRequestHandler.sendMessageRequest(request);
|
|
1812
|
+
let sig = ''; // Message must be prefixed with "\x19Ethereum Signed Message:\n"
|
|
1813
|
+
// as defined by EIP-191
|
|
1933
1814
|
|
|
1934
|
-
|
|
1815
|
+
message = wallet.prefixEIP191Message(message); // TODO:
|
|
1816
|
+
// if (process.env.TEST_MODE === 'true' && this.prompter === null) {
|
|
1935
1817
|
|
|
1936
|
-
|
|
1818
|
+
if (_this.prompter === null) {
|
|
1819
|
+
// prompter is null, so we'll sign from here
|
|
1820
|
+
sig = await signer.signMessage(ethers.ethers.utils.arrayify(message), chainId);
|
|
1821
|
+
} else {
|
|
1822
|
+
if (typeof options === 'object' && options.counterfactual === true) {
|
|
1823
|
+
sig = await _this.prompter.promptSignMessage({
|
|
1824
|
+
chainId,
|
|
1825
|
+
message
|
|
1826
|
+
}, _this.connectOptions);
|
|
1827
|
+
} else {
|
|
1828
|
+
const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
|
|
1829
|
+
|
|
1830
|
+
if (promptResultForDeployment) {
|
|
1831
|
+
sig = await _this.prompter.promptSignMessage({
|
|
1832
|
+
chainId,
|
|
1833
|
+
message
|
|
1834
|
+
}, _this.connectOptions);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1937
1838
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1839
|
+
if (sig && sig.length > 0) {
|
|
1840
|
+
response.result = sig;
|
|
1841
|
+
} else {
|
|
1842
|
+
// The user has declined the request when value is null
|
|
1843
|
+
throw new Error('declined by user');
|
|
1844
|
+
}
|
|
1940
1845
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
utils.logger.error(`unexpected payload type ${request.type}`);
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
};
|
|
1846
|
+
break;
|
|
1847
|
+
}
|
|
1947
1848
|
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1849
|
+
case 'eth_signTypedData':
|
|
1850
|
+
case 'eth_signTypedData_v4':
|
|
1851
|
+
case 'sequence_signTypedData_v4':
|
|
1852
|
+
{
|
|
1853
|
+
var _request$params5;
|
|
1951
1854
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
}) {
|
|
1957
|
-
if (sessionId) {
|
|
1958
|
-
_this._sessionId = utils.sanitizeNumberString(sessionId); // persist transport session in localstorage for restoring after redirect/reload
|
|
1855
|
+
// note: signingAddress from json-rpc input is in hex format, and typedDataObject
|
|
1856
|
+
// should be an object, but in some instances may be double string encoded
|
|
1857
|
+
const typedDataObject = (_request$params5 = request.params) == null ? void 0 : _request$params5[1];
|
|
1858
|
+
let options;
|
|
1959
1859
|
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
}
|
|
1860
|
+
switch (request.method) {
|
|
1861
|
+
case 'sequence_signTypedData_v4':
|
|
1862
|
+
options = (_request$params6 = request.params) == null ? void 0 : _request$params6[2];
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1966
1865
|
|
|
1967
|
-
|
|
1968
|
-
//
|
|
1969
|
-
// for example, with the window-transport, we have to exchange messages to determine the
|
|
1970
|
-
// origin host of the dapp.
|
|
1866
|
+
let typedData = undefined;
|
|
1971
1867
|
|
|
1868
|
+
if (typeof typedDataObject === 'string') {
|
|
1869
|
+
try {
|
|
1870
|
+
typedData = JSON.parse(typedDataObject);
|
|
1871
|
+
} catch (e) {}
|
|
1872
|
+
} else {
|
|
1873
|
+
typedData = typedDataObject;
|
|
1874
|
+
}
|
|
1972
1875
|
|
|
1973
|
-
|
|
1876
|
+
if (!typedData || !typedData.domain || !typedData.types || !typedData.message) {
|
|
1877
|
+
throw new Error('invalid typedData object');
|
|
1878
|
+
}
|
|
1974
1879
|
|
|
1975
|
-
|
|
1976
|
-
const connectOptions = intent.options;
|
|
1977
|
-
const authorizeOptions = connectOptions; // overlapping types
|
|
1978
|
-
// Sanity/integrity check the intent payload, and set authorization origin
|
|
1979
|
-
// if its been determined as part of the init handshake from earlier.
|
|
1880
|
+
let sig = '';
|
|
1980
1881
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1882
|
+
if (_this.prompter === null) {
|
|
1883
|
+
// prompter is null, so we'll sign from here
|
|
1884
|
+
sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId);
|
|
1885
|
+
} else {
|
|
1886
|
+
if (typeof options === 'object' && options.counterfactual === true) {
|
|
1887
|
+
sig = await _this.prompter.promptSignMessage({
|
|
1888
|
+
chainId,
|
|
1889
|
+
typedData
|
|
1890
|
+
}, _this.connectOptions);
|
|
1891
|
+
} else {
|
|
1892
|
+
const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
|
|
1893
|
+
|
|
1894
|
+
if (promptResultForDeployment) {
|
|
1895
|
+
sig = await _this.prompter.promptSignMessage({
|
|
1896
|
+
chainId,
|
|
1897
|
+
typedData
|
|
1898
|
+
}, _this.connectOptions);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1995
1902
|
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
1903
|
+
if (sig && sig.length > 0) {
|
|
1904
|
+
response.result = sig;
|
|
1905
|
+
} else {
|
|
1906
|
+
// The user has declined the request when value is null
|
|
1907
|
+
throw new Error('declined by user');
|
|
1908
|
+
}
|
|
2000
1909
|
|
|
1910
|
+
break;
|
|
1911
|
+
}
|
|
2001
1912
|
|
|
2002
|
-
|
|
1913
|
+
case 'eth_sendTransaction':
|
|
1914
|
+
{
|
|
1915
|
+
// https://eth.wiki/json-rpc/API#eth_sendtransaction
|
|
1916
|
+
const [transactionParams] = request.params;
|
|
1917
|
+
let txnHash = '';
|
|
2003
1918
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
1919
|
+
if (_this.prompter === null) {
|
|
1920
|
+
// prompter is null, so we'll send from here
|
|
1921
|
+
const txnResponse = await signer.sendTransaction(transactionParams, chainId);
|
|
1922
|
+
txnHash = txnResponse.hash;
|
|
1923
|
+
} else {
|
|
1924
|
+
// prompt user to provide the response
|
|
1925
|
+
txnHash = await _this.prompter.promptSendTransaction(transactionParams, chainId, _this.connectOptions);
|
|
1926
|
+
}
|
|
2010
1927
|
|
|
1928
|
+
if (txnHash) {
|
|
1929
|
+
response.result = txnHash;
|
|
1930
|
+
} else {
|
|
1931
|
+
// The user has declined the request when value is null
|
|
1932
|
+
throw new Error('declined by user');
|
|
1933
|
+
}
|
|
2011
1934
|
|
|
2012
|
-
|
|
1935
|
+
break;
|
|
1936
|
+
}
|
|
2013
1937
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
1938
|
+
case 'eth_signTransaction':
|
|
1939
|
+
{
|
|
1940
|
+
// https://eth.wiki/json-rpc/API#eth_signTransaction
|
|
1941
|
+
const [transaction] = request.params;
|
|
1942
|
+
const sender = ethers.ethers.utils.getAddress(transaction.from);
|
|
2019
1943
|
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
let chainId = undefined;
|
|
1944
|
+
if (sender !== (await signer.getAddress())) {
|
|
1945
|
+
throw new Error('sender address does not match wallet');
|
|
1946
|
+
}
|
|
2024
1947
|
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
1948
|
+
if (_this.prompter === null) {
|
|
1949
|
+
// The eth_signTransaction method expects a `string` return value we instead return a `SignedTransactions` object,
|
|
1950
|
+
// this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one.
|
|
1951
|
+
//
|
|
1952
|
+
// TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however
|
|
1953
|
+
// we will want to resolveProperties the bignumber values to hex strings
|
|
1954
|
+
response.result = await signer.signTransactions(transaction, chainId);
|
|
1955
|
+
} else {
|
|
1956
|
+
response.result = await _this.prompter.promptSignTransaction(transaction, chainId, _this.connectOptions);
|
|
1957
|
+
}
|
|
2034
1958
|
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
2035
1961
|
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
1962
|
+
case 'eth_sendRawTransaction':
|
|
1963
|
+
{
|
|
1964
|
+
// NOTE: we're not using a prompter here as the transaction is already signed
|
|
1965
|
+
// and would have prompted the user upon signing.
|
|
1966
|
+
// https://eth.wiki/json-rpc/API#eth_sendRawTransaction
|
|
1967
|
+
if (transactions.isSignedTransactionBundle(request.params[0])) {
|
|
1968
|
+
const result = await signer.sendSignedTransactions(request.params[0], chainId);
|
|
1969
|
+
response.result = result.hash;
|
|
1970
|
+
} else {
|
|
1971
|
+
const tx = await provider.sendTransaction(request.params[0]);
|
|
1972
|
+
response.result = tx.hash;
|
|
1973
|
+
}
|
|
2041
1974
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
// upon success, the walletRequestHandler will notify the dapp with the ConnectDetails.
|
|
2045
|
-
// upon cancellation by user, the walletRequestHandler will throw an error
|
|
1975
|
+
break;
|
|
1976
|
+
}
|
|
2046
1977
|
|
|
1978
|
+
case 'eth_getTransactionCount':
|
|
1979
|
+
{
|
|
1980
|
+
const address = ethers.ethers.utils.getAddress(request.params[0]);
|
|
1981
|
+
const tag = request.params[1];
|
|
1982
|
+
const walletAddress = ethers.ethers.utils.getAddress(await signer.getAddress());
|
|
2047
1983
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1984
|
+
if (address === walletAddress) {
|
|
1985
|
+
const count = await signer.getTransactionCount(tag);
|
|
1986
|
+
response.result = ethers.ethers.BigNumber.from(count).toHexString();
|
|
1987
|
+
} else {
|
|
1988
|
+
const count = await provider.getTransactionCount(address, tag);
|
|
1989
|
+
response.result = ethers.ethers.BigNumber.from(count).toHexString();
|
|
1990
|
+
}
|
|
2053
1991
|
|
|
2054
|
-
|
|
2055
|
-
|
|
1992
|
+
break;
|
|
1993
|
+
}
|
|
2056
1994
|
|
|
2057
|
-
|
|
2058
|
-
|
|
1995
|
+
case 'eth_blockNumber':
|
|
1996
|
+
{
|
|
1997
|
+
response.result = await provider.getBlockNumber();
|
|
1998
|
+
break;
|
|
2059
1999
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2000
|
+
|
|
2001
|
+
case 'eth_getBlockByNumber':
|
|
2002
|
+
{
|
|
2003
|
+
response.result = await provider.getBlock(request.params[0]
|
|
2004
|
+
/* , jsonRpcRequest.params[1] */
|
|
2005
|
+
);
|
|
2006
|
+
break;
|
|
2066
2007
|
}
|
|
2067
|
-
}
|
|
2068
|
-
} else {
|
|
2069
|
-
// user is already connected, notify session details.
|
|
2070
|
-
// TODO: in future, keep list if 'connected' dapps / sessions in the session
|
|
2071
|
-
// controller, and only sync with allowed apps
|
|
2072
|
-
_this.notifyOpen({
|
|
2073
|
-
sessionId: _this._sessionId,
|
|
2074
|
-
chainId: `${chainId}`,
|
|
2075
|
-
session: await _this.walletRequestHandler.walletSession()
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
2008
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2009
|
+
case 'eth_getBlockByHash':
|
|
2010
|
+
{
|
|
2011
|
+
response.result = await provider.getBlock(request.params[0]
|
|
2012
|
+
/* , jsonRpcRequest.params[1] */
|
|
2013
|
+
);
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2082
2016
|
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2017
|
+
case 'eth_getTransactionByHash':
|
|
2018
|
+
{
|
|
2019
|
+
response.result = await provider.getTransaction(request.params[0]);
|
|
2020
|
+
break;
|
|
2021
|
+
}
|
|
2086
2022
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2023
|
+
case 'eth_call':
|
|
2024
|
+
{
|
|
2025
|
+
const [transactionObject, blockTag] = request.params;
|
|
2026
|
+
response.result = await provider.call(transactionObject, blockTag);
|
|
2027
|
+
break;
|
|
2028
|
+
}
|
|
2089
2029
|
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
};
|
|
2030
|
+
case 'eth_getCode':
|
|
2031
|
+
{
|
|
2032
|
+
const [contractAddress, blockTag] = request.params;
|
|
2033
|
+
response.result = await provider.getCode(contractAddress, blockTag);
|
|
2034
|
+
break;
|
|
2035
|
+
}
|
|
2097
2036
|
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2037
|
+
case 'eth_estimateGas':
|
|
2038
|
+
{
|
|
2039
|
+
const [transactionObject] = request.params;
|
|
2040
|
+
response.result = await provider.estimateGas(transactionObject);
|
|
2041
|
+
break;
|
|
2042
|
+
}
|
|
2102
2043
|
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2044
|
+
case 'eth_gasPrice':
|
|
2045
|
+
{
|
|
2046
|
+
const gasPrice = await provider.getGasPrice();
|
|
2047
|
+
response.result = gasPrice.toHexString();
|
|
2048
|
+
break;
|
|
2049
|
+
}
|
|
2107
2050
|
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
if (!this.registered) return;
|
|
2112
|
-
this.notifyAccountsChanged(accounts, origin);
|
|
2113
|
-
});
|
|
2114
|
-
this.walletRequestHandler.on('networks', networks => {
|
|
2115
|
-
if (!this.registered) return;
|
|
2116
|
-
this.notifyNetworks(networks);
|
|
2051
|
+
case 'wallet_switchEthereumChain':
|
|
2052
|
+
{
|
|
2053
|
+
const [switchParams] = request.params;
|
|
2117
2054
|
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
this.notifyChainChanged(ethers.ethers.utils.hexlify(networks.find(network => network.isDefaultChain).chainId));
|
|
2122
|
-
}
|
|
2123
|
-
});
|
|
2124
|
-
this.walletRequestHandler.on('walletContext', walletContext => {
|
|
2125
|
-
if (!this.registered || !walletContext) return;
|
|
2126
|
-
this.notifyWalletContext(walletContext);
|
|
2127
|
-
});
|
|
2128
|
-
this.walletRequestHandler.on('close', error => {
|
|
2129
|
-
if (!this.registered) return;
|
|
2130
|
-
this.notifyClose(error);
|
|
2131
|
-
});
|
|
2132
|
-
}
|
|
2055
|
+
if (!switchParams.chainId || switchParams.chainId.length === 0) {
|
|
2056
|
+
throw new Error('invalid chainId');
|
|
2057
|
+
}
|
|
2133
2058
|
|
|
2134
|
-
|
|
2135
|
-
return this._registered;
|
|
2136
|
-
}
|
|
2059
|
+
const _chainId = ethers.ethers.BigNumber.from(switchParams.chainId);
|
|
2137
2060
|
|
|
2138
|
-
|
|
2139
|
-
throw new Error('abstract method');
|
|
2140
|
-
}
|
|
2061
|
+
const ok = await _this.setDefaultNetwork(_chainId.toString(), true);
|
|
2141
2062
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2063
|
+
if (!ok) {
|
|
2064
|
+
throw new Error(`unable to set chainId ${_chainId}`);
|
|
2065
|
+
}
|
|
2145
2066
|
|
|
2146
|
-
|
|
2147
|
-
throw new Error('abstract method');
|
|
2148
|
-
}
|
|
2067
|
+
response.result = null; // success
|
|
2149
2068
|
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
sessionId,
|
|
2154
|
-
session,
|
|
2155
|
-
error
|
|
2156
|
-
} = openInfo;
|
|
2157
|
-
this.sendMessage({
|
|
2158
|
-
idx: -1,
|
|
2159
|
-
type: exports.EventType.OPEN,
|
|
2160
|
-
data: {
|
|
2161
|
-
chainId,
|
|
2162
|
-
sessionId,
|
|
2163
|
-
session,
|
|
2164
|
-
error
|
|
2165
|
-
}
|
|
2166
|
-
});
|
|
2167
|
-
}
|
|
2069
|
+
break;
|
|
2070
|
+
}
|
|
2071
|
+
// smart wallet method
|
|
2168
2072
|
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
} : null
|
|
2176
|
-
});
|
|
2177
|
-
}
|
|
2073
|
+
case 'sequence_getWalletContext':
|
|
2074
|
+
{
|
|
2075
|
+
response.result = await signer.getWalletContext();
|
|
2076
|
+
break;
|
|
2077
|
+
}
|
|
2078
|
+
// smart wallet method
|
|
2178
2079
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2080
|
+
case 'sequence_getWalletConfig':
|
|
2081
|
+
{
|
|
2082
|
+
const [_chainId2] = request.params;
|
|
2083
|
+
response.result = await signer.getWalletConfig(_chainId2);
|
|
2084
|
+
break;
|
|
2085
|
+
}
|
|
2086
|
+
// smart wallet method
|
|
2186
2087
|
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
type: exports.EventType.DISCONNECT,
|
|
2191
|
-
data: error ? {
|
|
2192
|
-
error
|
|
2193
|
-
} : null
|
|
2194
|
-
});
|
|
2195
|
-
}
|
|
2088
|
+
case 'sequence_getConfigTracker':
|
|
2089
|
+
{
|
|
2090
|
+
var _this$prompter;
|
|
2196
2091
|
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
data: accounts,
|
|
2202
|
-
origin: origin
|
|
2203
|
-
});
|
|
2204
|
-
}
|
|
2092
|
+
response.result = await ((_this$prompter = _this.prompter) == null ? void 0 : _this$prompter.getShareableConfigTrackerData());
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
2095
|
+
// smart wallet method
|
|
2205
2096
|
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2097
|
+
case 'sequence_getWalletState':
|
|
2098
|
+
{
|
|
2099
|
+
const [_chainId3] = request.params;
|
|
2100
|
+
response.result = await signer.getWalletState(_chainId3);
|
|
2101
|
+
break;
|
|
2102
|
+
}
|
|
2103
|
+
// smart wallet method
|
|
2213
2104
|
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2105
|
+
case 'sequence_getNetworks':
|
|
2106
|
+
{
|
|
2107
|
+
// NOTE: must ensure that the response result below returns clean serialized data, which is to omit
|
|
2108
|
+
// the provider and relayer objects and only return the urls so can be reinstantiated on dapp side.
|
|
2109
|
+
// This is handled by this.getNetworks() but noted here for future readers.
|
|
2110
|
+
response.result = await _this.getNetworks(true);
|
|
2111
|
+
break;
|
|
2112
|
+
}
|
|
2113
|
+
// smart wallet method
|
|
2221
2114
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2115
|
+
case 'sequence_updateConfig':
|
|
2116
|
+
{
|
|
2117
|
+
throw new Error('sequence_updateConfig method is not allowed from a dapp'); // NOTE: method is disabled as we don't need a dapp to request to update a config.
|
|
2118
|
+
// However, if we ever want this, we can enable it but must also use the prompter
|
|
2119
|
+
// for confirmation.
|
|
2120
|
+
//
|
|
2121
|
+
// const [newConfig] = request.params
|
|
2122
|
+
// response.result = await signer.updateConfig(newConfig)
|
|
2229
2123
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
utils.logger.warn("isValidInitAck, already in init'd state, so inquiry is invalid.");
|
|
2234
|
-
return false;
|
|
2235
|
-
}
|
|
2124
|
+
break;
|
|
2125
|
+
}
|
|
2126
|
+
// smart wallet method
|
|
2236
2127
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2128
|
+
case 'sequence_publishConfig':
|
|
2129
|
+
{
|
|
2130
|
+
throw new Error('sequence_publishConfig method is not allowed from a dapp');
|
|
2131
|
+
break;
|
|
2132
|
+
}
|
|
2133
|
+
// relayer method
|
|
2241
2134
|
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2135
|
+
case 'sequence_estimateGasLimits':
|
|
2136
|
+
{
|
|
2137
|
+
// TODO
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
// relayer method
|
|
2246
2141
|
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2142
|
+
case 'sequence_gasRefundOptions':
|
|
2143
|
+
{
|
|
2144
|
+
// TODO
|
|
2145
|
+
break;
|
|
2146
|
+
}
|
|
2147
|
+
// relayer method
|
|
2251
2148
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2149
|
+
case 'sequence_getNonce':
|
|
2150
|
+
{
|
|
2151
|
+
// TODO
|
|
2152
|
+
break;
|
|
2153
|
+
}
|
|
2154
|
+
// relayer method
|
|
2256
2155
|
|
|
2156
|
+
case 'sequence_relay':
|
|
2157
|
+
{
|
|
2158
|
+
// TODO
|
|
2159
|
+
break;
|
|
2160
|
+
}
|
|
2161
|
+
// set default network of wallet
|
|
2257
2162
|
|
|
2258
|
-
|
|
2259
|
-
|
|
2163
|
+
case 'sequence_setDefaultNetwork':
|
|
2164
|
+
{
|
|
2165
|
+
const [defaultNetworkId] = request.params;
|
|
2260
2166
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
// it, then it may set this._init to OK in its constructor.
|
|
2265
|
-
if (this._init === exports.InitState.OK) {
|
|
2266
|
-
resolve();
|
|
2267
|
-
return;
|
|
2268
|
-
}
|
|
2167
|
+
if (!defaultNetworkId) {
|
|
2168
|
+
throw new Error('invalid request, method argument defaultNetworkId cannot be empty');
|
|
2169
|
+
}
|
|
2269
2170
|
|
|
2270
|
-
|
|
2271
|
-
reject('transport init is in progress');
|
|
2272
|
-
return;
|
|
2273
|
-
} // start init timeout, if we don't receive confirmation
|
|
2274
|
-
// from provider within this amount of time, then we timeout
|
|
2171
|
+
const ok = await _this.setDefaultNetwork(defaultNetworkId);
|
|
2275
2172
|
|
|
2173
|
+
if (!ok) {
|
|
2174
|
+
throw new Error(`unable to set default network ${defaultNetworkId}`);
|
|
2175
|
+
}
|
|
2276
2176
|
|
|
2277
|
-
|
|
2278
|
-
|
|
2177
|
+
response.result = await _this.getNetworks(true);
|
|
2178
|
+
break;
|
|
2179
|
+
}
|
|
2279
2180
|
|
|
2280
|
-
|
|
2281
|
-
|
|
2181
|
+
default:
|
|
2182
|
+
{
|
|
2183
|
+
// NOTE: provider here will be chain-bound if chainId is provided
|
|
2184
|
+
const providerResponse = await provider.send(request.method, request.params);
|
|
2185
|
+
response.result = providerResponse;
|
|
2186
|
+
}
|
|
2282
2187
|
}
|
|
2283
|
-
}
|
|
2188
|
+
} catch (err) {
|
|
2189
|
+
utils.logger.error(err); // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#rpc-errors
|
|
2284
2190
|
|
|
2285
|
-
|
|
2286
|
-
|
|
2191
|
+
response.result = null;
|
|
2192
|
+
response.error = _extends({}, new Error(err), {
|
|
2193
|
+
code: 4001
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2287
2196
|
|
|
2288
|
-
|
|
2197
|
+
callback(undefined, response);
|
|
2198
|
+
};
|
|
2289
2199
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
resolve();
|
|
2295
|
-
}
|
|
2296
|
-
}; // send init request with random nonce to the provider, where we expect
|
|
2297
|
-
// for the provider to echo it back to us as complete handshake
|
|
2200
|
+
this.isSignedIn = async function () {
|
|
2201
|
+
await _this.signerReady();
|
|
2202
|
+
return !!_this.signer;
|
|
2203
|
+
};
|
|
2298
2204
|
|
|
2205
|
+
this.getSigner = async function () {
|
|
2206
|
+
await _this.signerReady();
|
|
2299
2207
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
type: exports.EventType.INIT,
|
|
2304
|
-
data: {
|
|
2305
|
-
nonce: this._initNonce
|
|
2306
|
-
}
|
|
2307
|
-
});
|
|
2308
|
-
this._init = exports.InitState.SENT_NONCE; // NOTE: the promise will resolve in the _initCallback method
|
|
2309
|
-
// which will be called from either handleMessage or the initTimeout
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2208
|
+
if (_this.signer === undefined) {
|
|
2209
|
+
throw new Error('signerReady failed resolve');
|
|
2210
|
+
}
|
|
2312
2211
|
|
|
2313
|
-
|
|
2212
|
+
return _this.signer;
|
|
2213
|
+
};
|
|
2314
2214
|
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
this.
|
|
2318
|
-
this.
|
|
2319
|
-
const port1 = new ProxyMessageChannelPort();
|
|
2320
|
-
const port2 = new ProxyMessageChannelPort();
|
|
2321
|
-
port1.conn = port2;
|
|
2322
|
-
port2.conn = port1;
|
|
2323
|
-
this.app = port1;
|
|
2324
|
-
this.wallet = port2;
|
|
2215
|
+
this.signer = _signer;
|
|
2216
|
+
this.prompter = prompter;
|
|
2217
|
+
this.mainnetNetworks = mainnetNetworks;
|
|
2218
|
+
this.testnetNetworks = testnetNetworks;
|
|
2325
2219
|
}
|
|
2326
2220
|
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2221
|
+
async signIn(signer, options = {}) {
|
|
2222
|
+
this.setSigner(signer);
|
|
2223
|
+
const {
|
|
2224
|
+
connect,
|
|
2225
|
+
mainnetNetworks,
|
|
2226
|
+
testnetNetworks,
|
|
2227
|
+
defaultNetworkId
|
|
2228
|
+
} = options;
|
|
2332
2229
|
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
}
|
|
2230
|
+
if (mainnetNetworks && mainnetNetworks.length > 0) {
|
|
2231
|
+
this.mainnetNetworks = mainnetNetworks;
|
|
2232
|
+
}
|
|
2336
2233
|
|
|
2337
|
-
|
|
2338
|
-
this.
|
|
2234
|
+
if (testnetNetworks && testnetNetworks.length > 0) {
|
|
2235
|
+
this.testnetNetworks = testnetNetworks;
|
|
2236
|
+
}
|
|
2339
2237
|
|
|
2340
|
-
|
|
2341
|
-
|
|
2238
|
+
if ((!this.mainnetNetworks || this.mainnetNetworks.length === 0) && (!this.testnetNetworks || this.testnetNetworks.length === 0)) {
|
|
2239
|
+
throw new Error('signIn failed as network configuration is empty');
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
const networkId = defaultNetworkId || this._defaultNetworkId;
|
|
2243
|
+
|
|
2244
|
+
if (networkId) {
|
|
2245
|
+
if (!(await this.setDefaultNetwork(networkId, false))) {
|
|
2246
|
+
throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`);
|
|
2342
2247
|
}
|
|
2248
|
+
} // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
|
|
2249
|
+
// necessary auth request, and then notify the dapp of the 'connect' details.
|
|
2250
|
+
//
|
|
2251
|
+
// NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
|
|
2252
|
+
// we don't trigger the promptConnect flow, as we consider the user just authenticated
|
|
2253
|
+
// for this dapp, so its safe to authorize in the connect() method without the prompt.
|
|
2254
|
+
//
|
|
2255
|
+
// NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
|
|
2343
2256
|
|
|
2344
|
-
if (message.type === 'close') {
|
|
2345
|
-
this.events.emit('close', message);
|
|
2346
|
-
}
|
|
2347
2257
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2258
|
+
if (connect) {
|
|
2259
|
+
const connectOptions = this._connectOptions;
|
|
2260
|
+
const connectDetails = await this.connect(connectOptions);
|
|
2261
|
+
this.notifyConnect(connectDetails);
|
|
2351
2262
|
|
|
2352
|
-
if (
|
|
2353
|
-
this.
|
|
2263
|
+
if (!connectOptions || connectOptions.keepWalletOpened !== true) {
|
|
2264
|
+
this.notifyClose();
|
|
2354
2265
|
}
|
|
2355
|
-
}
|
|
2266
|
+
}
|
|
2356
2267
|
}
|
|
2357
2268
|
|
|
2358
|
-
|
|
2359
|
-
|
|
2269
|
+
signOut() {
|
|
2270
|
+
// signed out state
|
|
2271
|
+
this.setSigner(null);
|
|
2360
2272
|
}
|
|
2361
2273
|
|
|
2362
|
-
|
|
2363
|
-
|
|
2274
|
+
accountReset() {
|
|
2275
|
+
// resetting signer puts the wallet in an uninitialized state, which requires the app to
|
|
2276
|
+
// re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
|
|
2277
|
+
this.signer = undefined;
|
|
2364
2278
|
}
|
|
2365
2279
|
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2280
|
+
signerReady(timeout = SIGNER_READY_TIMEOUT) {
|
|
2281
|
+
return new Promise((resolve, reject) => {
|
|
2282
|
+
if (this.signer !== undefined) {
|
|
2283
|
+
resolve();
|
|
2284
|
+
} else {
|
|
2285
|
+
setTimeout(() => {
|
|
2286
|
+
if (this.signer === undefined) {
|
|
2287
|
+
this.signerReadyCallbacks = [];
|
|
2288
|
+
reject(`signerReady timed out`);
|
|
2289
|
+
}
|
|
2290
|
+
}, timeout);
|
|
2291
|
+
this.signerReadyCallbacks.push(resolve);
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2372
2295
|
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2296
|
+
async connect(options) {
|
|
2297
|
+
if (!this.signer) {
|
|
2298
|
+
return {
|
|
2299
|
+
connected: false,
|
|
2300
|
+
chainId: '0x0',
|
|
2301
|
+
error: 'unable to connect without signed in account'
|
|
2376
2302
|
};
|
|
2303
|
+
}
|
|
2377
2304
|
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
this.on('close', (...args) => {
|
|
2382
|
-
this.port.events.emit('close', ...args);
|
|
2383
|
-
});
|
|
2384
|
-
this.on('connect', (...args) => {
|
|
2385
|
-
this.port.events.emit('connect', ...args);
|
|
2386
|
-
});
|
|
2387
|
-
this.on('disconnect', (...args) => {
|
|
2388
|
-
this.port.events.emit('disconnect', ...args);
|
|
2389
|
-
});
|
|
2390
|
-
this._registered = true;
|
|
2305
|
+
const connectDetails = {
|
|
2306
|
+
connected: true,
|
|
2307
|
+
chainId: ethers.ethers.utils.hexlify(await this.getChainId())
|
|
2391
2308
|
};
|
|
2392
2309
|
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2310
|
+
if (options && options.authorize) {
|
|
2311
|
+
// Perform ethauth eip712 request and construct the ConnectDetails response
|
|
2312
|
+
// including the auth proof
|
|
2313
|
+
const authOptions = {
|
|
2314
|
+
app: options.app,
|
|
2315
|
+
origin: options.origin,
|
|
2316
|
+
expiry: options.expiry
|
|
2317
|
+
}; // if (typeof(options.authorize) === 'object') {
|
|
2318
|
+
// authOptions = { ...authOptions, ...options.authorize }
|
|
2319
|
+
// }
|
|
2400
2320
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
path,
|
|
2411
|
-
intent,
|
|
2412
|
-
networkId,
|
|
2413
|
-
sessionId
|
|
2414
|
-
}
|
|
2415
|
-
});
|
|
2321
|
+
try {
|
|
2322
|
+
connectDetails.proof = await auth.signAuthorization(this.signer, authOptions);
|
|
2323
|
+
} catch (err) {
|
|
2324
|
+
utils.logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
|
|
2325
|
+
return {
|
|
2326
|
+
connected: false,
|
|
2327
|
+
chainId: '0x0',
|
|
2328
|
+
error: `signAuthorization failed: ${err.message}`
|
|
2329
|
+
};
|
|
2416
2330
|
}
|
|
2417
|
-
}
|
|
2331
|
+
} // Build session response for connect details
|
|
2418
2332
|
|
|
2419
|
-
this.state = exports.OpenState.CLOSED;
|
|
2420
|
-
this.port = port;
|
|
2421
2333
|
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
// consider it in completed state.
|
|
2334
|
+
connectDetails.session = await this.walletSession();
|
|
2335
|
+
return connectDetails;
|
|
2336
|
+
}
|
|
2426
2337
|
|
|
2338
|
+
// sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
|
|
2339
|
+
// (aka, the signer in this instance) and then responds with a wrapped response of
|
|
2340
|
+
// ProviderMessageResponse to be sent over the transport
|
|
2341
|
+
sendMessageRequest(message) {
|
|
2342
|
+
return new Promise(resolve => {
|
|
2343
|
+
this.sendAsync(message.data, (error, response) => {
|
|
2344
|
+
// TODO: if response includes data.error, why do we need a separate error argument here?
|
|
2345
|
+
const responseMessage = _extends({}, message, {
|
|
2346
|
+
data: response
|
|
2347
|
+
}); // NOTE: we always resolve here, are the sendAsync call will wrap any exceptions
|
|
2348
|
+
// in the error field of the response to ensure we send back to the user
|
|
2427
2349
|
|
|
2428
|
-
this._init = exports.InitState.OK;
|
|
2429
|
-
}
|
|
2430
2350
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
idx: -1,
|
|
2434
|
-
type: exports.EventType.CLOSE,
|
|
2435
|
-
data: null
|
|
2351
|
+
resolve(responseMessage);
|
|
2352
|
+
}, message.chainId);
|
|
2436
2353
|
});
|
|
2437
|
-
|
|
2438
|
-
}
|
|
2354
|
+
} // sendAsync implements the JsonRpcHandler interface for sending JsonRpcRequests to the wallet
|
|
2439
2355
|
|
|
2440
|
-
sendMessage(message) {
|
|
2441
|
-
if (!message.idx) {
|
|
2442
|
-
throw new Error('message idx is empty');
|
|
2443
|
-
}
|
|
2444
2356
|
|
|
2445
|
-
|
|
2357
|
+
on(event, fn) {
|
|
2358
|
+
this.events.on(event, fn);
|
|
2446
2359
|
}
|
|
2447
2360
|
|
|
2448
|
-
|
|
2361
|
+
once(event, fn) {
|
|
2362
|
+
this.events.once(event, fn);
|
|
2363
|
+
}
|
|
2449
2364
|
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2365
|
+
async getAddress() {
|
|
2366
|
+
if (!this.signer) {
|
|
2367
|
+
return '';
|
|
2368
|
+
} else {
|
|
2369
|
+
return this.signer.getAddress();
|
|
2370
|
+
}
|
|
2456
2371
|
}
|
|
2457
2372
|
|
|
2458
|
-
|
|
2459
|
-
this.
|
|
2460
|
-
|
|
2461
|
-
}
|
|
2373
|
+
async getChainId() {
|
|
2374
|
+
if (!this.signer) {
|
|
2375
|
+
return 0;
|
|
2376
|
+
} else {
|
|
2377
|
+
if (this._chainId) return this._chainId; // memoized
|
|
2462
2378
|
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
// because the wallet is already open.
|
|
2468
|
-
//
|
|
2469
|
-
// call this method from wallet redirect hander when a session restore is needed
|
|
2379
|
+
this._chainId = await this.signer.getChainId();
|
|
2380
|
+
return this._chainId;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2470
2383
|
|
|
2384
|
+
get openIntent() {
|
|
2385
|
+
return this._openIntent;
|
|
2386
|
+
}
|
|
2471
2387
|
|
|
2472
|
-
|
|
2473
|
-
|
|
2388
|
+
setOpenIntent(intent) {
|
|
2389
|
+
this._openIntent = intent;
|
|
2390
|
+
}
|
|
2474
2391
|
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
}
|
|
2392
|
+
get connectOptions() {
|
|
2393
|
+
return this._connectOptions;
|
|
2478
2394
|
}
|
|
2479
2395
|
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
this.port.handleMessage = undefined;
|
|
2483
|
-
this._registered = false;
|
|
2396
|
+
setConnectOptions(options) {
|
|
2397
|
+
this._connectOptions = options;
|
|
2484
2398
|
}
|
|
2485
2399
|
|
|
2486
|
-
|
|
2487
|
-
this.
|
|
2400
|
+
get defaultNetworkId() {
|
|
2401
|
+
return this._defaultNetworkId;
|
|
2488
2402
|
}
|
|
2489
2403
|
|
|
2490
|
-
|
|
2404
|
+
async setDefaultNetwork(chainId, notifyNetworks = true) {
|
|
2405
|
+
if (!chainId) return undefined;
|
|
2406
|
+
this._defaultNetworkId = chainId;
|
|
2407
|
+
this._chainId = undefined;
|
|
2491
2408
|
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2409
|
+
if (this.signer) {
|
|
2410
|
+
// Find network id among available networks
|
|
2411
|
+
const networks = await this.signer.getNetworks();
|
|
2412
|
+
const maybeNameLowerCase = chainId.toString().toLowerCase();
|
|
2413
|
+
const cidString = chainId.toString();
|
|
2414
|
+
const network = networks.find(n => n.chainId.toString() === cidString || n.name.toLowerCase() === maybeNameLowerCase); // Can't set network if it doesn't exist in available networks
|
|
2495
2415
|
|
|
2496
|
-
|
|
2497
|
-
|
|
2416
|
+
if (!network) return undefined;
|
|
2417
|
+
await this.signer.setDefaultNetwork(network.chainId);
|
|
2418
|
+
const defaultChainId = network.chainId;
|
|
2498
2419
|
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2420
|
+
if (defaultChainId && notifyNetworks) {
|
|
2421
|
+
await this.notifyNetworks();
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
return defaultChainId;
|
|
2425
|
+
} else {
|
|
2426
|
+
return undefined;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
async getNetworks(jsonRpcResponse) {
|
|
2431
|
+
if (!this.signer) {
|
|
2432
|
+
utils.logger.warn('signer not set: getNetworks is returning an empty list');
|
|
2433
|
+
return [];
|
|
2434
|
+
}
|
|
2508
2435
|
|
|
2436
|
+
const networks = await this.signer.getNetworks();
|
|
2509
2437
|
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
if (!this.provider) {
|
|
2515
|
-
this.provider = m; // unregister other providers
|
|
2438
|
+
if (jsonRpcResponse) {
|
|
2439
|
+
// omit provider and relayer objects as they are not serializable
|
|
2440
|
+
return networks.map(n => {
|
|
2441
|
+
const network = _extends({}, n);
|
|
2516
2442
|
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
}
|
|
2521
|
-
});
|
|
2522
|
-
}
|
|
2523
|
-
});
|
|
2443
|
+
network.provider = undefined;
|
|
2444
|
+
network.relayer = undefined;
|
|
2445
|
+
return network;
|
|
2524
2446
|
});
|
|
2525
|
-
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
this.provider = undefined;
|
|
2530
|
-
};
|
|
2531
|
-
|
|
2532
|
-
this.openWallet = (path, intent, networkId) => {
|
|
2533
|
-
if (this.provider) {
|
|
2534
|
-
this.provider.openWallet(path, intent, networkId);
|
|
2535
|
-
return;
|
|
2536
|
-
}
|
|
2447
|
+
} else {
|
|
2448
|
+
return networks;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2537
2451
|
|
|
2538
|
-
|
|
2452
|
+
async walletSession() {
|
|
2453
|
+
return !this.signer ? undefined : {
|
|
2454
|
+
walletContext: await this.signer.getWalletContext(),
|
|
2455
|
+
accountAddress: await this.signer.getAddress(),
|
|
2456
|
+
networks: await this.getNetworks(true)
|
|
2539
2457
|
};
|
|
2458
|
+
}
|
|
2540
2459
|
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
_this.provider.sendAsync(request, callback, chainId);
|
|
2460
|
+
notifyConnect(connectDetails, origin) {
|
|
2461
|
+
var _connectDetails$sessi;
|
|
2544
2462
|
|
|
2545
|
-
|
|
2546
|
-
}
|
|
2463
|
+
this.events.emit('connect', connectDetails);
|
|
2547
2464
|
|
|
2548
|
-
|
|
2549
|
-
|
|
2465
|
+
if ((_connectDetails$sessi = connectDetails.session) != null && _connectDetails$sessi.accountAddress) {
|
|
2466
|
+
var _connectDetails$sessi2;
|
|
2550
2467
|
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
}
|
|
2468
|
+
this.events.emit('accountsChanged', [(_connectDetails$sessi2 = connectDetails.session) == null ? void 0 : _connectDetails$sessi2.accountAddress], origin);
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2555
2471
|
|
|
2556
|
-
|
|
2557
|
-
|
|
2472
|
+
notifyDisconnect(origin) {
|
|
2473
|
+
this.events.emit('accountsChanged', [], origin);
|
|
2474
|
+
this.events.emit('disconnect');
|
|
2475
|
+
}
|
|
2558
2476
|
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
}
|
|
2477
|
+
async notifyNetworks(networks) {
|
|
2478
|
+
const n = networks || (await this.getNetworks(true));
|
|
2479
|
+
this.events.emit('networks', n);
|
|
2563
2480
|
|
|
2564
|
-
|
|
2565
|
-
|
|
2481
|
+
if (n.length > 0) {
|
|
2482
|
+
const defaultNetwork = n.find(network => network.isDefaultChain);
|
|
2566
2483
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
return _this.provider.waitUntilConnected();
|
|
2484
|
+
if (defaultNetwork) {
|
|
2485
|
+
this.events.emit('chainChanged', ethers.ethers.utils.hexlify(defaultNetwork.chainId));
|
|
2570
2486
|
}
|
|
2487
|
+
} else {
|
|
2488
|
+
this.events.emit('chainChanged', '0x0');
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2571
2491
|
|
|
2572
|
-
|
|
2573
|
-
|
|
2492
|
+
async notifyWalletContext() {
|
|
2493
|
+
if (!this.signer) {
|
|
2494
|
+
utils.logger.warn('signer not set: skipping to notify wallet context');
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2574
2497
|
|
|
2575
|
-
|
|
2576
|
-
this.
|
|
2498
|
+
const walletContext = await this.signer.getWalletContext();
|
|
2499
|
+
this.events.emit('walletContext', walletContext);
|
|
2577
2500
|
}
|
|
2578
2501
|
|
|
2579
|
-
|
|
2580
|
-
this.
|
|
2502
|
+
notifyClose(error) {
|
|
2503
|
+
this.events.emit('close', error);
|
|
2581
2504
|
}
|
|
2582
2505
|
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2506
|
+
setSigner(signer) {
|
|
2507
|
+
this.signer = signer;
|
|
2508
|
+
|
|
2509
|
+
if (signer !== undefined) {
|
|
2510
|
+
for (let i = 0; i < this.signerReadyCallbacks.length; i++) {
|
|
2511
|
+
this.signerReadyCallbacks[i]();
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
this.signerReadyCallbacks = [];
|
|
2586
2515
|
}
|
|
2587
2516
|
}
|
|
2588
2517
|
|
|
2589
|
-
|
|
2590
|
-
if
|
|
2591
|
-
|
|
2518
|
+
async handleConfirmWalletDeployPrompt(prompter, signer, chainId) {
|
|
2519
|
+
// check if wallet is deployed and up to date, if not, prompt user to deploy
|
|
2520
|
+
// if no chainId is provided, we'll assume the wallet is auth chain wallet and is up to date
|
|
2521
|
+
if (!chainId) {
|
|
2522
|
+
return true;
|
|
2592
2523
|
}
|
|
2593
2524
|
|
|
2594
|
-
|
|
2595
|
-
|
|
2525
|
+
if (!wallet.isUpgradableSigner(signer)) {
|
|
2526
|
+
return true;
|
|
2527
|
+
}
|
|
2596
2528
|
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
return this.provider.isConnected();
|
|
2529
|
+
if (await signer.isSettled(chainId)) {
|
|
2530
|
+
return true;
|
|
2600
2531
|
}
|
|
2601
2532
|
|
|
2602
|
-
|
|
2603
|
-
}
|
|
2533
|
+
const promptResult = await prompter.promptConfirmWalletDeploy(chainId, this.connectOptions); // if client returned true, check again to make sure wallet is deployed and up to date
|
|
2604
2534
|
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2535
|
+
if (promptResult) {
|
|
2536
|
+
if (!(await signer.isSettled(chainId))) {
|
|
2537
|
+
utils.logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct');
|
|
2538
|
+
return false;
|
|
2539
|
+
} else {
|
|
2540
|
+
return true;
|
|
2541
|
+
}
|
|
2609
2542
|
}
|
|
2610
2543
|
|
|
2611
|
-
|
|
2612
|
-
m.on(event, fn);
|
|
2613
|
-
});
|
|
2544
|
+
return false;
|
|
2614
2545
|
}
|
|
2615
2546
|
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
this.provider.once(event, fn);
|
|
2619
|
-
return;
|
|
2620
|
-
}
|
|
2547
|
+
}
|
|
2548
|
+
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'];
|
|
2621
2549
|
|
|
2622
|
-
|
|
2623
|
-
|
|
2550
|
+
const CHANNEL_ID = 'sequence-extension-message-handler';
|
|
2551
|
+
class ExtensionMessageHandler extends BaseWalletTransport {
|
|
2552
|
+
constructor(walletRequestHandler, runtime) {
|
|
2553
|
+
super(walletRequestHandler);
|
|
2554
|
+
this.runtime = runtime;
|
|
2555
|
+
this.port = void 0;
|
|
2556
|
+
this._init = exports.InitState.OK;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
register() {
|
|
2560
|
+
this._registered = true;
|
|
2561
|
+
this.port = this.runtime.connect({
|
|
2562
|
+
name: CHANNEL_ID
|
|
2624
2563
|
});
|
|
2625
2564
|
}
|
|
2626
2565
|
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2566
|
+
sendMessage(message) {
|
|
2567
|
+
utils.logger.info('[ExtensionMessageHandler send]', message);
|
|
2568
|
+
this.port.postMessage(message);
|
|
2569
|
+
}
|
|
2631
2570
|
|
|
2632
|
-
|
|
2633
|
-
this.messageProviders[i].emit(event, ...args);
|
|
2634
|
-
}
|
|
2571
|
+
}
|
|
2635
2572
|
|
|
2636
|
-
|
|
2573
|
+
class ExtensionMessageProvider extends BaseProviderTransport {
|
|
2574
|
+
constructor(runtime) {
|
|
2575
|
+
super();
|
|
2576
|
+
|
|
2577
|
+
this.register = () => {
|
|
2578
|
+
this._registered = true;
|
|
2579
|
+
};
|
|
2580
|
+
|
|
2581
|
+
runtime.onConnect.addListener(port => {
|
|
2582
|
+
if (port.name === CHANNEL_ID) {
|
|
2583
|
+
this._init = exports.InitState.OK;
|
|
2584
|
+
port.onMessage.addListener(message => {
|
|
2585
|
+
this.handleMessage(message);
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2637
2589
|
}
|
|
2638
2590
|
|
|
2639
|
-
sendMessage(message) {
|
|
2640
|
-
|
|
2641
|
-
throw new Error('message idx is empty');
|
|
2642
|
-
}
|
|
2591
|
+
sendMessage(message) {//noop
|
|
2592
|
+
}
|
|
2643
2593
|
|
|
2644
|
-
|
|
2645
|
-
this.provider.sendMessage(message);
|
|
2646
|
-
} else {
|
|
2647
|
-
throw new Error('impossible state, wallet must be opened first');
|
|
2648
|
-
}
|
|
2594
|
+
unregister() {//noop
|
|
2649
2595
|
}
|
|
2650
2596
|
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
this.provider.handleMessage(message);
|
|
2654
|
-
return;
|
|
2655
|
-
}
|
|
2597
|
+
openWallet(path, intent, networkId) {//noop
|
|
2598
|
+
}
|
|
2656
2599
|
|
|
2657
|
-
|
|
2600
|
+
closeWallet() {//noop
|
|
2658
2601
|
}
|
|
2659
2602
|
|
|
2660
2603
|
}
|
|
2661
2604
|
|
|
2662
|
-
|
|
2663
|
-
class
|
|
2664
|
-
constructor(
|
|
2605
|
+
// to be used on injected window.ethereum EIP1193 proxy
|
|
2606
|
+
class BaseInjectedTransport extends eventemitter2.EventEmitter2 {
|
|
2607
|
+
constructor(stream) {
|
|
2608
|
+
var _this;
|
|
2609
|
+
|
|
2665
2610
|
super();
|
|
2666
|
-
|
|
2667
|
-
this.
|
|
2611
|
+
_this = this;
|
|
2612
|
+
this.stream = stream;
|
|
2613
|
+
this.responseCallbacks = new Map();
|
|
2614
|
+
this._messageIdx = 0;
|
|
2668
2615
|
|
|
2669
|
-
this.
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
}
|
|
2616
|
+
this.nextMessageIdx = () => ++this._messageIdx;
|
|
2617
|
+
|
|
2618
|
+
this.handleMessage = message => {
|
|
2619
|
+
if (!message.type || !message.data) {
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
utils.logger.info('[received message]', message);
|
|
2624
|
+
const requestIdx = message.idx;
|
|
2625
|
+
const responseCallback = this.responseCallbacks.get(requestIdx);
|
|
2626
|
+
|
|
2627
|
+
if (requestIdx) {
|
|
2628
|
+
this.responseCallbacks.delete(requestIdx);
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
switch (message.type) {
|
|
2632
|
+
case exports.EventType.MESSAGE:
|
|
2633
|
+
if (responseCallback) {
|
|
2634
|
+
this.emit(exports.EventType.MESSAGE, message);
|
|
2635
|
+
responseCallback(message.data.error, message);
|
|
2636
|
+
} else {
|
|
2637
|
+
// NOTE: this would occur if 'idx' isn't set, which should never happen
|
|
2638
|
+
// or when we register two handler, or duplicate messages with the same idx are sent,
|
|
2639
|
+
// all of which should be prevented prior to getting to this point
|
|
2640
|
+
throw new Error('impossible state');
|
|
2641
|
+
}
|
|
2675
2642
|
|
|
2643
|
+
break;
|
|
2676
2644
|
|
|
2677
|
-
|
|
2678
|
-
|
|
2645
|
+
case exports.EventType.DISCONNECT:
|
|
2646
|
+
case exports.EventType.ACCOUNTS_CHANGED:
|
|
2647
|
+
case exports.EventType.CHAIN_CHANGED:
|
|
2648
|
+
this.emit(message.type, message.data);
|
|
2649
|
+
break;
|
|
2679
2650
|
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
clearInterval(interval);
|
|
2686
|
-
this.close();
|
|
2687
|
-
}
|
|
2688
|
-
}, 500);
|
|
2689
|
-
}); // close clean up
|
|
2651
|
+
default:
|
|
2652
|
+
console.error('unknown message type', message);
|
|
2653
|
+
break;
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2690
2656
|
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2657
|
+
this.sendMessageRequest = async function (message) {
|
|
2658
|
+
return new Promise((resolve, reject) => {
|
|
2659
|
+
if (!message.idx || message.idx <= 0) {
|
|
2660
|
+
reject(new Error('message idx not set'));
|
|
2695
2661
|
}
|
|
2696
|
-
});
|
|
2697
|
-
this._registered = true;
|
|
2698
|
-
};
|
|
2699
2662
|
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2663
|
+
const responseCallback = (error, response) => {
|
|
2664
|
+
if (error) {
|
|
2665
|
+
reject(error);
|
|
2666
|
+
} else if (response) {
|
|
2667
|
+
resolve(response);
|
|
2668
|
+
} else {
|
|
2669
|
+
throw new Error('no valid response to return');
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2703
2672
|
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2673
|
+
const {
|
|
2674
|
+
idx
|
|
2675
|
+
} = message;
|
|
2707
2676
|
|
|
2708
|
-
|
|
2677
|
+
if (!_this.responseCallbacks.get(idx)) {
|
|
2678
|
+
_this.responseCallbacks.set(idx, responseCallback);
|
|
2679
|
+
} else {
|
|
2680
|
+
reject(new Error('duplicate message idx, should never happen'));
|
|
2681
|
+
}
|
|
2709
2682
|
|
|
2710
|
-
|
|
2683
|
+
_this.sendMessage(message);
|
|
2684
|
+
});
|
|
2711
2685
|
};
|
|
2712
2686
|
|
|
2713
|
-
this.
|
|
2714
|
-
|
|
2715
|
-
// TODO: update the location of window to path
|
|
2716
|
-
this.walletWindow.focus();
|
|
2717
|
-
return;
|
|
2718
|
-
} // Instantiate new walletURL for this call
|
|
2687
|
+
this.stream.on('data', this.handleMessage);
|
|
2688
|
+
}
|
|
2719
2689
|
|
|
2690
|
+
sendMessage(message) {
|
|
2691
|
+
if (!this.stream.writable) {
|
|
2692
|
+
console.error('window post message stream is not writable');
|
|
2693
|
+
}
|
|
2720
2694
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2695
|
+
this.stream.write(message);
|
|
2696
|
+
}
|
|
2723
2697
|
|
|
2724
|
-
|
|
2725
|
-
walletURL.pathname = path.toLowerCase();
|
|
2726
|
-
} // Set session, intent and network id on walletURL
|
|
2698
|
+
}
|
|
2727
2699
|
|
|
2700
|
+
class Web3Provider extends providers.Web3Provider {
|
|
2701
|
+
static isSequenceProvider(cand) {
|
|
2702
|
+
return isSequenceProvider(cand);
|
|
2703
|
+
}
|
|
2728
2704
|
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2705
|
+
constructor(provider, defaultChainId) {
|
|
2706
|
+
const sender = new network.JsonRpcSender(provider, network.maybeChainId(defaultChainId));
|
|
2707
|
+
provider = sender;
|
|
2708
|
+
super(provider, 'any');
|
|
2709
|
+
this._sender = void 0;
|
|
2710
|
+
this._isSequenceProvider = void 0;
|
|
2711
|
+
this._defaultChainId = void 0;
|
|
2712
|
+
this._sender = sender;
|
|
2713
|
+
this._isSequenceProvider = true;
|
|
2714
|
+
this._defaultChainId = network.maybeChainId(defaultChainId);
|
|
2715
|
+
}
|
|
2732
2716
|
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
|
|
2737
|
-
if (intent.type === 'connect') {
|
|
2738
|
-
if (!intent.options) intent.options = {}; // skip setting origin host if we're in an browser extension execution context
|
|
2739
|
-
// allow origin that is passed in
|
|
2717
|
+
sendAsync(request, callback, chainId) {
|
|
2718
|
+
this._sender.sendAsync(request, callback, chainId);
|
|
2719
|
+
}
|
|
2740
2720
|
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
} // encode intent as base64 url-encoded param
|
|
2721
|
+
send(method, params, chainId) {
|
|
2722
|
+
return this._sender.send(method, params, chainId);
|
|
2723
|
+
}
|
|
2745
2724
|
|
|
2725
|
+
request(request) {
|
|
2726
|
+
return this.send(request.method, request.params || [], request.chainId);
|
|
2727
|
+
}
|
|
2746
2728
|
|
|
2747
|
-
|
|
2748
|
-
|
|
2729
|
+
getSigner() {
|
|
2730
|
+
return new Web3Signer(this, this._defaultChainId);
|
|
2731
|
+
}
|
|
2749
2732
|
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2733
|
+
async getChainId() {
|
|
2734
|
+
// TODO: is it safe to memoize this?
|
|
2735
|
+
const result = await this.send('eth_chainId', []);
|
|
2736
|
+
const chainId = ethers.ethers.BigNumber.from(result).toNumber();
|
|
2753
2737
|
|
|
2738
|
+
if (this._defaultChainId && this._defaultChainId !== chainId) {
|
|
2739
|
+
throw new Error(`provider chainId (${chainId}) does not match provider-bound chainId ${this._defaultChainId}`);
|
|
2740
|
+
}
|
|
2754
2741
|
|
|
2755
|
-
|
|
2756
|
-
|
|
2742
|
+
return chainId;
|
|
2743
|
+
}
|
|
2757
2744
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2745
|
+
}
|
|
2746
|
+
function isSequenceProvider(provider) {
|
|
2747
|
+
const cand = provider;
|
|
2748
|
+
return cand && cand.send !== undefined && cand._isSequenceProvider === true;
|
|
2749
|
+
}
|
|
2750
|
+
class LocalWeb3Provider extends Web3Provider {
|
|
2751
|
+
constructor(account, networks) {
|
|
2752
|
+
const walletRequestHandler = new WalletRequestHandler(account, null, networks || []);
|
|
2753
|
+
super(walletRequestHandler);
|
|
2754
|
+
}
|
|
2765
2755
|
|
|
2766
|
-
|
|
2756
|
+
}
|
|
2757
|
+
class Web3Signer extends wallet.Signer {
|
|
2758
|
+
constructor(provider, defaultChainId) {
|
|
2759
|
+
super();
|
|
2760
|
+
this.provider = void 0;
|
|
2761
|
+
this.defaultChainId = void 0;
|
|
2762
|
+
this._address = void 0;
|
|
2763
|
+
this._index = void 0;
|
|
2764
|
+
this._context = void 0;
|
|
2765
|
+
this._networks = void 0;
|
|
2766
|
+
this._providers = {};
|
|
2767
|
+
this.provider = provider;
|
|
2768
|
+
this.defaultChainId = defaultChainId;
|
|
2769
|
+
} // memoized
|
|
2767
2770
|
|
|
2768
|
-
walletURL.search = windowSessionParams.toString();
|
|
2769
|
-
this.walletWindow = window.open(walletURL.href, 'sequence.app', windowFeatures); // TODO: move this somewhere else
|
|
2770
|
-
// TODO: perhaps we trigger a .on('openTimeout') event..? maybe.. could help.
|
|
2771
|
-
// Popup blocking detection and notice
|
|
2772
|
-
// let warned = false
|
|
2773
|
-
// const warnPopupBlocked = () => {
|
|
2774
|
-
// if (warned) return
|
|
2775
|
-
// warned = true
|
|
2776
|
-
// // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
|
|
2777
|
-
// throw new Error('popup is blocked')
|
|
2778
|
-
// }
|
|
2779
|
-
// const popupCheck = setTimeout(() => {
|
|
2780
|
-
// if (!popup || popup.closed || typeof popup.closed === 'undefined') {
|
|
2781
|
-
// // popup is definitely blocked if we reach here.
|
|
2782
|
-
// warnPopupBlocked()
|
|
2783
|
-
// }
|
|
2784
|
-
// }, 1000)
|
|
2785
|
-
// const popupBlocked = popup === null || popup === undefined
|
|
2786
|
-
// if (popupBlocked) {
|
|
2787
|
-
// warnPopupBlocked()
|
|
2788
|
-
// return
|
|
2789
|
-
// }
|
|
2790
|
-
};
|
|
2791
2771
|
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2772
|
+
//
|
|
2773
|
+
// ethers AbstractSigner methods
|
|
2774
|
+
//
|
|
2775
|
+
async getAddress() {
|
|
2776
|
+
if (this._address) return this._address;
|
|
2777
|
+
const accounts = await this.provider.send('eth_accounts', []);
|
|
2778
|
+
this._address = accounts[0];
|
|
2779
|
+
this._index = 0;
|
|
2780
|
+
return ethers.ethers.utils.getAddress(this._address);
|
|
2781
|
+
}
|
|
2798
2782
|
|
|
2799
|
-
|
|
2783
|
+
signTransaction(transaction) {
|
|
2784
|
+
// TODO .. since ethers isn't using this method, perhaps we will?
|
|
2785
|
+
throw new Error('signTransaction is unsupported, use signTransactions instead');
|
|
2786
|
+
}
|
|
2800
2787
|
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
// event is not a ProviderMessage JSON object, skip
|
|
2805
|
-
return;
|
|
2806
|
-
}
|
|
2788
|
+
connect(provider) {
|
|
2789
|
+
throw new Error('unsupported: cannot alter JSON-RPC Signer connection');
|
|
2790
|
+
}
|
|
2807
2791
|
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2792
|
+
setDefaultNetwork(chainId) {
|
|
2793
|
+
return this.provider.send('sequence_setDefaultNetwork', [network.maybeChainId(chainId)]);
|
|
2794
|
+
} //
|
|
2795
|
+
// Sequence Signer methods
|
|
2796
|
+
//
|
|
2797
|
+
// getProvider returns a Web3Provider instance for the current chain. Note that this method
|
|
2798
|
+
// and signer is bound to a particular chain to prevent misuse. If you'd like a provider
|
|
2799
|
+
// for a specific chain, try getSender(chainId), or wallet.getProvider(chainId).
|
|
2811
2800
|
|
|
2812
2801
|
|
|
2813
|
-
|
|
2814
|
-
|
|
2802
|
+
async getProvider(chainId) {
|
|
2803
|
+
if (chainId) {
|
|
2804
|
+
const currentChainId = await this.getChainId();
|
|
2815
2805
|
|
|
2816
|
-
|
|
2817
|
-
|
|
2806
|
+
if (currentChainId !== chainId) {
|
|
2807
|
+
throw new Error(`signer is attempting to access chain ${chainId}, but is already bound to chain ${currentChainId}`);
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2818
2810
|
|
|
2819
|
-
|
|
2820
|
-
|
|
2811
|
+
return this.provider;
|
|
2812
|
+
} // getSender returns a Web3Provider instance via the signer transport. Note: for our case
|
|
2813
|
+
// the of sequence wallet, this will bring up the wallet window whenever using it, as the json-rpc
|
|
2814
|
+
// requests are sent to the window transport. Therefore, for anything non-signing related
|
|
2815
|
+
// you can write a higher-order JsonRpcRouter sender to route to the public provider endpoints
|
|
2816
|
+
// as we do in the WalletProvider.
|
|
2817
|
+
//
|
|
2818
|
+
// This method is primarily utilized internally when routing requests to a particular chainId.
|
|
2821
2819
|
|
|
2822
|
-
this.close();
|
|
2823
|
-
(_this$walletWindow = this.walletWindow) == null ? void 0 : _this$walletWindow.close();
|
|
2824
|
-
} // onmessage, receives ProviderMessageResponse from the wallet post-message transport
|
|
2825
2820
|
|
|
2821
|
+
async getSender(chainId) {
|
|
2822
|
+
if (!chainId || chainId && chainId === this.defaultChainId) {
|
|
2823
|
+
return this.provider;
|
|
2824
|
+
}
|
|
2826
2825
|
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
utils.logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable');
|
|
2830
|
-
return;
|
|
2826
|
+
if (!this._providers[chainId]) {
|
|
2827
|
+
this._providers[chainId] = new Web3Provider(new network.JsonRpcSender(this.provider, chainId), chainId);
|
|
2831
2828
|
}
|
|
2832
2829
|
|
|
2833
|
-
|
|
2834
|
-
this.walletWindow.postMessage(postedMessage, this.walletURL.origin);
|
|
2830
|
+
return this._providers[chainId];
|
|
2835
2831
|
}
|
|
2836
2832
|
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2833
|
+
getRelayer(chainId) {
|
|
2834
|
+
// TODO: JsonRpcRelayer ......? or, Web3Relayer.. or SequenceRelayer?
|
|
2835
|
+
// sequence_estimateGasLimits
|
|
2836
|
+
// sequence_gasRefundOptions
|
|
2837
|
+
// sequence_getNonce
|
|
2838
|
+
// sequence_relay
|
|
2839
|
+
throw new Error('TODO');
|
|
2840
|
+
}
|
|
2842
2841
|
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2842
|
+
async getWalletContext() {
|
|
2843
|
+
if (!this._context) {
|
|
2844
|
+
this._context = await this.provider.send('sequence_getWalletContext', []);
|
|
2845
|
+
}
|
|
2847
2846
|
|
|
2848
|
-
this.
|
|
2849
|
-
|
|
2850
|
-
// skip same-origin or when event.origin is empty/undefined
|
|
2851
|
-
return;
|
|
2852
|
-
}
|
|
2847
|
+
return this._context;
|
|
2848
|
+
}
|
|
2853
2849
|
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
} // Wallet always expects json-rpc request messages from a dapp
|
|
2850
|
+
async getConfigTracker() {
|
|
2851
|
+
return this.provider.send('sequence_getConfigTracker', []);
|
|
2852
|
+
}
|
|
2858
2853
|
|
|
2854
|
+
async getWalletConfig(chainId) {
|
|
2855
|
+
return await this.provider.send('sequence_getWalletConfig', [network.maybeChainId(chainId)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2856
|
+
}
|
|
2859
2857
|
|
|
2860
|
-
|
|
2858
|
+
async getWalletState(chainId) {
|
|
2859
|
+
return await this.provider.send('sequence_getWalletState', [network.maybeChainId(chainId)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2860
|
+
}
|
|
2861
2861
|
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
return;
|
|
2867
|
-
}
|
|
2862
|
+
async getNetworks() {
|
|
2863
|
+
if (!this._networks) {
|
|
2864
|
+
this._networks = await this.provider.send('sequence_getNetworks', []);
|
|
2865
|
+
}
|
|
2868
2866
|
|
|
2869
|
-
|
|
2867
|
+
return this._networks;
|
|
2868
|
+
}
|
|
2870
2869
|
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
}
|
|
2870
|
+
async getSigners() {
|
|
2871
|
+
var _this = this;
|
|
2874
2872
|
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
utils.logger.error('impossible state, init.OK and appOrigin required');
|
|
2878
|
-
return;
|
|
2879
|
-
} // Handle message via the base transport
|
|
2873
|
+
// Get all networks
|
|
2874
|
+
const networks = await this.getNetworks(); // Get all config for all networks
|
|
2880
2875
|
|
|
2876
|
+
const configs = await Promise.all(networks.map(async function (network) {
|
|
2877
|
+
return _this.getWalletConfig(network.chainId);
|
|
2878
|
+
})); // Get all addresses for all configs
|
|
2881
2879
|
|
|
2882
|
-
|
|
2883
|
-
|
|
2880
|
+
const addresses = configs.reduce((acc, config) => {
|
|
2881
|
+
return acc.concat(config.signers.map(s => s.address));
|
|
2882
|
+
}, []); // Remove duplicates
|
|
2884
2883
|
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
return {
|
|
2888
|
-
sessionId: params.get('sid'),
|
|
2889
|
-
networkId: params.get('net'),
|
|
2890
|
-
intent: utils.base64DecodeObject(params.get('intent'))
|
|
2891
|
-
};
|
|
2892
|
-
};
|
|
2884
|
+
return Array.from(new Set(addresses));
|
|
2885
|
+
}
|
|
2893
2886
|
|
|
2894
|
-
|
|
2887
|
+
buildDeployTransaction(chainId) {
|
|
2888
|
+
throw new Error('Method not implemented.');
|
|
2895
2889
|
}
|
|
2896
2890
|
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2891
|
+
deploy(chainId) {
|
|
2892
|
+
throw new Error('Method not implemented.');
|
|
2893
|
+
} // signMessage matches implementation from ethers JsonRpcSigner for compatibility, but with
|
|
2894
|
+
// multi-chain support.
|
|
2900
2895
|
|
|
2901
|
-
if (isPopup !== true) {
|
|
2902
|
-
return;
|
|
2903
|
-
} // record open details (sessionId + default network) from the window url
|
|
2904
2896
|
|
|
2897
|
+
async signMessage(message, chainId, allSigners, counterfactual) {
|
|
2898
|
+
chainId = network.maybeChainId(chainId) || this.defaultChainId;
|
|
2899
|
+
const provider = await this.getSender(chainId);
|
|
2905
2900
|
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
} = new URL(windowHref || window.location.href);
|
|
2910
|
-
let session = this.getWindowTransportSession(rawParams); // provider should always include sid when opening a new window
|
|
2901
|
+
if (!provider) {
|
|
2902
|
+
throw new Error(`no provider for chain id ${chainId}`);
|
|
2903
|
+
}
|
|
2911
2904
|
|
|
2912
|
-
const
|
|
2905
|
+
const data = typeof message === 'string' ? ethers.ethers.utils.toUtf8Bytes(message) : message;
|
|
2906
|
+
const address = await this.getAddress();
|
|
2913
2907
|
|
|
2914
|
-
if (
|
|
2915
|
-
|
|
2908
|
+
if (counterfactual) {
|
|
2909
|
+
return provider.send('sequence_sign', [ethers.ethers.utils.hexlify(data), address, {
|
|
2910
|
+
counterfactual: true
|
|
2911
|
+
}]);
|
|
2912
|
+
} else {
|
|
2913
|
+
// NOTE: as of ethers v5.5, it switched to using personal_sign, see
|
|
2914
|
+
// https://github.com/ethers-io/ethers.js/pull/1542 and see
|
|
2915
|
+
// https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info.
|
|
2916
|
+
return provider.send('personal_sign', [ethers.ethers.utils.hexlify(data), address]);
|
|
2916
2917
|
}
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
utils.logger.error('window session is undefined');
|
|
2920
|
-
return;
|
|
2921
|
-
} // record parent window instance for communication
|
|
2918
|
+
} // signTypedData matches implementation from ethers JsonRpcSigner for compatibility, but with
|
|
2919
|
+
// multi-chain support.
|
|
2922
2920
|
|
|
2923
2921
|
|
|
2924
|
-
|
|
2922
|
+
async signTypedData(domain, types, message, chainId, allSigners, counterfactual) {
|
|
2923
|
+
// Populate any ENS names (in-place)
|
|
2924
|
+
// const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => {
|
|
2925
|
+
// return this.provider.resolveName(name)
|
|
2926
|
+
// })
|
|
2927
|
+
if (counterfactual) {
|
|
2928
|
+
return this.provider.send('sequence_signTypedData_v4', [await this.getAddress(), ethers.ethers.utils._TypedDataEncoder.getPayload(domain, types, message), {
|
|
2929
|
+
counterfactual: true
|
|
2930
|
+
}], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2931
|
+
} else {
|
|
2932
|
+
return this.provider.send('eth_signTypedData_v4', [await this.getAddress(), ethers.ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2933
|
+
}
|
|
2934
|
+
} // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with
|
|
2935
|
+
// multi-chain support.
|
|
2925
2936
|
|
|
2926
|
-
window.addEventListener('message', this.onWindowEvent, false);
|
|
2927
|
-
this._registered = true; // send open event to the app which opened us
|
|
2928
2937
|
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2938
|
+
async sendTransaction(transaction, chainId, allSigners) {
|
|
2939
|
+
const provider = await this.getSender(network.maybeChainId(chainId) || this.defaultChainId);
|
|
2940
|
+
const tx = this.sendUncheckedTransaction(transaction, chainId).then(hash => {
|
|
2941
|
+
return ethers.ethers.utils.poll(() => {
|
|
2942
|
+
return provider.getTransaction(hash).then(tx => {
|
|
2943
|
+
if (tx === null) {
|
|
2944
|
+
return undefined;
|
|
2945
|
+
}
|
|
2932
2946
|
|
|
2933
|
-
|
|
2934
|
-
utils.logger.error(err);
|
|
2935
|
-
this.notifyClose({
|
|
2936
|
-
message: err
|
|
2947
|
+
return provider._wrapTransaction(tx, hash);
|
|
2937
2948
|
});
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
const err = `failed to open to network ${(_session2 = session) == null ? void 0 : _session2.networkId}, due to: ${e}`;
|
|
2944
|
-
utils.logger.error(err);
|
|
2945
|
-
this.notifyClose({
|
|
2946
|
-
message: err
|
|
2949
|
+
}, {
|
|
2950
|
+
onceBlock: this.provider
|
|
2951
|
+
}).catch(error => {
|
|
2952
|
+
error.transactionHash = hash;
|
|
2953
|
+
throw error;
|
|
2947
2954
|
});
|
|
2948
|
-
|
|
2949
|
-
});
|
|
2950
|
-
}
|
|
2955
|
+
}); // @ts-ignore
|
|
2951
2956
|
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
} // onmessage is called when (the wallet) receives request messages from the dapp
|
|
2956
|
-
// over the window post-messaging transport
|
|
2957
|
+
return tx;
|
|
2958
|
+
} // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to
|
|
2959
|
+
// send multiple transaction as a single payload and just one on-chain transaction.
|
|
2957
2960
|
|
|
2958
2961
|
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
// prepare payload
|
|
2962
|
-
const payload = JSON.stringify(message); // post-message to app.
|
|
2963
|
-
// only for init requests, we send to '*' origin
|
|
2962
|
+
async sendTransactionBatch(transactions, chainId, allSigners) {
|
|
2963
|
+
const batch = await wallet.resolveArrayProperties(transactions);
|
|
2964
2964
|
|
|
2965
|
-
if (
|
|
2966
|
-
|
|
2967
|
-
}
|
|
2968
|
-
this.postMessage(payload);
|
|
2969
|
-
}
|
|
2970
|
-
}
|
|
2965
|
+
if (!batch || batch.length === 0) {
|
|
2966
|
+
throw new Error('cannot send empty batch');
|
|
2967
|
+
} // sendTransactionBatch only accepts TransactionRequest, not TransactionResponses
|
|
2971
2968
|
|
|
2972
|
-
get isPopup() {
|
|
2973
|
-
return this._isPopup;
|
|
2974
|
-
}
|
|
2975
2969
|
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
utils.logger.error('impossible state, should not be calling postMessage until inited');
|
|
2979
|
-
return;
|
|
2970
|
+
if (batch.find(v => v.wait !== undefined && v.wait !== null)) {
|
|
2971
|
+
throw new Error('transaction request expected for sendTransactionBatch, transaction response found');
|
|
2980
2972
|
}
|
|
2981
2973
|
|
|
2982
|
-
|
|
2983
|
-
// init message transmission to global target -- for 'init' payloads only
|
|
2984
|
-
this.parentWindow.postMessage(message, '*');
|
|
2985
|
-
} else {
|
|
2986
|
-
// open message transmission
|
|
2987
|
-
if (this.appOrigin && this.appOrigin.length > 4) {
|
|
2988
|
-
// just above '.com'
|
|
2989
|
-
this.parentWindow.postMessage(message, this.appOrigin);
|
|
2990
|
-
} else {
|
|
2991
|
-
utils.logger.error('unable to postMessage as parentOrigin is invalid');
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2974
|
+
const tx = _extends({}, batch[0]);
|
|
2995
2975
|
|
|
2996
|
-
|
|
2976
|
+
if (batch.length > 1) {
|
|
2977
|
+
tx.auxiliary = batch.splice(1);
|
|
2978
|
+
}
|
|
2997
2979
|
|
|
2998
|
-
|
|
2999
|
-
class ExtensionMessageHandler extends BaseWalletTransport {
|
|
3000
|
-
constructor(walletRequestHandler, runtime) {
|
|
3001
|
-
super(walletRequestHandler);
|
|
3002
|
-
this.runtime = runtime;
|
|
3003
|
-
this.port = void 0;
|
|
3004
|
-
this._init = exports.InitState.OK;
|
|
2980
|
+
return this.sendTransaction(tx, chainId, allSigners);
|
|
3005
2981
|
}
|
|
3006
2982
|
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
name: CHANNEL_ID
|
|
3011
|
-
});
|
|
3012
|
-
}
|
|
2983
|
+
signTransactions(transaction, chainId, allSigners) {
|
|
2984
|
+
transaction = utils.shallowCopy(transaction); // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over
|
|
2985
|
+
// the wire.. see sendUncheckedTransaction and resolveProperties
|
|
3013
2986
|
|
|
3014
|
-
|
|
3015
|
-
utils.logger.info('[ExtensionMessageHandler send]', message);
|
|
3016
|
-
this.port.postMessage(message);
|
|
2987
|
+
return this.provider.send('eth_signTransaction', [transaction], network.maybeChainId(chainId) || this.defaultChainId);
|
|
3017
2988
|
}
|
|
3018
2989
|
|
|
3019
|
-
|
|
2990
|
+
sendSignedTransactions(signedBundle, chainId) {
|
|
2991
|
+
// sequence_relay
|
|
2992
|
+
throw new Error('TODO');
|
|
2993
|
+
} // updateConfig..
|
|
2994
|
+
// NOTE: this is not supported by the remote wallet by default.
|
|
3020
2995
|
|
|
3021
|
-
class ExtensionMessageProvider extends BaseProviderTransport {
|
|
3022
|
-
constructor(runtime) {
|
|
3023
|
-
super();
|
|
3024
2996
|
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
2997
|
+
async updateConfig(newConfig) {
|
|
2998
|
+
// sequence_updateConfig
|
|
2999
|
+
const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
|
|
3000
|
+
|
|
3001
|
+
if (tx === null) {
|
|
3002
|
+
return [config, undefined];
|
|
3003
|
+
}
|
|
3028
3004
|
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
this.handleMessage(message);
|
|
3034
|
-
});
|
|
3035
|
-
}
|
|
3036
|
-
});
|
|
3037
|
-
}
|
|
3005
|
+
const provider = await this.getSender(this.defaultChainId);
|
|
3006
|
+
return [config, provider._wrapTransaction(tx, tx.hash)];
|
|
3007
|
+
} // publishConfig..
|
|
3008
|
+
// NOTE: this is not supported by the remote wallet by default.
|
|
3038
3009
|
|
|
3039
|
-
sendMessage(message) {//noop
|
|
3040
|
-
}
|
|
3041
3010
|
|
|
3042
|
-
|
|
3043
|
-
|
|
3011
|
+
async publishConfig() {
|
|
3012
|
+
const provider = await this.getSender(this.defaultChainId);
|
|
3013
|
+
const tx = await provider.send('sequence_publishConfig', []);
|
|
3044
3014
|
|
|
3045
|
-
|
|
3046
|
-
|
|
3015
|
+
if (tx === null) {
|
|
3016
|
+
return undefined;
|
|
3017
|
+
}
|
|
3047
3018
|
|
|
3048
|
-
|
|
3019
|
+
return provider._wrapTransaction(tx, tx.hash);
|
|
3049
3020
|
}
|
|
3050
3021
|
|
|
3051
|
-
|
|
3022
|
+
async isDeployed(chainId) {
|
|
3023
|
+
const provider = await this.getSender(network.maybeChainId(chainId));
|
|
3024
|
+
const walletCode = await provider.getCode(await this.getAddress());
|
|
3025
|
+
return !!walletCode && walletCode !== '0x';
|
|
3026
|
+
} //
|
|
3027
|
+
// ethers JsonRpcSigner methods
|
|
3028
|
+
//
|
|
3052
3029
|
|
|
3053
|
-
// to be used on injected window.ethereum EIP1193 proxy
|
|
3054
|
-
class BaseInjectedTransport extends eventemitter2.EventEmitter2 {
|
|
3055
|
-
constructor(stream) {
|
|
3056
|
-
var _this;
|
|
3057
3030
|
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3031
|
+
async _legacySignMessage(message, chainId, allSigners) {
|
|
3032
|
+
const provider = await this.getSender(network.maybeChainId(chainId) || this.defaultChainId);
|
|
3033
|
+
const data = typeof message === 'string' ? ethers.ethers.utils.toUtf8Bytes(message) : message;
|
|
3034
|
+
const address = await this.getAddress(); // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
|
|
3035
|
+
// NOTE: ethers since 5.5 has switched to using personal_sign, we should review, etc.
|
|
3063
3036
|
|
|
3064
|
-
|
|
3037
|
+
return await provider.send('eth_sign', [address, ethers.ethers.utils.hexlify(data)]);
|
|
3038
|
+
}
|
|
3065
3039
|
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
}
|
|
3040
|
+
async _signTypedData(domain, types, message, chainId, allSigners) {
|
|
3041
|
+
return this.signTypedData(domain, types, message, chainId, allSigners);
|
|
3042
|
+
}
|
|
3070
3043
|
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3044
|
+
async sendUncheckedTransaction(transaction, chainId) {
|
|
3045
|
+
transaction = utils.shallowCopy(transaction);
|
|
3046
|
+
const fromAddress = this.getAddress(); // NOTE: we do not use provider estimation, and instead rely on our relayer to determine the gasLimit and gasPrice
|
|
3047
|
+
//
|
|
3048
|
+
// TODO: alternatively/one day, we could write a provider middleware to eth_estimateGas
|
|
3049
|
+
// and send it to our relayer url instead for estimation..
|
|
3050
|
+
//
|
|
3051
|
+
// if (!transaction.gasLimit) {
|
|
3052
|
+
// const estimate = shallowCopy(transaction)
|
|
3053
|
+
// estimate.from = fromAddress
|
|
3054
|
+
// transaction.gasLimit = this.provider.estimateGas(estimate)
|
|
3055
|
+
// }
|
|
3074
3056
|
|
|
3075
|
-
|
|
3076
|
-
|
|
3057
|
+
const provider = await this.getSender(network.maybeChainId(chainId) || this.defaultChainId);
|
|
3058
|
+
return utils.resolveProperties({
|
|
3059
|
+
tx: utils.resolveProperties(transaction),
|
|
3060
|
+
sender: await fromAddress
|
|
3061
|
+
}).then(({
|
|
3062
|
+
tx,
|
|
3063
|
+
sender
|
|
3064
|
+
}) => {
|
|
3065
|
+
if (tx.from != null) {
|
|
3066
|
+
if (ethers.ethers.utils.getAddress(tx.from) !== sender) {
|
|
3067
|
+
// logger.throwArgumentError("from address mismatch", "transaction", transaction)
|
|
3068
|
+
throw new Error(`from address mismatch for transaction ${transaction}`);
|
|
3069
|
+
}
|
|
3070
|
+
} else {
|
|
3071
|
+
tx.from = sender;
|
|
3077
3072
|
}
|
|
3078
3073
|
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
throw new Error('impossible state');
|
|
3089
|
-
}
|
|
3074
|
+
const hexTx = hexlifyTransaction(tx);
|
|
3075
|
+
return provider.send('eth_sendTransaction', [hexTx]).then(hash => {
|
|
3076
|
+
return hash;
|
|
3077
|
+
}, error => {
|
|
3078
|
+
// return checkError("sendTransaction", error, hexTx)
|
|
3079
|
+
throw new Error(`sendTransaction ${error}`);
|
|
3080
|
+
});
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3090
3083
|
|
|
3091
|
-
|
|
3084
|
+
connectUnchecked() {
|
|
3085
|
+
throw new Error('connectUnchecked is unsupported');
|
|
3086
|
+
}
|
|
3092
3087
|
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
break;
|
|
3088
|
+
async unlock(password) {
|
|
3089
|
+
const address = await this.getAddress();
|
|
3090
|
+
return this.provider.send('personal_unlockAccount', [address, password, null]);
|
|
3091
|
+
}
|
|
3098
3092
|
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
break;
|
|
3102
|
-
}
|
|
3103
|
-
};
|
|
3093
|
+
} // NOTE: method has been copied + modified from ethers.js JsonRpcProvider
|
|
3094
|
+
// Convert an ethers.js transaction into a JSON-RPC transaction
|
|
3104
3095
|
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3096
|
+
const allowedTransactionKeys = {
|
|
3097
|
+
chainId: true,
|
|
3098
|
+
data: true,
|
|
3099
|
+
gasLimit: true,
|
|
3100
|
+
gasPrice: true,
|
|
3101
|
+
nonce: true,
|
|
3102
|
+
to: true,
|
|
3103
|
+
value: true,
|
|
3104
|
+
from: true,
|
|
3105
|
+
auxiliary: true,
|
|
3106
|
+
expiration: true,
|
|
3107
|
+
afterNonce: true,
|
|
3108
|
+
delegateCall: true,
|
|
3109
|
+
revertOnError: true
|
|
3110
|
+
};
|
|
3110
3111
|
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
} else if (response) {
|
|
3115
|
-
resolve(response);
|
|
3116
|
-
} else {
|
|
3117
|
-
throw new Error('no valid response to return');
|
|
3118
|
-
}
|
|
3119
|
-
};
|
|
3112
|
+
const hexlifyTransaction = (transaction, allowExtra) => {
|
|
3113
|
+
// Check only allowed properties are given
|
|
3114
|
+
const allowed = utils.shallowCopy(allowedTransactionKeys);
|
|
3120
3115
|
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3116
|
+
if (allowExtra) {
|
|
3117
|
+
for (const key in allowExtra) {
|
|
3118
|
+
if (allowExtra[key]) {
|
|
3119
|
+
allowed[key] = true;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3124
3123
|
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3124
|
+
ethers.ethers.utils.checkProperties(transaction, allowed);
|
|
3125
|
+
const result = {} // Some nodes (INFURA ropsten; INFURA mainnet is fine) do not like leading zeros.
|
|
3126
|
+
;
|
|
3127
|
+
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(key => {
|
|
3128
|
+
if (!transaction[key]) {
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3130
3131
|
|
|
3131
|
-
|
|
3132
|
-
});
|
|
3133
|
-
};
|
|
3132
|
+
const value = ethers.ethers.utils.hexValue(transaction[key]);
|
|
3134
3133
|
|
|
3135
|
-
|
|
3136
|
-
|
|
3134
|
+
if (key === 'gasLimit') {
|
|
3135
|
+
key = 'gas';
|
|
3136
|
+
}
|
|
3137
3137
|
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3138
|
+
result[key] = value;
|
|
3139
|
+
});
|
|
3140
|
+
['from', 'to', 'data'].forEach(key => {
|
|
3141
|
+
if (!transaction[key]) {
|
|
3142
|
+
return;
|
|
3141
3143
|
}
|
|
3142
3144
|
|
|
3143
|
-
|
|
3145
|
+
result[key] = ethers.ethers.utils.hexlify(transaction[key]);
|
|
3146
|
+
});
|
|
3147
|
+
const auxiliary = transaction['auxiliary'];
|
|
3148
|
+
|
|
3149
|
+
if (auxiliary && auxiliary.length > 0) {
|
|
3150
|
+
result['auxiliary'] = [];
|
|
3151
|
+
auxiliary.forEach(a => {
|
|
3152
|
+
result['auxiliary'].push(hexlifyTransaction(a));
|
|
3153
|
+
});
|
|
3144
3154
|
}
|
|
3145
3155
|
|
|
3146
|
-
|
|
3156
|
+
return result;
|
|
3157
|
+
};
|
|
3147
3158
|
|
|
3148
3159
|
class WalletUtils {
|
|
3149
3160
|
constructor(walletProvider) {
|
|
3150
3161
|
var _this = this;
|
|
3151
3162
|
|
|
3152
3163
|
this.wallet = void 0;
|
|
3164
|
+
this.configTracker = void 0;
|
|
3153
3165
|
|
|
3154
3166
|
this.recoverWalletConfig = async function (address, digest, signature, chainId, walletContext) {
|
|
3155
3167
|
walletContext = walletContext || (await _this.wallet.getWalletContext());
|
|
3156
|
-
|
|
3168
|
+
|
|
3169
|
+
const provider = _this.wallet.getProvider(chainId);
|
|
3170
|
+
|
|
3171
|
+
if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
|
|
3172
|
+
const configTracker = await _this._configTracker();
|
|
3173
|
+
return recoverWalletConfig(address, digest, signature, chainId, provider, configTracker, walletContext);
|
|
3157
3174
|
};
|
|
3158
3175
|
|
|
3159
3176
|
this.recoverWalletConfigFromMessage = async function (address, message, signature, chainId, walletContext) {
|
|
3160
3177
|
walletContext = walletContext || (await _this.wallet.getWalletContext());
|
|
3161
|
-
|
|
3178
|
+
|
|
3179
|
+
const provider = _this.wallet.getProvider(chainId);
|
|
3180
|
+
|
|
3181
|
+
if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
|
|
3182
|
+
const configTracker = await _this._configTracker();
|
|
3183
|
+
return recoverWalletConfig(address, utils.encodeMessageDigest(wallet.prefixEIP191Message(message)), signature, chainId, provider, configTracker, walletContext);
|
|
3162
3184
|
};
|
|
3163
3185
|
|
|
3164
3186
|
this.recoverWalletConfigFromTypedData = async function (address, typedData, signature, chainId, walletContext) {
|
|
3165
3187
|
walletContext = walletContext || (await _this.wallet.getWalletContext());
|
|
3166
|
-
|
|
3188
|
+
|
|
3189
|
+
const provider = _this.wallet.getProvider(chainId);
|
|
3190
|
+
|
|
3191
|
+
if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
|
|
3192
|
+
const configTracker = await _this._configTracker();
|
|
3193
|
+
return recoverWalletConfig(address, utils.encodeTypedDataDigest(typedData), signature, chainId, provider, configTracker, walletContext);
|
|
3167
3194
|
};
|
|
3168
3195
|
|
|
3169
3196
|
this.wallet = walletProvider;
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
async _configTracker() {
|
|
3200
|
+
if (this.configTracker) return this.configTracker;
|
|
3201
|
+
const res = await this.wallet.getConfigTracker();
|
|
3202
|
+
if (res) this.configTracker = res;
|
|
3203
|
+
return res;
|
|
3170
3204
|
} // Sign message on a specified chain, or DefaultChain by default
|
|
3171
3205
|
|
|
3172
3206
|
|
|
@@ -3174,13 +3208,6 @@ class WalletUtils {
|
|
|
3174
3208
|
const signer = this.wallet.getSigner();
|
|
3175
3209
|
if (!signer) throw new Error('unable to get signer');
|
|
3176
3210
|
return signer.signMessage(message, chainId, allSigners);
|
|
3177
|
-
} // Sign message on the AuthChain
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
async signAuthMessage(message, allSigners) {
|
|
3181
|
-
const signer = await this.wallet.getAuthSigner();
|
|
3182
|
-
if (!signer) throw new Error('unable to get AuthChain signer');
|
|
3183
|
-
return signer.signMessage(message, await signer.getChainId(), allSigners);
|
|
3184
3211
|
} // Sign EIP-712 TypedData on a specified chain, or DefaultChain by default
|
|
3185
3212
|
|
|
3186
3213
|
|
|
@@ -3188,29 +3215,41 @@ class WalletUtils {
|
|
|
3188
3215
|
const signer = this.wallet.getSigner();
|
|
3189
3216
|
if (!signer) throw new Error('unable to get signer');
|
|
3190
3217
|
return signer.signTypedData(domain, types, message, chainId, allSigners);
|
|
3191
|
-
} // Sign EIP-712 TypedData on the AuthChain
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
async signAuthTypedData(domain, types, message, allSigners) {
|
|
3195
|
-
const signer = await this.wallet.getAuthSigner();
|
|
3196
|
-
if (!signer) throw new Error('unable to get AuthChain signer');
|
|
3197
|
-
return signer.signTypedData(domain, types, message, await signer.getChainId(), allSigners);
|
|
3198
3218
|
} // Verify signature of a digest, one of a message, typedData or other
|
|
3199
3219
|
|
|
3200
3220
|
|
|
3201
3221
|
async isValidSignature(address, digest, signature, chainId, walletContext) {
|
|
3202
3222
|
const provider = this.wallet.getProvider(chainId);
|
|
3203
3223
|
if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
|
|
3204
|
-
return isValidSignature(address, digest, signature, provider, chainId, walletContext)
|
|
3224
|
+
const configTracker = await this._configTracker(); // return isValidSignature(address, digest, signature, provider, chainId, walletContext, configTracker)
|
|
3225
|
+
|
|
3226
|
+
return wallet.isValidSignature({
|
|
3227
|
+
address,
|
|
3228
|
+
digest,
|
|
3229
|
+
signature,
|
|
3230
|
+
chainId,
|
|
3231
|
+
context: walletContext,
|
|
3232
|
+
provider,
|
|
3233
|
+
configTracker
|
|
3234
|
+
});
|
|
3205
3235
|
} // Verify message signature
|
|
3206
3236
|
|
|
3207
3237
|
|
|
3208
3238
|
async isValidMessageSignature(address, message, signature, chainId, walletContext) {
|
|
3209
3239
|
const provider = this.wallet.getProvider(chainId);
|
|
3210
3240
|
if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`);
|
|
3211
|
-
const
|
|
3241
|
+
const configTracker = await this._configTracker();
|
|
3242
|
+
const prefixed = wallet.prefixEIP191Message(message);
|
|
3212
3243
|
const digest = utils.encodeMessageDigest(prefixed);
|
|
3213
|
-
return isValidSignature(
|
|
3244
|
+
return wallet.isValidSignature({
|
|
3245
|
+
address,
|
|
3246
|
+
digest,
|
|
3247
|
+
signature,
|
|
3248
|
+
chainId,
|
|
3249
|
+
context: walletContext,
|
|
3250
|
+
provider,
|
|
3251
|
+
configTracker
|
|
3252
|
+
});
|
|
3214
3253
|
} // Verify typedData signature
|
|
3215
3254
|
|
|
3216
3255
|
|
|
@@ -3469,20 +3508,6 @@ class Wallet {
|
|
|
3469
3508
|
return network.chainId;
|
|
3470
3509
|
};
|
|
3471
3510
|
|
|
3472
|
-
this.getAuthChainId = async function () {
|
|
3473
|
-
if (!_this.networks || _this.networks.length < 1) {
|
|
3474
|
-
throw new Error('networks have not been set by session. connect first.');
|
|
3475
|
-
}
|
|
3476
|
-
|
|
3477
|
-
const network = _this.networks.find(network => network.isAuthChain);
|
|
3478
|
-
|
|
3479
|
-
if (!network) {
|
|
3480
|
-
throw new Error('networks must have an auth chain specified');
|
|
3481
|
-
}
|
|
3482
|
-
|
|
3483
|
-
return network.chainId;
|
|
3484
|
-
};
|
|
3485
|
-
|
|
3486
3511
|
this.openWallet = async function (path, intent, networkId) {
|
|
3487
3512
|
if ((intent == null ? void 0 : intent.type) !== 'connect' && !_this.isConnected()) {
|
|
3488
3513
|
throw new Error('connect first');
|
|
@@ -3689,14 +3714,6 @@ class Wallet {
|
|
|
3689
3714
|
return provider;
|
|
3690
3715
|
}
|
|
3691
3716
|
|
|
3692
|
-
async getAuthProvider() {
|
|
3693
|
-
return this.getProvider((await this.getAuthNetwork()).chainId);
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
|
-
async getAuthNetwork() {
|
|
3697
|
-
return (await this.getNetworks()).find(n => n.isAuthChain);
|
|
3698
|
-
}
|
|
3699
|
-
|
|
3700
3717
|
getAllProviders() {
|
|
3701
3718
|
return this.providers;
|
|
3702
3719
|
}
|
|
@@ -3705,10 +3722,6 @@ class Wallet {
|
|
|
3705
3722
|
return this.getProvider(chainId).getSigner();
|
|
3706
3723
|
}
|
|
3707
3724
|
|
|
3708
|
-
async getAuthSigner() {
|
|
3709
|
-
return (await this.getAuthProvider()).getSigner();
|
|
3710
|
-
}
|
|
3711
|
-
|
|
3712
3725
|
getWalletConfig(chainId) {
|
|
3713
3726
|
return this.getSigner().getWalletConfig(chainId);
|
|
3714
3727
|
}
|
|
@@ -3725,6 +3738,11 @@ class Wallet {
|
|
|
3725
3738
|
return this.getSigner(chainId).isDeployed();
|
|
3726
3739
|
}
|
|
3727
3740
|
|
|
3741
|
+
async getConfigTracker() {
|
|
3742
|
+
const options = await this.getSigner().getConfigTracker();
|
|
3743
|
+
return options && new config.SessionsApiConfigTracker(options);
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3728
3746
|
on(event, fn) {
|
|
3729
3747
|
this.transport.messageProvider.on(event, fn);
|
|
3730
3748
|
}
|
|
@@ -3752,8 +3770,10 @@ class Wallet {
|
|
|
3752
3770
|
if (!this.config.networks && !this.config.networkRpcUrl) {
|
|
3753
3771
|
this.networks = networks;
|
|
3754
3772
|
return;
|
|
3755
|
-
} //
|
|
3773
|
+
} // init networks
|
|
3774
|
+
|
|
3756
3775
|
|
|
3776
|
+
this.networks = networks; // combine custom network config with networks in the session
|
|
3757
3777
|
|
|
3758
3778
|
if (this.config.networks) {
|
|
3759
3779
|
this.networks = networks.map(n => _extends({}, n)); // copy
|
|
@@ -3768,7 +3788,11 @@ class Wallet {
|
|
|
3768
3788
|
|
|
3769
3789
|
|
|
3770
3790
|
if (this.config.networkRpcUrl) {
|
|
3771
|
-
this.networks.find(network => network.isDefaultChain)
|
|
3791
|
+
const network = this.networks.find(network => network.isDefaultChain);
|
|
3792
|
+
|
|
3793
|
+
if (network) {
|
|
3794
|
+
network.rpcUrl = this.config.networkRpcUrl;
|
|
3795
|
+
}
|
|
3772
3796
|
}
|
|
3773
3797
|
}
|
|
3774
3798
|
|
|
@@ -3824,11 +3848,5 @@ exports.WindowMessageProvider = WindowMessageProvider;
|
|
|
3824
3848
|
exports.WindowSessionParams = WindowSessionParams;
|
|
3825
3849
|
exports.isBrowserExtension = isBrowserExtension;
|
|
3826
3850
|
exports.isSequenceProvider = isSequenceProvider;
|
|
3827
|
-
exports.isValidMessageSignature = isValidMessageSignature;
|
|
3828
|
-
exports.isValidSignature = isValidSignature;
|
|
3829
|
-
exports.isValidTypedDataSignature = isValidTypedDataSignature;
|
|
3830
|
-
exports.isWalletUpToDate = isWalletUpToDate;
|
|
3831
|
-
exports.messageToBytes = messageToBytes;
|
|
3832
3851
|
exports.nextMessageIdx = nextMessageIdx;
|
|
3833
|
-
exports.prefixEIP191Message = prefixEIP191Message;
|
|
3834
3852
|
exports.recoverWalletConfig = recoverWalletConfig;
|