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