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