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