@0xsequence/provider 0.35.8 → 0.35.12
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 -2023
- package/dist/0xsequence-provider.cjs.prod.js +2046 -2023
- package/dist/0xsequence-provider.esm.js +2047 -2026
- package/dist/declarations/src/{commands → utils}/index.d.ts +1 -1
- package/dist/declarations/src/utils.d.ts +5 -2
- package/dist/declarations/src/wallet.d.ts +3 -3
- package/package.json +8 -8
- package/src/provider.ts +1 -0
- package/src/transports/window-transport/window-message-provider.ts +3 -1
- package/src/{commands → utils}/index.ts +10 -3
- package/src/utils.ts +38 -6
- package/src/wallet.ts +4 -4
|
@@ -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,1833 +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
|
-
throw new Error('A WindowMessageProvider is already registered. There can only be one.');
|
|
1380
|
-
} // listen for incoming messages from wallet
|
|
1381
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.
|
|
1382
1455
|
|
|
1383
|
-
window.addEventListener('message', this.onWindowEvent);
|
|
1384
|
-
registeredWindowMessageProvider = this; // open heartbeat
|
|
1385
1456
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
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;
|
|
1393
1464
|
}
|
|
1394
|
-
}, 500);
|
|
1395
|
-
}); // close clean up
|
|
1396
1465
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1466
|
+
return provider._wrapTransaction(tx, hash);
|
|
1467
|
+
});
|
|
1468
|
+
}, {
|
|
1469
|
+
onceBlock: this.provider
|
|
1470
|
+
}).catch(error => {
|
|
1471
|
+
error.transactionHash = hash;
|
|
1472
|
+
throw error;
|
|
1402
1473
|
});
|
|
1403
|
-
|
|
1404
|
-
};
|
|
1474
|
+
}); // @ts-ignore
|
|
1405
1475
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
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.
|
|
1409
1479
|
|
|
1410
|
-
if (registeredWindowMessageProvider === this) {
|
|
1411
|
-
registeredWindowMessageProvider = undefined;
|
|
1412
|
-
}
|
|
1413
1480
|
|
|
1414
|
-
|
|
1481
|
+
async sendTransactionBatch(transactions, chainId, allSigners) {
|
|
1482
|
+
const batch = await wallet.resolveArrayProperties(transactions);
|
|
1415
1483
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
this.openWallet = (path, intent, networkId) => {
|
|
1420
|
-
if (this.walletWindow && this.isOpened()) {
|
|
1421
|
-
// TODO: update the location of window to path
|
|
1422
|
-
this.walletWindow.focus();
|
|
1423
|
-
return;
|
|
1424
|
-
} // Instantiate new walletURL for this call
|
|
1484
|
+
if (!batch || batch.length === 0) {
|
|
1485
|
+
throw new Error('cannot send empty batch');
|
|
1486
|
+
} // sendTransactionBatch only accepts TransactionRequest, not TransactionResponses
|
|
1425
1487
|
|
|
1426
1488
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
if (path && path !== '') {
|
|
1431
|
-
walletURL.pathname = path.toLowerCase();
|
|
1432
|
-
} // Set session, intent and network id on walletURL
|
|
1489
|
+
if (batch.find(v => v.wait !== undefined && v.wait !== null)) {
|
|
1490
|
+
throw new Error('transaction request expected for sendTransactionBatch, transaction response found');
|
|
1491
|
+
}
|
|
1433
1492
|
|
|
1493
|
+
const tx = _extends({}, batch[0]);
|
|
1434
1494
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1495
|
+
if (batch.length > 1) {
|
|
1496
|
+
tx.auxiliary = batch.splice(1);
|
|
1497
|
+
}
|
|
1438
1498
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
// when connecting to the wallet, however, this will be verified and enforced
|
|
1442
|
-
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped.
|
|
1443
|
-
if (intent.type === 'connect') {
|
|
1444
|
-
if (!intent.options) intent.options = {}; // skip setting origin host if we're in an browser extension execution context
|
|
1445
|
-
// allow origin that is passed in
|
|
1499
|
+
return this.sendTransaction(tx, chainId, allSigners);
|
|
1500
|
+
}
|
|
1446
1501
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
} // 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
|
|
1451
1505
|
|
|
1506
|
+
return this.provider.send('eth_signTransaction', [transaction], network.maybeChainId(chainId) || this.defaultChainId);
|
|
1507
|
+
}
|
|
1452
1508
|
|
|
1453
|
-
|
|
1454
|
-
|
|
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.
|
|
1455
1514
|
|
|
1456
|
-
if (networkId) {
|
|
1457
|
-
windowSessionParams.set('net', `${networkId}`);
|
|
1458
|
-
} // Open popup window on center of the app window
|
|
1459
1515
|
|
|
1516
|
+
async updateConfig(newConfig) {
|
|
1517
|
+
// sequence_updateConfig
|
|
1518
|
+
const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
|
|
1460
1519
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1520
|
+
if (tx === null) {
|
|
1521
|
+
return [config, undefined];
|
|
1522
|
+
}
|
|
1463
1523
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
windowSize = [450, 750];
|
|
1469
|
-
windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
|
|
1470
|
-
}
|
|
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.
|
|
1471
1528
|
|
|
1472
|
-
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
|
|
1473
1529
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
// Popup blocking detection and notice
|
|
1478
|
-
// let warned = false
|
|
1479
|
-
// const warnPopupBlocked = () => {
|
|
1480
|
-
// if (warned) return
|
|
1481
|
-
// warned = true
|
|
1482
|
-
// // alert('popup is blocked! hey yo') // NOTE: for debug purposes only
|
|
1483
|
-
// throw new Error('popup is blocked')
|
|
1484
|
-
// }
|
|
1485
|
-
// const popupCheck = setTimeout(() => {
|
|
1486
|
-
// if (!popup || popup.closed || typeof popup.closed === 'undefined') {
|
|
1487
|
-
// // popup is definitely blocked if we reach here.
|
|
1488
|
-
// warnPopupBlocked()
|
|
1489
|
-
// }
|
|
1490
|
-
// }, 1000)
|
|
1491
|
-
// const popupBlocked = popup === null || popup === undefined
|
|
1492
|
-
// if (popupBlocked) {
|
|
1493
|
-
// warnPopupBlocked()
|
|
1494
|
-
// return
|
|
1495
|
-
// }
|
|
1496
|
-
};
|
|
1530
|
+
async publishConfig() {
|
|
1531
|
+
const provider = await this.getSender(this.defaultChainId);
|
|
1532
|
+
const tx = await provider.send('sequence_publishConfig', []);
|
|
1497
1533
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
// Safetly can skip events not from the wallet
|
|
1502
|
-
return;
|
|
1503
|
-
}
|
|
1534
|
+
if (tx === null) {
|
|
1535
|
+
return undefined;
|
|
1536
|
+
}
|
|
1504
1537
|
|
|
1505
|
-
|
|
1538
|
+
return provider._wrapTransaction(tx, tx.hash);
|
|
1539
|
+
}
|
|
1506
1540
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
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
|
+
//
|
|
1513
1548
|
|
|
1514
|
-
if (!message) {
|
|
1515
|
-
throw new Error('ProviderMessage object is empty');
|
|
1516
|
-
} // handle message with base message provider
|
|
1517
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.
|
|
1518
1555
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1556
|
+
return await provider.send('eth_sign', [address, ethers.ethers.utils.hexlify(data)]);
|
|
1557
|
+
}
|
|
1521
1558
|
|
|
1522
|
-
|
|
1559
|
+
async _signTypedData(domain, types, message, chainId, allSigners) {
|
|
1560
|
+
return this.signTypedData(domain, types, message, chainId, allSigners);
|
|
1523
1561
|
}
|
|
1524
1562
|
|
|
1525
|
-
|
|
1526
|
-
|
|
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
|
+
// }
|
|
1527
1575
|
|
|
1528
|
-
this.
|
|
1529
|
-
|
|
1530
|
-
|
|
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
|
+
}
|
|
1531
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
|
+
}
|
|
1532
1602
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1603
|
+
connectUnchecked() {
|
|
1604
|
+
throw new Error('connectUnchecked is unsupported');
|
|
1605
|
+
}
|
|
1538
1606
|
|
|
1539
|
-
|
|
1540
|
-
this.
|
|
1607
|
+
async unlock(password) {
|
|
1608
|
+
const address = await this.getAddress();
|
|
1609
|
+
return this.provider.send('personal_unlockAccount', [address, password, null]);
|
|
1541
1610
|
}
|
|
1542
1611
|
|
|
1543
|
-
}
|
|
1612
|
+
} // NOTE: method has been copied + modified from ethers.js JsonRpcProvider
|
|
1613
|
+
// Convert an ethers.js transaction into a JSON-RPC transaction
|
|
1544
1614
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
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
|
+
};
|
|
1548
1630
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
this._isPopup = false;
|
|
1631
|
+
const hexlifyTransaction = (transaction, allowExtra) => {
|
|
1632
|
+
// Check only allowed properties are given
|
|
1633
|
+
const allowed = utils.shallowCopy(allowedTransactionKeys);
|
|
1553
1634
|
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1635
|
+
if (allowExtra) {
|
|
1636
|
+
for (const key in allowExtra) {
|
|
1637
|
+
if (allowExtra[key]) {
|
|
1638
|
+
allowed[key] = true;
|
|
1558
1639
|
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1559
1642
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
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
|
+
}
|
|
1564
1650
|
|
|
1651
|
+
const value = ethers.ethers.utils.hexValue(transaction[key]);
|
|
1565
1652
|
|
|
1566
|
-
|
|
1653
|
+
if (key === 'gasLimit') {
|
|
1654
|
+
key = 'gas';
|
|
1655
|
+
}
|
|
1567
1656
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1657
|
+
result[key] = value;
|
|
1658
|
+
});
|
|
1659
|
+
['from', 'to', 'data'].forEach(key => {
|
|
1660
|
+
if (!transaction[key]) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1574
1663
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
_this.appOrigin = event.origin;
|
|
1579
|
-
}
|
|
1664
|
+
result[key] = ethers.ethers.utils.hexlify(transaction[key]);
|
|
1665
|
+
});
|
|
1666
|
+
const auxiliary = transaction['auxiliary'];
|
|
1580
1667
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1668
|
+
if (auxiliary && auxiliary.length > 0) {
|
|
1669
|
+
result['auxiliary'] = [];
|
|
1670
|
+
auxiliary.forEach(a => {
|
|
1671
|
+
result['auxiliary'].push(hexlifyTransaction(a));
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1586
1674
|
|
|
1675
|
+
return result;
|
|
1676
|
+
};
|
|
1587
1677
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1678
|
+
const isValidSignature = async (address, digest, sig, provider, chainId, walletContext) => {
|
|
1679
|
+
if (!chainId) {
|
|
1680
|
+
var _await$provider$getNe;
|
|
1590
1681
|
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
return {
|
|
1594
|
-
sessionId: params.get('sid'),
|
|
1595
|
-
networkId: params.get('net'),
|
|
1596
|
-
intent: utils.base64DecodeObject(params.get('intent'))
|
|
1597
|
-
};
|
|
1598
|
-
};
|
|
1682
|
+
chainId = (_await$provider$getNe = await provider.getNetwork()) == null ? void 0 : _await$provider$getNe.chainId;
|
|
1683
|
+
}
|
|
1599
1684
|
|
|
1600
|
-
|
|
1685
|
+
if (!walletContext && Web3Provider.isSequenceProvider(provider)) {
|
|
1686
|
+
walletContext = await provider.getSigner().getWalletContext();
|
|
1601
1687
|
}
|
|
1602
1688
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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);
|
|
1606
1702
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
} // record open details (sessionId + default network) from the window url
|
|
1703
|
+
if (walletContext) {
|
|
1704
|
+
const recoveredWalletAddress = config.addressOf(config$1, walletContext);
|
|
1610
1705
|
|
|
1706
|
+
if (config$1.address && config$1.address !== recoveredWalletAddress) {
|
|
1707
|
+
throw new Error('recovered address does not match the WalletConfig address, check the WalletContext');
|
|
1708
|
+
} else {
|
|
1709
|
+
config$1.address = recoveredWalletAddress;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1611
1712
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
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
|
+
*/
|
|
1617
1723
|
|
|
1618
|
-
|
|
1724
|
+
const isWalletUpToDate = async (signer, chainId) => {
|
|
1725
|
+
var _walletState$find;
|
|
1619
1726
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1727
|
+
const walletState = await signer.getWalletState();
|
|
1728
|
+
const networks = await signer.getNetworks();
|
|
1729
|
+
const walletStateForRequiredChain = walletState.find(state => state.chainId === chainId);
|
|
1623
1730
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
} // record parent window instance for communication
|
|
1731
|
+
if (!walletStateForRequiredChain) {
|
|
1732
|
+
throw new Error(`WalletRequestHandler: could not find wallet state for chainId ${chainId}`);
|
|
1733
|
+
}
|
|
1628
1734
|
|
|
1735
|
+
const isDeployed = walletStateForRequiredChain.deployed;
|
|
1629
1736
|
|
|
1630
|
-
|
|
1737
|
+
if (!networks) {
|
|
1738
|
+
throw new Error(`isWalletUpToDate util: could not get networks from signer`);
|
|
1739
|
+
}
|
|
1631
1740
|
|
|
1632
|
-
|
|
1633
|
-
this._registered = true; // send open event to the app which opened us
|
|
1741
|
+
const authChain = networks.find(network => network.isAuthChain);
|
|
1634
1742
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1743
|
+
if (!authChain) {
|
|
1744
|
+
throw new Error(`isWalletUpToDate util: could not get auth chain network information`);
|
|
1745
|
+
}
|
|
1638
1746
|
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
this.notifyClose({
|
|
1642
|
-
message: err
|
|
1643
|
-
});
|
|
1644
|
-
window.close();
|
|
1645
|
-
}
|
|
1646
|
-
}).catch(e => {
|
|
1647
|
-
var _session2;
|
|
1747
|
+
const authChainId = authChain.chainId;
|
|
1748
|
+
const authChainConfig = (_walletState$find = walletState.find(state => state.chainId === authChainId)) == null ? void 0 : _walletState$find.config;
|
|
1648
1749
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
this.notifyClose({
|
|
1652
|
-
message: err
|
|
1653
|
-
});
|
|
1654
|
-
window.close();
|
|
1655
|
-
});
|
|
1750
|
+
if (!authChainConfig) {
|
|
1751
|
+
throw new Error(`isWalletUpToDate util: could not get auth chain config`);
|
|
1656
1752
|
}
|
|
1657
1753
|
|
|
1658
|
-
|
|
1659
|
-
window.removeEventListener('message', this.onWindowEvent);
|
|
1660
|
-
this._registered = false;
|
|
1661
|
-
} // onmessage is called when (the wallet) receives request messages from the dapp
|
|
1662
|
-
// over the window post-messaging transport
|
|
1754
|
+
const requiredChainConfig = walletStateForRequiredChain.config;
|
|
1663
1755
|
|
|
1756
|
+
if (!requiredChainConfig) {
|
|
1757
|
+
throw new Error(`isWalletUpToDate util: could not get config for chainId ${chainId}`);
|
|
1758
|
+
}
|
|
1664
1759
|
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
const payload = JSON.stringify(message); // post-message to app.
|
|
1669
|
-
// only for init requests, we send to '*' origin
|
|
1760
|
+
const isUpToDate = config.isConfigEqual(authChainConfig, requiredChainConfig);
|
|
1761
|
+
return isDeployed && isUpToDate;
|
|
1762
|
+
}; // window.localstorage helper
|
|
1670
1763
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1764
|
+
class LocalStore {
|
|
1765
|
+
constructor(key, def) {
|
|
1766
|
+
this.def = def;
|
|
1767
|
+
this.key = void 0;
|
|
1768
|
+
this.key = key;
|
|
1676
1769
|
}
|
|
1677
1770
|
|
|
1678
|
-
get
|
|
1679
|
-
|
|
1680
|
-
}
|
|
1771
|
+
get() {
|
|
1772
|
+
const val = window.localStorage.getItem(this.key);
|
|
1681
1773
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
utils.logger.error('impossible state, should not be calling postMessage until inited');
|
|
1685
|
-
return;
|
|
1774
|
+
if (val === null) {
|
|
1775
|
+
return this.def;
|
|
1686
1776
|
}
|
|
1687
1777
|
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
// open message transmission
|
|
1693
|
-
if (this.appOrigin && this.appOrigin.length > 4) {
|
|
1694
|
-
// just above '.com'
|
|
1695
|
-
this.parentWindow.postMessage(message, this.appOrigin);
|
|
1696
|
-
} else {
|
|
1697
|
-
utils.logger.error('unable to postMessage as parentOrigin is invalid');
|
|
1698
|
-
}
|
|
1778
|
+
try {
|
|
1779
|
+
return JSON.parse(val);
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
console.error(err);
|
|
1699
1782
|
}
|
|
1783
|
+
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
set(val) {
|
|
1788
|
+
val ? window.localStorage.setItem(this.key, JSON.stringify(val)) : window.localStorage.removeItem(this.key);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
del() {
|
|
1792
|
+
window.localStorage.removeItem(this.key);
|
|
1700
1793
|
}
|
|
1701
1794
|
|
|
1702
1795
|
}
|
|
1703
1796
|
|
|
1704
|
-
const
|
|
1705
|
-
class
|
|
1706
|
-
//
|
|
1707
|
-
//
|
|
1708
|
-
//
|
|
1709
|
-
constructor(
|
|
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) {
|
|
1710
1803
|
var _this = this;
|
|
1711
1804
|
|
|
1712
|
-
this.
|
|
1713
|
-
this.
|
|
1714
|
-
this.
|
|
1715
|
-
this.
|
|
1716
|
-
this.
|
|
1717
|
-
this.
|
|
1718
|
-
this.
|
|
1719
|
-
this._defaultNetworkId = void 0;
|
|
1720
|
-
this._chainId = void 0;
|
|
1721
|
-
this.events = new eventemitter2.EventEmitter2();
|
|
1805
|
+
this.walletRequestHandler = void 0;
|
|
1806
|
+
this._sessionId = void 0;
|
|
1807
|
+
this._registered = void 0;
|
|
1808
|
+
this._init = void 0;
|
|
1809
|
+
this._initNonce = void 0;
|
|
1810
|
+
this._initCallback = void 0;
|
|
1811
|
+
this.appOrigin = void 0;
|
|
1722
1812
|
|
|
1723
|
-
this.
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
throw new Error('prompter connect options are empty');
|
|
1727
|
-
}
|
|
1813
|
+
this.sendAsync = async function (request, callback, chainId) {
|
|
1814
|
+
throw new Error('abstract method');
|
|
1815
|
+
};
|
|
1728
1816
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
}
|
|
1817
|
+
this.handleMessage = async function (message) {
|
|
1818
|
+
const request = message; // ensure initial handshake is complete before accepting
|
|
1819
|
+
// other kinds of messages.
|
|
1733
1820
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
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
|
+
}
|
|
1740
1832
|
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
|
1744
1839
|
|
|
1745
|
-
return promptConnectDetails;
|
|
1746
|
-
};
|
|
1747
1840
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
throw ErrSignedInRequired;
|
|
1761
|
-
} // wallet signer
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
const signer = _this.signer;
|
|
1765
|
-
if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured'); // fetch the provider for the specific chain, or undefined will select defaultChain
|
|
1766
|
-
|
|
1767
|
-
const provider = await signer.getProvider(chainId);
|
|
1768
|
-
if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`);
|
|
1769
|
-
|
|
1770
|
-
switch (request.method) {
|
|
1771
|
-
case 'net_version':
|
|
1772
|
-
{
|
|
1773
|
-
const result = await provider.send('net_version', []);
|
|
1774
|
-
response.result = result;
|
|
1775
|
-
break;
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
case 'eth_chainId':
|
|
1779
|
-
{
|
|
1780
|
-
const result = await provider.send('eth_chainId', []);
|
|
1781
|
-
response.result = result;
|
|
1782
|
-
break;
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
case 'eth_accounts':
|
|
1786
|
-
{
|
|
1787
|
-
const walletAddress = await signer.getAddress();
|
|
1788
|
-
response.result = [walletAddress];
|
|
1789
|
-
break;
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
case 'eth_getBalance':
|
|
1793
|
-
{
|
|
1794
|
-
const [accountAddress, blockTag] = request.params;
|
|
1795
|
-
const walletBalance = await provider.getBalance(accountAddress, blockTag);
|
|
1796
|
-
response.result = walletBalance.toHexString();
|
|
1797
|
-
break;
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1800
|
-
case 'personal_sign':
|
|
1801
|
-
case 'eth_sign':
|
|
1802
|
-
{
|
|
1803
|
-
// note: message from json-rpc input is in hex format
|
|
1804
|
-
let message; // there is a difference in the order of the params:
|
|
1805
|
-
// personal_sign: [data, address]
|
|
1806
|
-
// eth_sign: [address, data]
|
|
1807
|
-
|
|
1808
|
-
if (request.method === 'personal_sign') {
|
|
1809
|
-
const [data, address] = request.params;
|
|
1810
|
-
message = data;
|
|
1811
|
-
} else {
|
|
1812
|
-
const [address, data] = request.params;
|
|
1813
|
-
message = data;
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
let sig = ''; // TODO:
|
|
1817
|
-
// if (process.env.TEST_MODE === 'true' && this.prompter === null) {
|
|
1818
|
-
|
|
1819
|
-
if (_this.prompter === null) {
|
|
1820
|
-
// prompter is null, so we'll sign from here
|
|
1821
|
-
sig = await signer.signMessage(ethers.ethers.utils.arrayify(message), chainId);
|
|
1822
|
-
} else {
|
|
1823
|
-
const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
|
|
1824
|
-
|
|
1825
|
-
if (promptResultForDeployment) {
|
|
1826
|
-
sig = await _this.prompter.promptSignMessage({
|
|
1827
|
-
chainId: chainId,
|
|
1828
|
-
message: message
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
if (sig && sig.length > 0) {
|
|
1834
|
-
response.result = sig;
|
|
1835
|
-
} else {
|
|
1836
|
-
// The user has declined the request when value is null
|
|
1837
|
-
throw new Error('declined by user');
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
break;
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
case 'eth_signTypedData':
|
|
1844
|
-
case 'eth_signTypedData_v4':
|
|
1845
|
-
{
|
|
1846
|
-
// note: signingAddress from json-rpc input is in hex format, and typedDataObject
|
|
1847
|
-
// should be an object, but in some instances may be double string encoded
|
|
1848
|
-
const [signingAddress, typedDataObject] = request.params;
|
|
1849
|
-
let typedData = undefined;
|
|
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
|
+
}
|
|
1850
1853
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
} catch (e) {}
|
|
1855
|
-
} else {
|
|
1856
|
-
typedData = typedDataObject;
|
|
1857
|
-
}
|
|
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
|
|
1858
1857
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1862
1860
|
|
|
1863
|
-
|
|
1861
|
+
case exports.EventType.MESSAGE:
|
|
1862
|
+
{
|
|
1863
|
+
const response = await _this.walletRequestHandler.sendMessageRequest(request);
|
|
1864
1864
|
|
|
1865
|
-
|
|
1866
|
-
// prompter is null, so we'll sign from here
|
|
1867
|
-
sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId);
|
|
1868
|
-
} else {
|
|
1869
|
-
const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
|
|
1865
|
+
_this.sendMessage(response);
|
|
1870
1866
|
|
|
1871
|
-
|
|
1872
|
-
sig = await _this.prompter.promptSignMessage({
|
|
1873
|
-
chainId: chainId,
|
|
1874
|
-
typedData: typedData
|
|
1875
|
-
});
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1867
|
+
if (response.data.error) ;
|
|
1878
1868
|
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
} else {
|
|
1882
|
-
// The user has declined the request when value is null
|
|
1883
|
-
throw new Error('declined by user');
|
|
1884
|
-
}
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1885
1871
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1872
|
+
default:
|
|
1873
|
+
{
|
|
1874
|
+
utils.logger.error(`unexpected payload type ${request.type}`);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1888
1878
|
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
const [transactionParams] = request.params;
|
|
1893
|
-
let txnHash = '';
|
|
1879
|
+
this.sendMessageRequest = async function (message) {
|
|
1880
|
+
return _this.walletRequestHandler.sendMessageRequest(message);
|
|
1881
|
+
};
|
|
1894
1882
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
}
|
|
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
|
|
1903
1890
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1891
|
+
_this.saveTransportSession({
|
|
1892
|
+
sessionId,
|
|
1893
|
+
intent,
|
|
1894
|
+
networkId
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1910
1897
|
|
|
1911
|
-
|
|
1912
|
-
|
|
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.
|
|
1913
1902
|
|
|
1914
|
-
case 'eth_signTransaction':
|
|
1915
|
-
{
|
|
1916
|
-
// https://eth.wiki/json-rpc/API#eth_signTransaction
|
|
1917
|
-
const [transaction] = request.params;
|
|
1918
|
-
const sender = ethers.ethers.utils.getAddress(transaction.from);
|
|
1919
1903
|
|
|
1920
|
-
|
|
1921
|
-
throw new Error('sender address does not match wallet');
|
|
1922
|
-
}
|
|
1904
|
+
await _this.init(); // Prepare connect options from intent
|
|
1923
1905
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
// we will want to resolveProperties the bignumber values to hex strings
|
|
1930
|
-
response.result = await signer.signTransactions(transaction, chainId);
|
|
1931
|
-
} else {
|
|
1932
|
-
response.result = await _this.prompter.promptSignTransaction(transaction, chainId);
|
|
1933
|
-
}
|
|
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.
|
|
1934
1911
|
|
|
1935
|
-
|
|
1912
|
+
if (_this.appOrigin && authorizeOptions != null && authorizeOptions.origin) {
|
|
1913
|
+
if (!isBrowserExtension()) {
|
|
1914
|
+
if (authorizeOptions.origin !== _this.appOrigin) {
|
|
1915
|
+
throw new Error('origin is invalid');
|
|
1936
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
|
+
}
|
|
1937
1926
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
// https://eth.wiki/json-rpc/API#eth_sendRawTransaction
|
|
1943
|
-
if (transactions.isSignedTransactions(request.params[0])) {
|
|
1944
|
-
const txChainId = ethers.BigNumber.from(request.params[0].chainId).toNumber();
|
|
1945
|
-
const tx = await (await signer.getRelayer(txChainId)).relay(request.params[0]);
|
|
1946
|
-
response.result = (await tx).hash;
|
|
1947
|
-
} else {
|
|
1948
|
-
const tx = await provider.sendTransaction(request.params[0]);
|
|
1949
|
-
response.result = tx.hash;
|
|
1950
|
-
}
|
|
1927
|
+
if (connectOptions.app) {
|
|
1928
|
+
connectOptions.app = utils.sanitizeAlphanumeric(connectOptions.app);
|
|
1929
|
+
} // Set connect options on the walletRequestHandler as our primary
|
|
1930
|
+
// wallet controller
|
|
1951
1931
|
|
|
1952
|
-
break;
|
|
1953
|
-
}
|
|
1954
1932
|
|
|
1955
|
-
|
|
1956
|
-
{
|
|
1957
|
-
const address = ethers.ethers.utils.getAddress(request.params[0]);
|
|
1958
|
-
const tag = request.params[1];
|
|
1959
|
-
const walletAddress = ethers.ethers.utils.getAddress(await signer.getAddress());
|
|
1933
|
+
_this.walletRequestHandler.setConnectOptions(connectOptions);
|
|
1960
1934
|
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
}
|
|
1935
|
+
if (connectOptions.networkId) {
|
|
1936
|
+
networkId = connectOptions.networkId;
|
|
1937
|
+
}
|
|
1938
|
+
} else {
|
|
1939
|
+
_this.walletRequestHandler.setConnectOptions(undefined);
|
|
1940
|
+
} // ensure signer is ready
|
|
1968
1941
|
|
|
1969
|
-
break;
|
|
1970
|
-
}
|
|
1971
1942
|
|
|
1972
|
-
|
|
1973
|
-
{
|
|
1974
|
-
response.result = await provider.getBlockNumber();
|
|
1975
|
-
break;
|
|
1976
|
-
}
|
|
1943
|
+
await _this.walletRequestHandler.getSigner(); // Notify open and proceed to prompt for connection if intended
|
|
1977
1944
|
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
break;
|
|
1984
|
-
}
|
|
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
|
+
});
|
|
1985
1950
|
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
);
|
|
1991
|
-
break;
|
|
1992
|
-
}
|
|
1951
|
+
return true;
|
|
1952
|
+
} else {
|
|
1953
|
+
// Set default network, in case of error chainId will be undefined or 0
|
|
1954
|
+
let chainId = undefined;
|
|
1993
1955
|
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
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
|
|
1999
1965
|
|
|
2000
|
-
case 'eth_call':
|
|
2001
|
-
{
|
|
2002
|
-
const [transactionObject, blockTag] = request.params;
|
|
2003
|
-
response.result = await provider.call(transactionObject, blockTag);
|
|
2004
|
-
break;
|
|
2005
|
-
}
|
|
2006
1966
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
}
|
|
1967
|
+
if (!chainId || chainId <= 0) {
|
|
1968
|
+
_this.notifyOpen({
|
|
1969
|
+
sessionId: _this._sessionId,
|
|
1970
|
+
error: `failed to open wallet on network ${networkId}`
|
|
1971
|
+
});
|
|
2013
1972
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
break;
|
|
2019
|
-
}
|
|
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
|
|
2020
1977
|
|
|
2021
|
-
case 'eth_gasPrice':
|
|
2022
|
-
{
|
|
2023
|
-
const gasPrice = await provider.getGasPrice();
|
|
2024
|
-
response.result = gasPrice.toHexString();
|
|
2025
|
-
break;
|
|
2026
|
-
}
|
|
2027
|
-
// smart wallet method
|
|
2028
1978
|
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
// 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
|
+
});
|
|
2035
1984
|
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
const [_chainId] = request.params;
|
|
2039
|
-
response.result = await signer.getWalletConfig(_chainId);
|
|
2040
|
-
break;
|
|
2041
|
-
}
|
|
2042
|
-
// smart wallet method
|
|
1985
|
+
try {
|
|
1986
|
+
const connectDetails = await _this.walletRequestHandler.promptConnect(intent.options);
|
|
2043
1987
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const [_chainId2] = request.params;
|
|
2047
|
-
response.result = await signer.getWalletState(_chainId2);
|
|
2048
|
-
break;
|
|
1988
|
+
if (connectDetails.connected) {
|
|
1989
|
+
_this.walletRequestHandler.notifyConnect(connectDetails);
|
|
2049
1990
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
// This is handled by this.getNetworks() but noted here for future readers.
|
|
2057
|
-
response.result = await _this.getNetworks(true);
|
|
2058
|
-
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();
|
|
2059
1997
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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
|
+
}
|
|
2070
2010
|
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
// smart wallet method
|
|
2011
|
+
return true;
|
|
2012
|
+
};
|
|
2074
2013
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
break;
|
|
2079
|
-
}
|
|
2080
|
-
// relayer method
|
|
2014
|
+
this.saveTransportSession = session => {
|
|
2015
|
+
window.localStorage.setItem(TRANSPORT_SESSION_LS_KEY, JSON.stringify(session));
|
|
2016
|
+
};
|
|
2081
2017
|
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
// TODO
|
|
2085
|
-
break;
|
|
2086
|
-
}
|
|
2087
|
-
// relayer method
|
|
2018
|
+
this.getCachedTransportSession = () => {
|
|
2019
|
+
const session = window.localStorage.getItem(TRANSPORT_SESSION_LS_KEY);
|
|
2088
2020
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
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
|
+
};
|
|
2095
2028
|
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
}
|
|
2101
|
-
// 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
|
|
2102
2033
|
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
}
|
|
2108
|
-
// 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
|
|
2109
2038
|
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
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);
|
|
2113
2048
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
+
}
|
|
2117
2064
|
|
|
2118
|
-
|
|
2065
|
+
get registered() {
|
|
2066
|
+
return this._registered;
|
|
2067
|
+
}
|
|
2119
2068
|
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2069
|
+
register() {
|
|
2070
|
+
throw new Error('abstract method');
|
|
2071
|
+
}
|
|
2123
2072
|
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2073
|
+
unregister() {
|
|
2074
|
+
throw new Error('abstract method');
|
|
2075
|
+
}
|
|
2127
2076
|
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
const providerResponse = await provider.send(request.method, request.params);
|
|
2132
|
-
response.result = providerResponse;
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
} catch (err) {
|
|
2136
|
-
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
|
+
}
|
|
2137
2080
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
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
|
|
2142
2096
|
}
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2143
2099
|
|
|
2144
|
-
|
|
2145
|
-
|
|
2100
|
+
notifyClose(error) {
|
|
2101
|
+
this.sendMessage({
|
|
2102
|
+
idx: -1,
|
|
2103
|
+
type: exports.EventType.CLOSE,
|
|
2104
|
+
data: error ? {
|
|
2105
|
+
error
|
|
2106
|
+
} : null
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2146
2109
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2110
|
+
notifyConnect(connectDetails) {
|
|
2111
|
+
this.sendMessage({
|
|
2112
|
+
idx: -1,
|
|
2113
|
+
type: exports.EventType.CONNECT,
|
|
2114
|
+
data: connectDetails
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2151
2117
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2118
|
+
notifyDisconnect(error) {
|
|
2119
|
+
this.sendMessage({
|
|
2120
|
+
idx: -1,
|
|
2121
|
+
type: exports.EventType.DISCONNECT,
|
|
2122
|
+
data: error ? {
|
|
2123
|
+
error
|
|
2124
|
+
} : null
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2154
2127
|
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2128
|
+
notifyAccountsChanged(accounts) {
|
|
2129
|
+
this.sendMessage({
|
|
2130
|
+
idx: -1,
|
|
2131
|
+
type: exports.EventType.ACCOUNTS_CHANGED,
|
|
2132
|
+
data: accounts
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2158
2135
|
|
|
2159
|
-
|
|
2160
|
-
|
|
2136
|
+
notifyChainChanged(chainIdHex) {
|
|
2137
|
+
this.sendMessage({
|
|
2138
|
+
idx: -1,
|
|
2139
|
+
type: exports.EventType.CHAIN_CHANGED,
|
|
2140
|
+
data: chainIdHex
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2161
2143
|
|
|
2162
|
-
|
|
2163
|
-
this.
|
|
2164
|
-
|
|
2165
|
-
|
|
2144
|
+
notifyNetworks(networks) {
|
|
2145
|
+
this.sendMessage({
|
|
2146
|
+
idx: -1,
|
|
2147
|
+
type: exports.EventType.NETWORKS,
|
|
2148
|
+
data: networks
|
|
2149
|
+
});
|
|
2166
2150
|
}
|
|
2167
2151
|
|
|
2168
|
-
|
|
2169
|
-
this.
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
} = options;
|
|
2152
|
+
notifyWalletContext(walletContext) {
|
|
2153
|
+
this.sendMessage({
|
|
2154
|
+
idx: -1,
|
|
2155
|
+
type: exports.EventType.WALLET_CONTEXT,
|
|
2156
|
+
data: walletContext
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2176
2159
|
|
|
2177
|
-
|
|
2178
|
-
|
|
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;
|
|
2179
2165
|
}
|
|
2180
2166
|
|
|
2181
|
-
if (
|
|
2182
|
-
|
|
2167
|
+
if (message.type !== exports.EventType.INIT) {
|
|
2168
|
+
utils.logger.warn('isValidInitAck, invalid message type, expecting init');
|
|
2169
|
+
return false;
|
|
2183
2170
|
}
|
|
2184
2171
|
|
|
2185
|
-
|
|
2186
|
-
|
|
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;
|
|
2187
2180
|
}
|
|
2188
2181
|
|
|
2189
|
-
|
|
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
|
|
2190
2186
|
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
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;
|
|
2194
2198
|
}
|
|
2195
|
-
} // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform
|
|
2196
|
-
// necessary auth request, and then notify the dapp of the 'connect' details.
|
|
2197
|
-
//
|
|
2198
|
-
// NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made
|
|
2199
|
-
// we don't trigger the promptConnect flow, as we consider the user just authenticated
|
|
2200
|
-
// for this dapp, so its safe to authorize in the connect() method without the prompt.
|
|
2201
|
-
//
|
|
2202
|
-
// NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows
|
|
2203
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
|
|
2204
2205
|
|
|
2205
|
-
if (connect) {
|
|
2206
|
-
const connectOptions = this._connectOptions;
|
|
2207
|
-
const connectDetails = await this.connect(connectOptions);
|
|
2208
|
-
this.notifyConnect(connectDetails);
|
|
2209
2206
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
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
|
|
2228
|
+
|
|
2215
2229
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
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
|
+
});
|
|
2219
2241
|
}
|
|
2220
2242
|
|
|
2221
|
-
|
|
2222
|
-
// resetting signer puts the wallet in an uninitialized state, which requires the app to
|
|
2223
|
-
// re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in).
|
|
2224
|
-
this.signer = undefined;
|
|
2225
|
-
}
|
|
2243
|
+
}
|
|
2226
2244
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
}, timeout);
|
|
2238
|
-
this.signerReadyCallbacks.push(resolve);
|
|
2239
|
-
}
|
|
2240
|
-
});
|
|
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;
|
|
2241
2255
|
}
|
|
2242
2256
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
error: 'unable to connect without signed in account'
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2257
|
+
}
|
|
2258
|
+
class ProxyMessageChannelPort {
|
|
2259
|
+
constructor() {
|
|
2260
|
+
this.conn = void 0;
|
|
2261
|
+
this.events = new eventemitter2.EventEmitter2();
|
|
2251
2262
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
chainId: ethers.ethers.utils.hexlify(await this.getChainId())
|
|
2263
|
+
this.handleMessage = message => {
|
|
2264
|
+
throw new Error('ProxyMessageChannelPort is not registered');
|
|
2255
2265
|
};
|
|
2256
2266
|
|
|
2257
|
-
|
|
2258
|
-
//
|
|
2259
|
-
// including the auth proof
|
|
2260
|
-
const authOptions = {
|
|
2261
|
-
app: options.app,
|
|
2262
|
-
origin: options.origin,
|
|
2263
|
-
expiry: options.expiry
|
|
2264
|
-
}; // if (typeof(options.authorize) === 'object') {
|
|
2265
|
-
// authOptions = { ...authOptions, ...options.authorize }
|
|
2266
|
-
// }
|
|
2267
|
+
this.sendMessage = message => {
|
|
2268
|
+
this.conn.handleMessage(message); // trigger events
|
|
2267
2269
|
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
} catch (err) {
|
|
2271
|
-
utils.logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`);
|
|
2272
|
-
return {
|
|
2273
|
-
connected: false,
|
|
2274
|
-
chainId: '0x0',
|
|
2275
|
-
error: `signAuthorization failed: ${err.message}`
|
|
2276
|
-
};
|
|
2270
|
+
if (message.type === 'open') {
|
|
2271
|
+
this.events.emit('open', message);
|
|
2277
2272
|
}
|
|
2278
|
-
} // Build session response for connect details
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
connectDetails.session = await this.walletSession();
|
|
2282
|
-
return connectDetails;
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
// sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler
|
|
2286
|
-
// (aka, the signer in this instance) and then responds with a wrapped response of
|
|
2287
|
-
// ProviderMessageResponse to be sent over the transport
|
|
2288
|
-
sendMessageRequest(message) {
|
|
2289
|
-
return new Promise(resolve => {
|
|
2290
|
-
this.sendAsync(message.data, (error, response) => {
|
|
2291
|
-
// TODO: if response includes data.error, why do we need a separate error argument here?
|
|
2292
|
-
const responseMessage = _extends({}, message, {
|
|
2293
|
-
data: response
|
|
2294
|
-
}); // NOTE: we always resolve here, are the sendAsync call will wrap any exceptions
|
|
2295
|
-
// in the error field of the response to ensure we send back to the user
|
|
2296
2273
|
|
|
2274
|
+
if (message.type === 'close') {
|
|
2275
|
+
this.events.emit('close', message);
|
|
2276
|
+
}
|
|
2297
2277
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
} // sendAsync implements the JsonRpcHandler interface for sending JsonRpcRequests to the wallet
|
|
2278
|
+
if (message.type === 'connect') {
|
|
2279
|
+
this.events.emit('connect', message);
|
|
2280
|
+
}
|
|
2302
2281
|
|
|
2282
|
+
if (message.type === 'disconnect') {
|
|
2283
|
+
this.events.emit('disconnect', message);
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2303
2287
|
|
|
2304
2288
|
on(event, fn) {
|
|
2305
2289
|
this.events.on(event, fn);
|
|
@@ -2309,750 +2293,785 @@ class WalletRequestHandler {
|
|
|
2309
2293
|
this.events.once(event, fn);
|
|
2310
2294
|
}
|
|
2311
2295
|
|
|
2312
|
-
|
|
2313
|
-
if (!this.signer) {
|
|
2314
|
-
return '';
|
|
2315
|
-
} else {
|
|
2316
|
-
return this.signer.getAddress();
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2296
|
+
}
|
|
2319
2297
|
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
if (this._chainId) return this._chainId; // memoized
|
|
2298
|
+
class ProxyMessageProvider extends BaseProviderTransport {
|
|
2299
|
+
constructor(port) {
|
|
2300
|
+
super();
|
|
2301
|
+
this.port = void 0;
|
|
2325
2302
|
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2303
|
+
this.register = () => {
|
|
2304
|
+
this.port.handleMessage = message => {
|
|
2305
|
+
this.handleMessage(message);
|
|
2306
|
+
};
|
|
2330
2307
|
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
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
|
+
};
|
|
2334
2322
|
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2323
|
+
this.unregister = () => {
|
|
2324
|
+
this._registered = false;
|
|
2325
|
+
this.closeWallet();
|
|
2326
|
+
this.events.removeAllListeners(); // @ts-ignore
|
|
2338
2327
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
}
|
|
2328
|
+
this.port.handleMessage = undefined;
|
|
2329
|
+
};
|
|
2342
2330
|
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
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
|
+
};
|
|
2346
2348
|
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
}
|
|
2349
|
+
this.state = exports.OpenState.CLOSED;
|
|
2350
|
+
this.port = port;
|
|
2350
2351
|
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
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.
|
|
2355
2356
|
|
|
2356
|
-
if (this.signer && this.signer.setNetworks) {
|
|
2357
|
-
const defaultChainId = this.signer.setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId);
|
|
2358
2357
|
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
}
|
|
2358
|
+
this._init = exports.InitState.OK;
|
|
2359
|
+
}
|
|
2362
2360
|
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2361
|
+
closeWallet() {
|
|
2362
|
+
this.sendMessage({
|
|
2363
|
+
idx: -1,
|
|
2364
|
+
type: exports.EventType.CLOSE,
|
|
2365
|
+
data: null
|
|
2366
|
+
});
|
|
2367
|
+
this.close();
|
|
2367
2368
|
}
|
|
2368
2369
|
|
|
2369
|
-
|
|
2370
|
-
if (!
|
|
2371
|
-
|
|
2372
|
-
return [];
|
|
2370
|
+
sendMessage(message) {
|
|
2371
|
+
if (!message.idx) {
|
|
2372
|
+
throw new Error('message idx is empty');
|
|
2373
2373
|
}
|
|
2374
2374
|
|
|
2375
|
-
|
|
2375
|
+
this.port.sendMessage(message);
|
|
2376
|
+
}
|
|
2376
2377
|
|
|
2377
|
-
|
|
2378
|
-
// omit provider and relayer objects as they are not serializable
|
|
2379
|
-
return networks.map(n => {
|
|
2380
|
-
const network = _extends({}, n);
|
|
2378
|
+
}
|
|
2381
2379
|
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
}
|
|
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;
|
|
2389
2386
|
}
|
|
2390
2387
|
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
accountAddress: await this.signer.getAddress(),
|
|
2395
|
-
networks: await this.getNetworks(true)
|
|
2388
|
+
register() {
|
|
2389
|
+
this.port.handleMessage = message => {
|
|
2390
|
+
this.handleMessage(message);
|
|
2396
2391
|
};
|
|
2397
|
-
}
|
|
2398
2392
|
|
|
2399
|
-
|
|
2400
|
-
|
|
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
|
|
2401
2400
|
|
|
2402
|
-
this.events.emit('connect', connectDetails);
|
|
2403
2401
|
|
|
2404
|
-
|
|
2405
|
-
|
|
2402
|
+
restoreSession() {
|
|
2403
|
+
const cachedSession = this.getCachedTransportSession();
|
|
2406
2404
|
|
|
2407
|
-
|
|
2405
|
+
if (cachedSession) {
|
|
2406
|
+
this.open(cachedSession);
|
|
2408
2407
|
}
|
|
2409
2408
|
}
|
|
2410
2409
|
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
this.
|
|
2414
|
-
this.
|
|
2410
|
+
unregister() {
|
|
2411
|
+
// @ts-ignore
|
|
2412
|
+
this.port.handleMessage = undefined;
|
|
2413
|
+
this._registered = false;
|
|
2415
2414
|
}
|
|
2416
2415
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
this.events.emit('networks', n);
|
|
2420
|
-
|
|
2421
|
-
if (n && n.length > 0) {
|
|
2422
|
-
this.events.emit('chainChanged', ethers.ethers.utils.hexlify(n[0].chainId));
|
|
2423
|
-
} else {
|
|
2424
|
-
this.events.emit('chainChanged', '0x0');
|
|
2425
|
-
}
|
|
2416
|
+
sendMessage(message) {
|
|
2417
|
+
this.port.sendMessage(message);
|
|
2426
2418
|
}
|
|
2427
2419
|
|
|
2428
|
-
|
|
2429
|
-
if (!this.signer) {
|
|
2430
|
-
utils.logger.warn('signer not set: skipping to notify wallet context');
|
|
2431
|
-
return;
|
|
2432
|
-
}
|
|
2420
|
+
}
|
|
2433
2421
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2422
|
+
class MuxMessageProvider {
|
|
2423
|
+
constructor(...messageProviders) {
|
|
2424
|
+
var _this = this;
|
|
2437
2425
|
|
|
2438
|
-
|
|
2439
|
-
this.
|
|
2440
|
-
}
|
|
2426
|
+
this.messageProviders = void 0;
|
|
2427
|
+
this.provider = void 0;
|
|
2441
2428
|
|
|
2442
|
-
|
|
2443
|
-
|
|
2429
|
+
this.register = () => {
|
|
2430
|
+
if (this.messageProviders.length === 1) {
|
|
2431
|
+
this.provider = this.messageProviders[0];
|
|
2432
|
+
this.provider.register();
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2444
2435
|
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
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;
|
|
2448
2462
|
}
|
|
2449
2463
|
|
|
2450
|
-
this.
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2464
|
+
this.messageProviders.forEach(m => m.openWallet(path, intent, networkId));
|
|
2465
|
+
};
|
|
2453
2466
|
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
if (!chainId) {
|
|
2458
|
-
return true;
|
|
2459
|
-
}
|
|
2467
|
+
this.sendAsync = async function (request, callback, chainId) {
|
|
2468
|
+
if (_this.provider) {
|
|
2469
|
+
_this.provider.sendAsync(request, callback, chainId);
|
|
2460
2470
|
|
|
2461
|
-
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2462
2473
|
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
}
|
|
2474
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
2475
|
+
};
|
|
2466
2476
|
|
|
2467
|
-
|
|
2477
|
+
this.sendMessageRequest = async function (message) {
|
|
2478
|
+
if (_this.provider) {
|
|
2479
|
+
return _this.provider.sendMessageRequest(message);
|
|
2480
|
+
}
|
|
2468
2481
|
|
|
2469
|
-
|
|
2470
|
-
|
|
2482
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
2483
|
+
};
|
|
2471
2484
|
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
return
|
|
2475
|
-
} else {
|
|
2476
|
-
return true;
|
|
2485
|
+
this.waitUntilOpened = async function () {
|
|
2486
|
+
if (_this.provider) {
|
|
2487
|
+
return _this.provider.waitUntilOpened();
|
|
2477
2488
|
}
|
|
2478
|
-
}
|
|
2479
2489
|
|
|
2480
|
-
|
|
2481
|
-
|
|
2490
|
+
return Promise.race(_this.messageProviders.map(p => p.waitUntilOpened()));
|
|
2491
|
+
};
|
|
2482
2492
|
|
|
2483
|
-
|
|
2484
|
-
|
|
2493
|
+
this.waitUntilConnected = async function () {
|
|
2494
|
+
if (_this.provider) {
|
|
2495
|
+
return _this.provider.waitUntilConnected();
|
|
2496
|
+
}
|
|
2485
2497
|
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
this.
|
|
2491
|
-
this.port = void 0;
|
|
2492
|
-
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;
|
|
2493
2503
|
}
|
|
2494
2504
|
|
|
2495
|
-
|
|
2496
|
-
this.
|
|
2497
|
-
this.port = this.runtime.connect({
|
|
2498
|
-
name: CHANNEL_ID
|
|
2499
|
-
});
|
|
2505
|
+
add(...messageProviders) {
|
|
2506
|
+
this.messageProviders.push(...messageProviders);
|
|
2500
2507
|
}
|
|
2501
2508
|
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2509
|
+
closeWallet() {
|
|
2510
|
+
if (this.provider) {
|
|
2511
|
+
this.provider.closeWallet();
|
|
2512
|
+
}
|
|
2505
2513
|
}
|
|
2506
2514
|
|
|
2507
|
-
|
|
2515
|
+
isOpened() {
|
|
2516
|
+
if (this.provider) {
|
|
2517
|
+
return this.provider.isOpened();
|
|
2518
|
+
}
|
|
2508
2519
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
super();
|
|
2520
|
+
return false;
|
|
2521
|
+
}
|
|
2512
2522
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2523
|
+
isConnected() {
|
|
2524
|
+
if (this.provider) {
|
|
2525
|
+
return this.provider.isConnected();
|
|
2526
|
+
}
|
|
2516
2527
|
|
|
2517
|
-
|
|
2518
|
-
if (port.name === CHANNEL_ID) {
|
|
2519
|
-
this._init = exports.InitState.OK;
|
|
2520
|
-
port.onMessage.addListener(message => {
|
|
2521
|
-
this.handleMessage(message);
|
|
2522
|
-
});
|
|
2523
|
-
}
|
|
2524
|
-
});
|
|
2528
|
+
return false;
|
|
2525
2529
|
}
|
|
2526
2530
|
|
|
2527
|
-
|
|
2531
|
+
on(event, fn) {
|
|
2532
|
+
if (this.provider) {
|
|
2533
|
+
this.provider.on(event, fn);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
this.messageProviders.forEach(m => {
|
|
2538
|
+
m.on(event, fn);
|
|
2539
|
+
});
|
|
2528
2540
|
}
|
|
2529
2541
|
|
|
2530
|
-
|
|
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
|
+
});
|
|
2531
2551
|
}
|
|
2532
2552
|
|
|
2533
|
-
|
|
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;
|
|
2534
2563
|
}
|
|
2535
2564
|
|
|
2536
|
-
|
|
2565
|
+
sendMessage(message) {
|
|
2566
|
+
if (!message.idx || message.idx <= 0) {
|
|
2567
|
+
throw new Error('message idx is empty');
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
if (this.provider) {
|
|
2571
|
+
this.provider.sendMessage(message);
|
|
2572
|
+
} else {
|
|
2573
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
2574
|
+
}
|
|
2537
2575
|
}
|
|
2538
2576
|
|
|
2539
|
-
|
|
2577
|
+
handleMessage(message) {
|
|
2578
|
+
if (this.provider) {
|
|
2579
|
+
this.provider.handleMessage(message);
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2540
2582
|
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
constructor(stream) {
|
|
2544
|
-
var _this;
|
|
2583
|
+
throw new Error('impossible state, wallet must be opened first');
|
|
2584
|
+
}
|
|
2545
2585
|
|
|
2546
|
-
|
|
2547
|
-
_this = this;
|
|
2548
|
-
this.stream = stream;
|
|
2549
|
-
this.responseCallbacks = new Map();
|
|
2550
|
-
this._messageIdx = 0;
|
|
2586
|
+
}
|
|
2551
2587
|
|
|
2552
|
-
|
|
2588
|
+
let registeredWindowMessageProvider;
|
|
2589
|
+
class WindowMessageProvider extends BaseProviderTransport {
|
|
2590
|
+
constructor(walletAppURL) {
|
|
2591
|
+
super();
|
|
2592
|
+
this.walletURL = void 0;
|
|
2593
|
+
this.walletWindow = void 0;
|
|
2553
2594
|
|
|
2554
|
-
this.
|
|
2555
|
-
if (
|
|
2556
|
-
|
|
2557
|
-
|
|
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
|
|
2558
2601
|
|
|
2559
|
-
console.log('[received message]', message);
|
|
2560
|
-
const requestIdx = message.idx;
|
|
2561
|
-
const responseCallback = this.responseCallbacks.get(requestIdx);
|
|
2562
2602
|
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
}
|
|
2603
|
+
window.addEventListener('message', this.onWindowEvent);
|
|
2604
|
+
registeredWindowMessageProvider = this; // open heartbeat
|
|
2566
2605
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
// or when we register two handler, or duplicate messages with the same idx are sent,
|
|
2575
|
-
// all of which should be prevented prior to getting to this point
|
|
2576
|
-
throw new Error('impossible state');
|
|
2606
|
+
this.on('open', () => {
|
|
2607
|
+
// Heartbeat to track if window closed
|
|
2608
|
+
const popup = this.walletWindow;
|
|
2609
|
+
const interval = setInterval(() => {
|
|
2610
|
+
if (popup && popup.closed) {
|
|
2611
|
+
clearInterval(interval);
|
|
2612
|
+
this.close();
|
|
2577
2613
|
}
|
|
2614
|
+
}, 500);
|
|
2615
|
+
}); // close clean up
|
|
2578
2616
|
|
|
2579
|
-
|
|
2617
|
+
this.on('close', () => {
|
|
2618
|
+
if (this.walletWindow) {
|
|
2619
|
+
this.walletWindow.close();
|
|
2620
|
+
this.walletWindow = null;
|
|
2621
|
+
}
|
|
2622
|
+
});
|
|
2623
|
+
this._registered = true;
|
|
2624
|
+
};
|
|
2580
2625
|
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
this.emit(message.type, message.data);
|
|
2585
|
-
break;
|
|
2626
|
+
this.unregister = () => {
|
|
2627
|
+
this._registered = false;
|
|
2628
|
+
this.closeWallet(); // disable message listener
|
|
2586
2629
|
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
break;
|
|
2630
|
+
if (registeredWindowMessageProvider === this) {
|
|
2631
|
+
registeredWindowMessageProvider = undefined;
|
|
2590
2632
|
}
|
|
2633
|
+
|
|
2634
|
+
window.removeEventListener('message', this.onWindowEvent); // clear event listeners
|
|
2635
|
+
|
|
2636
|
+
this.events.removeAllListeners();
|
|
2591
2637
|
};
|
|
2592
2638
|
|
|
2593
|
-
this.
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
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
|
|
2598
2645
|
|
|
2599
|
-
const responseCallback = (error, response) => {
|
|
2600
|
-
if (error) {
|
|
2601
|
-
reject(error);
|
|
2602
|
-
} else if (response) {
|
|
2603
|
-
resolve(response);
|
|
2604
|
-
} else {
|
|
2605
|
-
throw new Error('no valid response to return');
|
|
2606
|
-
}
|
|
2607
|
-
};
|
|
2608
2646
|
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
} = message;
|
|
2647
|
+
const walletURL = new URL(this.walletURL.href);
|
|
2648
|
+
const windowSessionParams = new WindowSessionParams();
|
|
2612
2649
|
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
reject(new Error('duplicate message idx, should never happen'));
|
|
2617
|
-
}
|
|
2650
|
+
if (path && path !== '') {
|
|
2651
|
+
walletURL.pathname = path.toLowerCase();
|
|
2652
|
+
} // Set session, intent and network id on walletURL
|
|
2618
2653
|
|
|
2619
|
-
_this.sendMessage(message);
|
|
2620
|
-
});
|
|
2621
|
-
};
|
|
2622
2654
|
|
|
2623
|
-
|
|
2624
|
-
|
|
2655
|
+
this._init = exports.InitState.NIL;
|
|
2656
|
+
this._sessionId = `${performance.now()}`;
|
|
2657
|
+
windowSessionParams.set('sid', this._sessionId);
|
|
2625
2658
|
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
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
|
|
2630
2666
|
|
|
2631
|
-
|
|
2632
|
-
|
|
2667
|
+
if (!isBrowserExtension()) {
|
|
2668
|
+
intent.options.origin = window.location.origin;
|
|
2669
|
+
}
|
|
2670
|
+
} // encode intent as base6 url-encoded param
|
|
2633
2671
|
|
|
2634
|
-
}
|
|
2635
2672
|
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
return isSequenceProvider(cand);
|
|
2639
|
-
}
|
|
2673
|
+
windowSessionParams.set('intent', utils.base64EncodeObject(intent));
|
|
2674
|
+
}
|
|
2640
2675
|
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
super(provider, 'any');
|
|
2645
|
-
this._sender = void 0;
|
|
2646
|
-
this._isSequenceProvider = void 0;
|
|
2647
|
-
this._defaultChainId = void 0;
|
|
2648
|
-
this._sender = sender;
|
|
2649
|
-
this._isSequenceProvider = true;
|
|
2650
|
-
this._defaultChainId = network.maybeChainId(defaultChainId);
|
|
2651
|
-
}
|
|
2676
|
+
if (networkId) {
|
|
2677
|
+
windowSessionParams.set('net', `${networkId}`);
|
|
2678
|
+
} // Open popup window on center of the app window
|
|
2652
2679
|
|
|
2653
|
-
sendAsync(request, callback, chainId) {
|
|
2654
|
-
this._sender.sendAsync(request, callback, chainId);
|
|
2655
|
-
}
|
|
2656
2680
|
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
}
|
|
2681
|
+
let windowSize;
|
|
2682
|
+
let windowPos;
|
|
2660
2683
|
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
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
|
+
}
|
|
2664
2691
|
|
|
2665
|
-
|
|
2666
|
-
const result = await this.send('eth_chainId', []);
|
|
2667
|
-
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
|
|
2668
2693
|
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
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
|
+
};
|
|
2672
2717
|
|
|
2673
|
-
|
|
2674
|
-
|
|
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
|
+
}
|
|
2675
2724
|
|
|
2676
|
-
|
|
2677
|
-
function isSequenceProvider(provider) {
|
|
2678
|
-
const cand = provider;
|
|
2679
|
-
return cand && cand.send !== undefined && cand._isSequenceProvider === true;
|
|
2680
|
-
}
|
|
2681
|
-
class LocalWeb3Provider extends Web3Provider {
|
|
2682
|
-
constructor(signer, networks) {
|
|
2683
|
-
const walletRequestHandler = new WalletRequestHandler(signer, null, networks || []);
|
|
2684
|
-
super(walletRequestHandler);
|
|
2685
|
-
}
|
|
2725
|
+
let message;
|
|
2686
2726
|
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
this._providers = {};
|
|
2698
|
-
this.provider = provider;
|
|
2699
|
-
this.defaultChainId = defaultChainId;
|
|
2700
|
-
} // 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
|
|
2701
2737
|
|
|
2702
2738
|
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
if (this._address) return this._address;
|
|
2708
|
-
const accounts = await this.provider.send('eth_accounts', []);
|
|
2709
|
-
this._address = accounts[0];
|
|
2710
|
-
this._index = 0;
|
|
2711
|
-
return ethers.ethers.utils.getAddress(this._address);
|
|
2739
|
+
this.handleMessage(message);
|
|
2740
|
+
};
|
|
2741
|
+
|
|
2742
|
+
this.walletURL = new URL(walletAppURL);
|
|
2712
2743
|
}
|
|
2713
2744
|
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
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);
|
|
2717
2761
|
}
|
|
2718
2762
|
|
|
2719
|
-
|
|
2720
|
-
throw new Error('unsupported: cannot alter JSON-RPC Signer connection');
|
|
2721
|
-
} //
|
|
2722
|
-
// Sequence Signer methods
|
|
2723
|
-
//
|
|
2724
|
-
// getProvider returns a Web3Provider instance for the current chain. Note that this method
|
|
2725
|
-
// and signer is bound to a particular chain to prevent misuse. If you'd like a provider
|
|
2726
|
-
// for a specific chain, try getSender(chainId), or wallet.getProvider(chainId).
|
|
2763
|
+
}
|
|
2727
2764
|
|
|
2765
|
+
class WindowMessageHandler extends BaseWalletTransport {
|
|
2766
|
+
constructor(walletRequestHandler) {
|
|
2767
|
+
var _this;
|
|
2728
2768
|
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2769
|
+
super(walletRequestHandler);
|
|
2770
|
+
_this = this;
|
|
2771
|
+
this.parentWindow = void 0;
|
|
2772
|
+
this._isPopup = false;
|
|
2732
2773
|
|
|
2733
|
-
|
|
2734
|
-
|
|
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;
|
|
2735
2778
|
}
|
|
2736
|
-
}
|
|
2737
|
-
|
|
2738
|
-
return this.provider;
|
|
2739
|
-
} // getSender returns a Web3Provider instance via the signer transport. Note: for our case
|
|
2740
|
-
// the of sequence wallet, this will bring up the wallet window whenever using it, as the json-rpc
|
|
2741
|
-
// requests are sent to the window transport. Therefore, for anything non-signing related
|
|
2742
|
-
// you can write a higher-order JsonRpcRouter sender to route to the public provider endpoints
|
|
2743
|
-
// as we do in the WalletProvider.
|
|
2744
|
-
//
|
|
2745
|
-
// This method is primarily utilized internally when routing requests to a particular chainId.
|
|
2746
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
|
|
2747
2784
|
|
|
2748
|
-
async getSender(chainId) {
|
|
2749
|
-
if (!chainId || chainId && chainId === this.defaultChainId) {
|
|
2750
|
-
return this.provider;
|
|
2751
|
-
}
|
|
2752
2785
|
|
|
2753
|
-
|
|
2754
|
-
this._providers[chainId] = new Web3Provider(new network.JsonRpcSender(this.provider, chainId), chainId);
|
|
2755
|
-
}
|
|
2786
|
+
let request;
|
|
2756
2787
|
|
|
2757
|
-
|
|
2758
|
-
|
|
2788
|
+
try {
|
|
2789
|
+
request = JSON.parse(event.data);
|
|
2790
|
+
} catch (err) {
|
|
2791
|
+
// event is not a ProviderMessage JSON object, skip
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2759
2794
|
|
|
2760
|
-
|
|
2761
|
-
// TODO: JsonRpcRelayer ......? or, Web3Relayer.. or SequenceRelayer?
|
|
2762
|
-
// sequence_estimateGasLimits
|
|
2763
|
-
// sequence_gasRefundOptions
|
|
2764
|
-
// sequence_getNonce
|
|
2765
|
-
// sequence_relay
|
|
2766
|
-
throw new Error('TODO');
|
|
2767
|
-
}
|
|
2795
|
+
utils.logger.debug('RECEIVED MESSAGE', request); // Record event origin for valid init ack
|
|
2768
2796
|
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
}
|
|
2797
|
+
if (_this._init !== exports.InitState.OK && _this.isValidInitAck(request)) {
|
|
2798
|
+
_this.appOrigin = event.origin;
|
|
2799
|
+
}
|
|
2773
2800
|
|
|
2774
|
-
|
|
2775
|
-
|
|
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
|
|
2776
2806
|
|
|
2777
|
-
async getWalletConfig(chainId) {
|
|
2778
|
-
return await this.provider.send('sequence_getWalletConfig', [network.maybeChainId(chainId)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2779
|
-
}
|
|
2780
2807
|
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
}
|
|
2808
|
+
_this.handleMessage(request);
|
|
2809
|
+
};
|
|
2784
2810
|
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
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
|
+
};
|
|
2789
2819
|
|
|
2790
|
-
|
|
2820
|
+
this._init = exports.InitState.NIL;
|
|
2791
2821
|
}
|
|
2792
2822
|
|
|
2793
|
-
|
|
2794
|
-
const
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
if (!authChainId) {
|
|
2798
|
-
throw new Error('authChainId could not be determined from network list');
|
|
2799
|
-
}
|
|
2823
|
+
register(windowHref) {
|
|
2824
|
+
const isPopup = parent.window.opener !== null;
|
|
2825
|
+
this._isPopup = isPopup;
|
|
2800
2826
|
|
|
2801
|
-
|
|
2827
|
+
if (isPopup !== true) {
|
|
2828
|
+
return;
|
|
2829
|
+
} // record open details (sessionId + default network) from the window url
|
|
2802
2830
|
|
|
2803
|
-
if (!walletConfig || walletConfig.length === 0) {
|
|
2804
|
-
throw new Error(`walletConfig returned zero results for authChainId {authChainId}`);
|
|
2805
|
-
}
|
|
2806
2831
|
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
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
|
|
2810
2837
|
|
|
2838
|
+
const isNewWindowSession = !!session.sessionId; // attempt to restore previous session in the case of a redirect or window reload
|
|
2811
2839
|
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
const address = await this.getAddress(); // NOTE: as of ethers v5.5, it switched to using personal_sign, see
|
|
2816
|
-
// https://github.com/ethers-io/ethers.js/pull/1542 and see
|
|
2817
|
-
// https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info.
|
|
2840
|
+
if (!isNewWindowSession) {
|
|
2841
|
+
session = this.getCachedTransportSession();
|
|
2842
|
+
}
|
|
2818
2843
|
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2844
|
+
if (!session) {
|
|
2845
|
+
utils.logger.error('window session is undefined');
|
|
2846
|
+
return;
|
|
2847
|
+
} // record parent window instance for communication
|
|
2822
2848
|
|
|
2823
2849
|
|
|
2824
|
-
|
|
2825
|
-
// Populate any ENS names (in-place)
|
|
2826
|
-
// const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => {
|
|
2827
|
-
// return this.provider.resolveName(name)
|
|
2828
|
-
// })
|
|
2829
|
-
return await this.provider.send('eth_signTypedData_v4', [await this.getAddress(), ethers.ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], network.maybeChainId(chainId) || this.defaultChainId);
|
|
2830
|
-
} // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with
|
|
2831
|
-
// multi-chain support.
|
|
2850
|
+
this.parentWindow = parent.window.opener; // listen for window-transport requests
|
|
2832
2851
|
|
|
2852
|
+
window.addEventListener('message', this.onWindowEvent, false);
|
|
2853
|
+
this._registered = true; // send open event to the app which opened us
|
|
2833
2854
|
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
return ethers.ethers.utils.poll(() => {
|
|
2838
|
-
return provider.getTransaction(hash).then(tx => {
|
|
2839
|
-
if (tx === null) {
|
|
2840
|
-
return undefined;
|
|
2841
|
-
}
|
|
2855
|
+
this.open(session).then(opened => {
|
|
2856
|
+
if (!opened) {
|
|
2857
|
+
var _session;
|
|
2842
2858
|
|
|
2843
|
-
|
|
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
|
|
2844
2863
|
});
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
throw error;
|
|
2850
|
-
});
|
|
2851
|
-
}); // @ts-ignore
|
|
2852
|
-
|
|
2853
|
-
return tx;
|
|
2854
|
-
} // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to
|
|
2855
|
-
// send multiple transaction as a single payload and just one on-chain transaction.
|
|
2864
|
+
window.close();
|
|
2865
|
+
}
|
|
2866
|
+
}).catch(e => {
|
|
2867
|
+
var _session2;
|
|
2856
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
|
+
}
|
|
2857
2877
|
|
|
2858
|
-
|
|
2859
|
-
|
|
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
|
|
2860
2883
|
|
|
2861
|
-
if (!batch || batch.length === 0) {
|
|
2862
|
-
throw new Error('cannot send empty batch');
|
|
2863
|
-
} // sendTransactionBatch only accepts TransactionRequest, not TransactionResponses
|
|
2864
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
|
|
2865
2890
|
|
|
2866
|
-
if (
|
|
2867
|
-
|
|
2891
|
+
if (message.type === exports.EventType.INIT) {
|
|
2892
|
+
this.postMessage(payload, true);
|
|
2893
|
+
} else {
|
|
2894
|
+
this.postMessage(payload);
|
|
2868
2895
|
}
|
|
2896
|
+
}
|
|
2869
2897
|
|
|
2870
|
-
|
|
2898
|
+
get isPopup() {
|
|
2899
|
+
return this._isPopup;
|
|
2900
|
+
}
|
|
2871
2901
|
|
|
2872
|
-
|
|
2873
|
-
|
|
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;
|
|
2874
2906
|
}
|
|
2875
2907
|
|
|
2876
|
-
|
|
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
|
+
}
|
|
2877
2920
|
}
|
|
2878
2921
|
|
|
2879
|
-
|
|
2880
|
-
transaction = utils.shallowCopy(transaction); // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over
|
|
2881
|
-
// the wire.. see sendUncheckedTransaction and resolveProperties
|
|
2922
|
+
}
|
|
2882
2923
|
|
|
2883
|
-
|
|
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;
|
|
2884
2931
|
}
|
|
2885
2932
|
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2933
|
+
register() {
|
|
2934
|
+
this._registered = true;
|
|
2935
|
+
this.port = this.runtime.connect({
|
|
2936
|
+
name: CHANNEL_ID
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2891
2939
|
|
|
2940
|
+
sendMessage(message) {
|
|
2941
|
+
console.log('[ExtensionMessageHandler send]', message);
|
|
2942
|
+
this.port.postMessage(message);
|
|
2943
|
+
}
|
|
2892
2944
|
|
|
2893
|
-
|
|
2894
|
-
// sequence_updateConfig
|
|
2895
|
-
const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId);
|
|
2945
|
+
}
|
|
2896
2946
|
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2947
|
+
class ExtensionMessageProvider extends BaseProviderTransport {
|
|
2948
|
+
constructor(runtime) {
|
|
2949
|
+
super();
|
|
2900
2950
|
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
// NOTE: this is not supported by the remote wallet by default.
|
|
2951
|
+
this.register = () => {
|
|
2952
|
+
this._registered = true;
|
|
2953
|
+
};
|
|
2905
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
|
+
}
|
|
2906
2964
|
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
const tx = await provider.send('sequence_publishConfig', []);
|
|
2965
|
+
sendMessage(message) {//noop
|
|
2966
|
+
}
|
|
2910
2967
|
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
}
|
|
2968
|
+
unregister() {//noop
|
|
2969
|
+
}
|
|
2914
2970
|
|
|
2915
|
-
|
|
2971
|
+
openWallet(path, intent, networkId) {//noop
|
|
2916
2972
|
}
|
|
2917
2973
|
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
const walletCode = await provider.getCode(await this.getAddress());
|
|
2921
|
-
return !!walletCode && walletCode !== '0x';
|
|
2922
|
-
} //
|
|
2923
|
-
// ethers JsonRpcSigner methods
|
|
2924
|
-
//
|
|
2974
|
+
closeWallet() {//noop
|
|
2975
|
+
}
|
|
2925
2976
|
|
|
2977
|
+
}
|
|
2926
2978
|
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
// 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;
|
|
2932
2983
|
|
|
2933
|
-
|
|
2934
|
-
|
|
2984
|
+
super();
|
|
2985
|
+
_this = this;
|
|
2986
|
+
this.stream = stream;
|
|
2987
|
+
this.responseCallbacks = new Map();
|
|
2988
|
+
this._messageIdx = 0;
|
|
2935
2989
|
|
|
2936
|
-
|
|
2937
|
-
return this.signTypedData(domain, types, message, chainId, allSigners);
|
|
2938
|
-
}
|
|
2990
|
+
this.nextMessageIdx = () => ++this._messageIdx;
|
|
2939
2991
|
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
// TODO: alternatively/one day, we could write a provider middleware to eth_estimateGas
|
|
2945
|
-
// and send it to our relayer url instead for estimation..
|
|
2946
|
-
//
|
|
2947
|
-
// if (!transaction.gasLimit) {
|
|
2948
|
-
// const estimate = shallowCopy(transaction)
|
|
2949
|
-
// estimate.from = fromAddress
|
|
2950
|
-
// transaction.gasLimit = this.provider.estimateGas(estimate)
|
|
2951
|
-
// }
|
|
2992
|
+
this.handleMessage = message => {
|
|
2993
|
+
if (!message.type || !message.data) {
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2952
2996
|
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
sender
|
|
2960
|
-
}) => {
|
|
2961
|
-
if (tx.from != null) {
|
|
2962
|
-
if (ethers.ethers.utils.getAddress(tx.from) !== sender) {
|
|
2963
|
-
// logger.throwArgumentError("from address mismatch", "transaction", transaction)
|
|
2964
|
-
throw new Error(`from address mismatch for transaction ${transaction}`);
|
|
2965
|
-
}
|
|
2966
|
-
} else {
|
|
2967
|
-
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);
|
|
2968
3003
|
}
|
|
2969
3004
|
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
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
|
+
}
|
|
2979
3016
|
|
|
2980
|
-
|
|
2981
|
-
throw new Error('connectUnchecked is unsupported');
|
|
2982
|
-
}
|
|
3017
|
+
break;
|
|
2983
3018
|
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
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;
|
|
2988
3024
|
|
|
2989
|
-
|
|
2990
|
-
|
|
3025
|
+
default:
|
|
3026
|
+
console.error('unknown message type', message);
|
|
3027
|
+
break;
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
2991
3030
|
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
nonce: true,
|
|
2998
|
-
to: true,
|
|
2999
|
-
value: true,
|
|
3000
|
-
from: true,
|
|
3001
|
-
auxiliary: true,
|
|
3002
|
-
expiration: true,
|
|
3003
|
-
afterNonce: true,
|
|
3004
|
-
delegateCall: true,
|
|
3005
|
-
revertOnError: true
|
|
3006
|
-
};
|
|
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
|
+
}
|
|
3007
3036
|
|
|
3008
|
-
const
|
|
3009
|
-
|
|
3010
|
-
|
|
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
|
+
};
|
|
3011
3046
|
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
allowed[key] = true;
|
|
3016
|
-
}
|
|
3017
|
-
}
|
|
3018
|
-
}
|
|
3047
|
+
const {
|
|
3048
|
+
idx
|
|
3049
|
+
} = message;
|
|
3019
3050
|
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
return;
|
|
3026
|
-
}
|
|
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
|
+
}
|
|
3027
3056
|
|
|
3028
|
-
|
|
3057
|
+
_this.sendMessage(message);
|
|
3058
|
+
});
|
|
3059
|
+
};
|
|
3029
3060
|
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
}
|
|
3061
|
+
this.stream.on('data', this.handleMessage);
|
|
3062
|
+
}
|
|
3033
3063
|
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
if (!transaction[key]) {
|
|
3038
|
-
return;
|
|
3064
|
+
sendMessage(message) {
|
|
3065
|
+
if (!this.stream.writable) {
|
|
3066
|
+
console.error('window post message stream is not writable');
|
|
3039
3067
|
}
|
|
3040
3068
|
|
|
3041
|
-
|
|
3042
|
-
});
|
|
3043
|
-
const auxiliary = transaction['auxiliary'];
|
|
3044
|
-
|
|
3045
|
-
if (auxiliary && auxiliary.length > 0) {
|
|
3046
|
-
result['auxiliary'] = [];
|
|
3047
|
-
auxiliary.forEach(a => {
|
|
3048
|
-
result['auxiliary'].push(hexlifyTransaction(a));
|
|
3049
|
-
});
|
|
3069
|
+
this.stream.write(message);
|
|
3050
3070
|
}
|
|
3051
3071
|
|
|
3052
|
-
|
|
3053
|
-
};
|
|
3072
|
+
}
|
|
3054
3073
|
|
|
3055
|
-
class
|
|
3074
|
+
class WalletUtils {
|
|
3056
3075
|
constructor(walletProvider) {
|
|
3057
3076
|
var _this = this;
|
|
3058
3077
|
|
|
@@ -3112,7 +3131,9 @@ class WalletCommands {
|
|
|
3112
3131
|
} // Verify message signature
|
|
3113
3132
|
|
|
3114
3133
|
|
|
3115
|
-
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;
|
|
3116
3137
|
return this.isValidSignature(address, utils.encodeMessageDigest(message), signature, chainId, walletContext);
|
|
3117
3138
|
} // Verify typedData signature
|
|
3118
3139
|
|
|
@@ -3144,7 +3165,7 @@ class Wallet {
|
|
|
3144
3165
|
constructor(defaultNetworkId, config) {
|
|
3145
3166
|
var _this = this;
|
|
3146
3167
|
|
|
3147
|
-
this.
|
|
3168
|
+
this.utils = void 0;
|
|
3148
3169
|
this.config = void 0;
|
|
3149
3170
|
this.session = void 0;
|
|
3150
3171
|
this.connectedSites = void 0;
|
|
@@ -3470,7 +3491,7 @@ class Wallet {
|
|
|
3470
3491
|
this.networks = [];
|
|
3471
3492
|
this.providers = {};
|
|
3472
3493
|
this.connectedSites = new LocalStore('@sequence.connectedSites', []);
|
|
3473
|
-
this.
|
|
3494
|
+
this.utils = new WalletUtils(this);
|
|
3474
3495
|
this.init();
|
|
3475
3496
|
}
|
|
3476
3497
|
|
|
@@ -3713,7 +3734,9 @@ exports.WindowMessageProvider = WindowMessageProvider;
|
|
|
3713
3734
|
exports.WindowSessionParams = WindowSessionParams;
|
|
3714
3735
|
exports.isBrowserExtension = isBrowserExtension;
|
|
3715
3736
|
exports.isSequenceProvider = isSequenceProvider;
|
|
3737
|
+
exports.isValidMessageSignature = isValidMessageSignature;
|
|
3716
3738
|
exports.isValidSignature = isValidSignature;
|
|
3739
|
+
exports.isValidTypedDataSignature = isValidTypedDataSignature;
|
|
3717
3740
|
exports.isWalletUpToDate = isWalletUpToDate;
|
|
3718
3741
|
exports.nextMessageIdx = nextMessageIdx;
|
|
3719
3742
|
exports.recoverWalletConfig = recoverWalletConfig;
|