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