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