@toruslabs/ethereum-controllers 5.11.0 → 6.0.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/ethereumControllers.cjs.js +80 -431
- package/dist/ethereumControllers.esm.js +30 -349
- package/dist/ethereumControllers.umd.min.js +1 -1
- package/dist/ethereumControllers.umd.min.js.LICENSE.txt +7 -2
- package/dist/lib.cjs/Account/AccountTrackerController.js +160 -0
- package/dist/lib.cjs/Block/PollingBlockTracker.js +85 -0
- package/dist/lib.cjs/Currency/CurrencyController.js +111 -0
- package/dist/lib.cjs/Gas/GasFeeController.js +214 -0
- package/dist/lib.cjs/Gas/gasUtil.js +148 -0
- package/dist/lib.cjs/Keyring/KeyringController.js +93 -0
- package/dist/lib.cjs/Message/AbstractMessageController.js +107 -0
- package/dist/lib.cjs/Message/AddChainController.js +78 -0
- package/dist/lib.cjs/Message/MessageController.js +77 -0
- package/dist/lib.cjs/Message/PersonalMessageController.js +77 -0
- package/dist/lib.cjs/Message/SwitchChainController.js +78 -0
- package/dist/lib.cjs/Message/TypedMessageController.js +81 -0
- package/dist/lib.cjs/Message/utils.js +112 -0
- package/dist/lib.cjs/Network/NetworkController.js +201 -0
- package/dist/lib.cjs/Network/cacheIdentifier.js +112 -0
- package/dist/lib.cjs/Network/createEthereumMiddleware.js +302 -0
- package/dist/lib.cjs/Network/createJsonRpcClient.js +64 -0
- package/dist/lib.cjs/Nfts/NftHandler.js +180 -0
- package/dist/lib.cjs/Nfts/NftsController.js +213 -0
- package/dist/lib.cjs/Preferences/PreferencesController.js +476 -0
- package/dist/lib.cjs/Tokens/TokenHandler.js +51 -0
- package/dist/lib.cjs/Tokens/TokenRatesController.js +112 -0
- package/dist/lib.cjs/Tokens/TokensController.js +259 -0
- package/dist/lib.cjs/Transaction/NonceTracker.js +150 -0
- package/dist/lib.cjs/Transaction/PendingTransactionTracker.js +222 -0
- package/dist/lib.cjs/Transaction/TransactionController.js +515 -0
- package/dist/lib.cjs/Transaction/TransactionGasUtil.js +81 -0
- package/dist/lib.cjs/Transaction/TransactionStateHistoryHelper.js +42 -0
- package/dist/lib.cjs/Transaction/TransactionStateManager.js +296 -0
- package/dist/lib.cjs/Transaction/TransactionUtils.js +341 -0
- package/dist/lib.cjs/index.js +171 -0
- package/dist/lib.cjs/utils/abis.js +510 -0
- package/dist/lib.cjs/utils/constants.js +362 -0
- package/dist/lib.cjs/utils/contractAddresses.js +16 -0
- package/dist/lib.cjs/utils/conversionUtils.js +232 -0
- package/dist/lib.cjs/utils/helpers.js +244 -0
- package/dist/lib.cjs/utils/lodashUtils.js +25 -0
- package/dist/lib.esm/Account/AccountTrackerController.js +158 -0
- package/dist/lib.esm/Block/PollingBlockTracker.js +83 -0
- package/dist/lib.esm/Currency/CurrencyController.js +109 -0
- package/dist/lib.esm/Gas/GasFeeController.js +212 -0
- package/dist/lib.esm/Gas/gasUtil.js +141 -0
- package/dist/lib.esm/Keyring/KeyringController.js +91 -0
- package/dist/lib.esm/Message/AbstractMessageController.js +105 -0
- package/dist/lib.esm/Message/AddChainController.js +76 -0
- package/dist/lib.esm/Message/MessageController.js +75 -0
- package/dist/lib.esm/Message/PersonalMessageController.js +75 -0
- package/dist/lib.esm/Message/SwitchChainController.js +76 -0
- package/dist/lib.esm/Message/TypedMessageController.js +79 -0
- package/dist/lib.esm/Message/utils.js +105 -0
- package/dist/lib.esm/Network/NetworkController.js +199 -0
- package/dist/lib.esm/Network/cacheIdentifier.js +107 -0
- package/dist/lib.esm/Network/createEthereumMiddleware.js +289 -0
- package/dist/lib.esm/Network/createJsonRpcClient.js +60 -0
- package/dist/lib.esm/Nfts/NftHandler.js +178 -0
- package/dist/lib.esm/Nfts/NftsController.js +211 -0
- package/dist/lib.esm/Preferences/PreferencesController.js +474 -0
- package/dist/lib.esm/Tokens/TokenHandler.js +49 -0
- package/dist/lib.esm/Tokens/TokenRatesController.js +109 -0
- package/dist/lib.esm/Tokens/TokensController.js +257 -0
- package/dist/lib.esm/Transaction/NonceTracker.js +148 -0
- package/dist/lib.esm/Transaction/PendingTransactionTracker.js +220 -0
- package/dist/lib.esm/Transaction/TransactionController.js +513 -0
- package/dist/lib.esm/Transaction/TransactionGasUtil.js +79 -0
- package/dist/lib.esm/Transaction/TransactionStateHistoryHelper.js +38 -0
- package/dist/lib.esm/Transaction/TransactionStateManager.js +294 -0
- package/dist/lib.esm/Transaction/TransactionUtils.js +326 -0
- package/dist/lib.esm/index.js +33 -0
- package/dist/lib.esm/utils/abis.js +505 -0
- package/dist/lib.esm/utils/constants.js +323 -0
- package/dist/lib.esm/utils/contractAddresses.js +14 -0
- package/dist/lib.esm/utils/conversionUtils.js +218 -0
- package/dist/lib.esm/utils/helpers.js +227 -0
- package/dist/lib.esm/utils/lodashUtils.js +21 -0
- package/dist/types/Account/AccountTrackerController.d.ts +5 -5
- package/dist/types/Block/PollingBlockTracker.d.ts +1 -2
- package/dist/types/Currency/CurrencyController.d.ts +1 -1
- package/dist/types/Gas/GasFeeController.d.ts +3 -3
- package/dist/types/Gas/gasUtil.d.ts +1 -1
- package/dist/types/Keyring/KeyringController.d.ts +3 -5
- package/dist/types/Message/AbstractMessageController.d.ts +5 -6
- package/dist/types/Message/AddChainController.d.ts +4 -4
- package/dist/types/Message/MessageController.d.ts +4 -4
- package/dist/types/Message/PersonalMessageController.d.ts +4 -4
- package/dist/types/Message/SwitchChainController.d.ts +4 -4
- package/dist/types/Message/TypedMessageController.d.ts +6 -7
- package/dist/types/Message/utils.d.ts +2 -7
- package/dist/types/Network/NetworkController.d.ts +4 -4
- package/dist/types/Network/cacheIdentifier.d.ts +1 -1
- package/dist/types/Network/createEthereumMiddleware.d.ts +2 -18
- package/dist/types/Network/createJsonRpcClient.d.ts +2 -2
- package/dist/types/Nfts/NftsController.d.ts +2 -2
- package/dist/types/Preferences/PreferencesController.d.ts +4 -4
- package/dist/types/Tokens/TokensController.d.ts +3 -3
- package/dist/types/Transaction/NonceTracker.d.ts +5 -5
- package/dist/types/Transaction/PendingTransactionTracker.d.ts +5 -5
- package/dist/types/Transaction/TransactionController.d.ts +12 -12
- package/dist/types/Transaction/TransactionGasUtil.d.ts +4 -4
- package/dist/types/Transaction/TransactionStateManager.d.ts +3 -3
- package/dist/types/Transaction/TransactionUtils.d.ts +1 -1
- package/dist/types/index.d.ts +12 -14
- package/dist/types/utils/constants.d.ts +1 -5
- package/dist/types/utils/helpers.d.ts +4 -4
- package/dist/types/utils/interfaces.d.ts +43 -23
- package/package.json +10 -9
- package/dist/types/Message/DecryptMessageController.d.ts +0 -20
- package/dist/types/Message/EncryptionPublicKeyController.d.ts +0 -20
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* The buffer module from node.js, for the browser.
|
|
3
|
+
*
|
|
4
|
+
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
/*!
|
|
2
9
|
* The buffer module from node.js, for the browser.
|
|
3
10
|
*
|
|
@@ -17,8 +24,6 @@
|
|
|
17
24
|
* MIT licensed
|
|
18
25
|
*/
|
|
19
26
|
|
|
20
|
-
/*! https://mths.be/punycode v1.4.1 by @mathias */
|
|
21
|
-
|
|
22
27
|
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
23
28
|
|
|
24
29
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
|
|
4
|
+
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
|
|
5
|
+
var baseControllers = require('@toruslabs/base-controllers');
|
|
6
|
+
var asyncMutex = require('async-mutex');
|
|
7
|
+
var ethers = require('ethers');
|
|
8
|
+
var log = require('loglevel');
|
|
9
|
+
var abis = require('../utils/abis.js');
|
|
10
|
+
var contractAddresses = require('../utils/contractAddresses.js');
|
|
11
|
+
|
|
12
|
+
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tracks accounts based on blocks.
|
|
16
|
+
* If block tracker provides latest block, we query accounts from it.
|
|
17
|
+
* Preferences state changes also retrigger accounts update.
|
|
18
|
+
* Network state changes also retrigger accounts update.
|
|
19
|
+
*/
|
|
20
|
+
class AccountTrackerController extends baseControllers.BaseController {
|
|
21
|
+
constructor({
|
|
22
|
+
config,
|
|
23
|
+
state,
|
|
24
|
+
provider,
|
|
25
|
+
blockTracker,
|
|
26
|
+
getIdentities,
|
|
27
|
+
onPreferencesStateChange,
|
|
28
|
+
getCurrentChainId
|
|
29
|
+
}) {
|
|
30
|
+
super({
|
|
31
|
+
config,
|
|
32
|
+
state
|
|
33
|
+
});
|
|
34
|
+
_defineProperty(this, "provider", void 0);
|
|
35
|
+
_defineProperty(this, "blockTracker", void 0);
|
|
36
|
+
_defineProperty(this, "mutex", new asyncMutex.Mutex());
|
|
37
|
+
_defineProperty(this, "ethersProvider", void 0);
|
|
38
|
+
_defineProperty(this, "getIdentities", void 0);
|
|
39
|
+
_defineProperty(this, "getCurrentChainId", void 0);
|
|
40
|
+
this.defaultState = {
|
|
41
|
+
accounts: {}
|
|
42
|
+
};
|
|
43
|
+
this.initialize();
|
|
44
|
+
this.provider = provider;
|
|
45
|
+
this.blockTracker = blockTracker;
|
|
46
|
+
this.ethersProvider = new ethers.BrowserProvider(this.provider, "any");
|
|
47
|
+
this.getIdentities = getIdentities;
|
|
48
|
+
this.getCurrentChainId = getCurrentChainId;
|
|
49
|
+
onPreferencesStateChange(() => {
|
|
50
|
+
const refreshNeeded = this.syncAccounts();
|
|
51
|
+
if (refreshNeeded) {
|
|
52
|
+
log.info("onPreferencesStateChange called");
|
|
53
|
+
this.refresh();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.blockTrackerListener = this.blockTrackerListener.bind(this);
|
|
57
|
+
}
|
|
58
|
+
blockTrackerListener() {
|
|
59
|
+
this.refresh();
|
|
60
|
+
}
|
|
61
|
+
startPolling() {
|
|
62
|
+
this.stopPolling();
|
|
63
|
+
// Initiate block tracker internal tracking.
|
|
64
|
+
if (Object.keys(this.state.accounts).length > 0) {
|
|
65
|
+
// Adding this listener on block tracker triggers it to start polling.
|
|
66
|
+
this.blockTracker.on("latest", this.blockTrackerListener);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
stopPolling() {
|
|
70
|
+
this.blockTracker.removeListener("latest", this.blockTrackerListener);
|
|
71
|
+
}
|
|
72
|
+
syncAccounts() {
|
|
73
|
+
const {
|
|
74
|
+
accounts
|
|
75
|
+
} = this.state;
|
|
76
|
+
const addresses = Object.keys(this.getIdentities());
|
|
77
|
+
const existing = Object.keys(accounts);
|
|
78
|
+
const newAddresses = addresses.filter(address => existing.indexOf(address) === -1);
|
|
79
|
+
const oldAddresses = existing.filter(address => addresses.indexOf(address) === -1);
|
|
80
|
+
let isUpdated = false;
|
|
81
|
+
newAddresses.forEach(address => {
|
|
82
|
+
isUpdated = true;
|
|
83
|
+
accounts[address] = {
|
|
84
|
+
balance: "0x0"
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
oldAddresses.forEach(address => {
|
|
88
|
+
isUpdated = true;
|
|
89
|
+
delete accounts[address];
|
|
90
|
+
});
|
|
91
|
+
this.update({
|
|
92
|
+
accounts: _objectSpread({}, accounts)
|
|
93
|
+
});
|
|
94
|
+
return isUpdated;
|
|
95
|
+
}
|
|
96
|
+
async refresh() {
|
|
97
|
+
const releaseLock = await this.mutex.acquire();
|
|
98
|
+
try {
|
|
99
|
+
this._updateAccounts();
|
|
100
|
+
} catch (error) {} finally {
|
|
101
|
+
releaseLock();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async _updateAccounts() {
|
|
105
|
+
const {
|
|
106
|
+
accounts
|
|
107
|
+
} = this.state;
|
|
108
|
+
const addresses = Object.keys(accounts);
|
|
109
|
+
const chainId = this.getCurrentChainId();
|
|
110
|
+
if (chainId === "loading") return;
|
|
111
|
+
if (addresses.length > 0) {
|
|
112
|
+
if (contractAddresses.SINGLE_CALL_BALANCES_ADDRESSES[chainId]) {
|
|
113
|
+
await this._updateAccountsViaBalanceChecker(addresses, contractAddresses.SINGLE_CALL_BALANCES_ADDRESSES[chainId]);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
log.info("falling back to ethQuery.getBalance");
|
|
117
|
+
await Promise.all(addresses.map(x => this._updateAccount(x)));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async _updateAccount(address) {
|
|
121
|
+
const balance = await this.provider.request({
|
|
122
|
+
method: "eth_getBalance",
|
|
123
|
+
params: [address, "latest"]
|
|
124
|
+
});
|
|
125
|
+
const {
|
|
126
|
+
accounts
|
|
127
|
+
} = this.state;
|
|
128
|
+
if (!accounts[address]) return;
|
|
129
|
+
accounts[address] = {
|
|
130
|
+
balance: ethers.toQuantity(balance)
|
|
131
|
+
};
|
|
132
|
+
this.update({
|
|
133
|
+
accounts
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async _updateAccountsViaBalanceChecker(addresses, deployedContractAddress) {
|
|
137
|
+
const ethContract = new ethers.Contract(deployedContractAddress, abis.singleBalanceCheckerAbi, this.ethersProvider);
|
|
138
|
+
try {
|
|
139
|
+
const result = await ethContract.balances(addresses, [ZERO_ADDRESS]);
|
|
140
|
+
const {
|
|
141
|
+
accounts
|
|
142
|
+
} = this.state;
|
|
143
|
+
addresses.forEach((address, index) => {
|
|
144
|
+
const balance = ethers.toQuantity(result[index]);
|
|
145
|
+
if (!accounts[address]) return;
|
|
146
|
+
accounts[address] = {
|
|
147
|
+
balance
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
return this.update({
|
|
151
|
+
accounts
|
|
152
|
+
});
|
|
153
|
+
} catch (error) {
|
|
154
|
+
log.warn("Torus - Account Tracker single call balance fetch failed", error);
|
|
155
|
+
return Promise.all(addresses.map(x => this._updateAccount(x)));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
exports.AccountTrackerController = AccountTrackerController;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var baseControllers = require('@toruslabs/base-controllers');
|
|
4
|
+
var log = require('loglevel');
|
|
5
|
+
var helpers = require('../utils/helpers.js');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_POLLING_INTERVAL = 20;
|
|
8
|
+
const DEFAULT_RETRY_TIMEOUT = 2;
|
|
9
|
+
const SEC = 1000;
|
|
10
|
+
class PollingBlockTracker extends baseControllers.BaseBlockTracker {
|
|
11
|
+
constructor({
|
|
12
|
+
config,
|
|
13
|
+
state = {}
|
|
14
|
+
}) {
|
|
15
|
+
if (!config.provider) {
|
|
16
|
+
throw new Error("PollingBlockTracker - no provider specified.");
|
|
17
|
+
}
|
|
18
|
+
super({
|
|
19
|
+
config,
|
|
20
|
+
state
|
|
21
|
+
});
|
|
22
|
+
const pollingInterval = config.pollingInterval || DEFAULT_POLLING_INTERVAL;
|
|
23
|
+
const retryTimeout = config.retryTimeout || DEFAULT_RETRY_TIMEOUT;
|
|
24
|
+
|
|
25
|
+
// merge default + provided config.
|
|
26
|
+
this.defaultConfig = {
|
|
27
|
+
provider: config.provider,
|
|
28
|
+
pollingInterval: pollingInterval * SEC,
|
|
29
|
+
retryTimeout: retryTimeout * SEC,
|
|
30
|
+
setSkipCacheFlag: config.setSkipCacheFlag || false
|
|
31
|
+
};
|
|
32
|
+
this.initialize();
|
|
33
|
+
}
|
|
34
|
+
async checkForLatestBlock() {
|
|
35
|
+
await this._updateLatestBlock();
|
|
36
|
+
return this.getLatestBlock();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// overrides the BaseBlockTracker._start method.
|
|
40
|
+
_start() {
|
|
41
|
+
this._synchronize().catch(err => this.emit("error", err));
|
|
42
|
+
}
|
|
43
|
+
async _synchronize() {
|
|
44
|
+
while (this.state._isRunning) {
|
|
45
|
+
if (helpers.idleTimeTracker.checkIfIdle()) return;
|
|
46
|
+
try {
|
|
47
|
+
await this._updateLatestBlock();
|
|
48
|
+
await baseControllers.timeout(this.config.pollingInterval);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`);
|
|
51
|
+
try {
|
|
52
|
+
this.emit("error", newErr);
|
|
53
|
+
} catch (emitErr) {
|
|
54
|
+
log.error(newErr);
|
|
55
|
+
}
|
|
56
|
+
await baseControllers.timeout(this.config.retryTimeout);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async _updateLatestBlock() {
|
|
61
|
+
// fetch + set latest block
|
|
62
|
+
const latestBlock = await this._fetchLatestBlock();
|
|
63
|
+
this._newPotentialLatest(latestBlock);
|
|
64
|
+
}
|
|
65
|
+
async _fetchLatestBlock() {
|
|
66
|
+
try {
|
|
67
|
+
const block = await this.config.provider.request({
|
|
68
|
+
method: "eth_getBlockByNumber",
|
|
69
|
+
params: ["latest", false]
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
blockHash: block.hash,
|
|
73
|
+
idempotencyKey: block.number,
|
|
74
|
+
timestamp: block.timestamp,
|
|
75
|
+
baseFeePerGas: block.baseFeePerGas,
|
|
76
|
+
gasLimit: block.gasLimit
|
|
77
|
+
};
|
|
78
|
+
} catch (error) {
|
|
79
|
+
log.error("Polling Block Tracker: ", error);
|
|
80
|
+
throw new Error(`PollingBlockTracker - encountered error fetching block:\n${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
exports.PollingBlockTracker = PollingBlockTracker;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
|
|
4
|
+
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
|
|
5
|
+
var baseControllers = require('@toruslabs/base-controllers');
|
|
6
|
+
var httpHelpers = require('@toruslabs/http-helpers');
|
|
7
|
+
var log = require('loglevel');
|
|
8
|
+
var helpers = require('../utils/helpers.js');
|
|
9
|
+
|
|
10
|
+
class CurrencyController extends baseControllers.BaseCurrencyController {
|
|
11
|
+
constructor({
|
|
12
|
+
config,
|
|
13
|
+
state,
|
|
14
|
+
onNetworkChanged
|
|
15
|
+
}) {
|
|
16
|
+
super({
|
|
17
|
+
config,
|
|
18
|
+
state
|
|
19
|
+
});
|
|
20
|
+
_defineProperty(this, "conversionInterval", void 0);
|
|
21
|
+
this.defaultState = _objectSpread(_objectSpread({}, this.defaultState), {}, {
|
|
22
|
+
commonDenomination: "USD",
|
|
23
|
+
commonDenominatorPrice: 0
|
|
24
|
+
});
|
|
25
|
+
this.initialize();
|
|
26
|
+
onNetworkChanged(networkState => {
|
|
27
|
+
// to be called as (listener) => this.networkController.on('networkDidChange', listener);
|
|
28
|
+
if (networkState.providerConfig.ticker.toUpperCase() !== this.state.nativeCurrency.toUpperCase()) {
|
|
29
|
+
this.setNativeCurrency(networkState.providerConfig.ticker);
|
|
30
|
+
this.updateConversionRate();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
setCommonDenomination(commonDenomination) {
|
|
35
|
+
this.update({
|
|
36
|
+
commonDenomination
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
getCommonDenomination() {
|
|
40
|
+
return this.state.commonDenomination;
|
|
41
|
+
}
|
|
42
|
+
setCommonDenominatorPrice(commonDenominatorPrice) {
|
|
43
|
+
this.update({
|
|
44
|
+
commonDenominatorPrice
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
getCommonDenominatorPrice() {
|
|
48
|
+
return this.state.commonDenominatorPrice;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
|
|
53
|
+
* stored at the controller's conversionInterval property. If it is called and such an id already exists, the
|
|
54
|
+
* previous interval is clear and a new one is created.
|
|
55
|
+
*/
|
|
56
|
+
scheduleConversionInterval() {
|
|
57
|
+
if (this.conversionInterval) {
|
|
58
|
+
window.clearInterval(this.conversionInterval);
|
|
59
|
+
}
|
|
60
|
+
this.conversionInterval = window.setInterval(() => {
|
|
61
|
+
if (!helpers.idleTimeTracker.checkIfIdle()) {
|
|
62
|
+
this.updateConversionRate();
|
|
63
|
+
}
|
|
64
|
+
}, this.config.pollInterval);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
|
|
69
|
+
* fetched from an external API
|
|
70
|
+
*/
|
|
71
|
+
async updateConversionRate() {
|
|
72
|
+
const currentCurrency = this.getCurrentCurrency();
|
|
73
|
+
const nativeCurrency = this.getNativeCurrency();
|
|
74
|
+
const commonDenomination = this.getCommonDenomination();
|
|
75
|
+
const conversionRate = await this.retrieveConversionRate(nativeCurrency, currentCurrency, commonDenomination);
|
|
76
|
+
const currentCurrencyRate = Number.parseFloat(conversionRate[currentCurrency.toUpperCase()]);
|
|
77
|
+
const commonDenominationRate = Number.parseFloat(conversionRate[commonDenomination.toUpperCase()]);
|
|
78
|
+
// set conversion rate
|
|
79
|
+
if (currentCurrencyRate || commonDenominationRate) {
|
|
80
|
+
// ETC
|
|
81
|
+
this.setConversionRate(currentCurrencyRate);
|
|
82
|
+
this.setConversionDate(Math.floor(Date.now() / 1000).toString());
|
|
83
|
+
if (currentCurrency.toUpperCase() === commonDenomination.toUpperCase()) {
|
|
84
|
+
this.setCommonDenominatorPrice(currentCurrencyRate);
|
|
85
|
+
} else {
|
|
86
|
+
this.setCommonDenominatorPrice(commonDenominationRate);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
this.setConversionRate(0);
|
|
90
|
+
this.setConversionDate("N/A");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async retrieveConversionRate(fromCurrency, toCurrency, commonDenomination) {
|
|
94
|
+
try {
|
|
95
|
+
let apiUrl = `${this.config.api}/currency?fsym=${fromCurrency.toUpperCase()}&tsyms=${toCurrency.toUpperCase()}`;
|
|
96
|
+
if (commonDenomination && commonDenomination.toUpperCase() !== toCurrency.toUpperCase()) {
|
|
97
|
+
apiUrl += `,${commonDenomination.toUpperCase()}`;
|
|
98
|
+
}
|
|
99
|
+
const parsedResponse = await httpHelpers.get(apiUrl);
|
|
100
|
+
return parsedResponse;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
log.error(error, `CurrencyController - updateCommonDenominatorPrice: Failed to query rate for currency: ${fromCurrency}/ ${toCurrency}`);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
[toCurrency.toUpperCase()]: "0",
|
|
106
|
+
[commonDenomination.toUpperCase()]: "0"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
exports.CurrencyController = CurrencyController;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
|
|
4
|
+
var util = require('@ethereumjs/util');
|
|
5
|
+
var baseControllers = require('@toruslabs/base-controllers');
|
|
6
|
+
var log = require('loglevel');
|
|
7
|
+
var constants = require('../utils/constants.js');
|
|
8
|
+
var helpers = require('../utils/helpers.js');
|
|
9
|
+
var gasUtil = require('./gasUtil.js');
|
|
10
|
+
|
|
11
|
+
const GAS_FEE_API = "https://mock-gas-server.herokuapp.com/";
|
|
12
|
+
const LEGACY_GAS_PRICES_API_URL = "https://api.metaswap.codefi.network/gasPrices";
|
|
13
|
+
/**
|
|
14
|
+
* Returns gas prices in dec gwei
|
|
15
|
+
*/
|
|
16
|
+
class GasFeeController extends baseControllers.BaseController {
|
|
17
|
+
constructor({
|
|
18
|
+
config,
|
|
19
|
+
state,
|
|
20
|
+
getNetworkIdentifier,
|
|
21
|
+
getProvider,
|
|
22
|
+
fetchGasEstimates = gasUtil.fetchGasEstimates,
|
|
23
|
+
fetchEthGasPriceEstimate = gasUtil.fetchEthGasPriceEstimate,
|
|
24
|
+
fetchLegacyGasPriceEstimates = gasUtil.fetchLegacyGasPriceEstimates,
|
|
25
|
+
fetchGasEstimatesViaEthFeeHistory = gasUtil.fetchGasEstimatesViaEthFeeHistory,
|
|
26
|
+
getCurrentNetworkLegacyGasAPICompatibility,
|
|
27
|
+
getCurrentNetworkEIP1559Compatibility,
|
|
28
|
+
getCurrentAccountEIP1559Compatibility,
|
|
29
|
+
onNetworkStateChange
|
|
30
|
+
}) {
|
|
31
|
+
super({
|
|
32
|
+
config,
|
|
33
|
+
state
|
|
34
|
+
});
|
|
35
|
+
_defineProperty(this, "name", "GasFeeController");
|
|
36
|
+
// https://0x.org/docs/introduction/0x-cheat-sheet#swap-api-endpoints
|
|
37
|
+
_defineProperty(this, "API_SUPPORTED_CHAINIDS", new Set(["0x1", "0x5", "0xa4b1", "0xa86a", "0x2105", "0x38", "0xfa", "0xa", "0x89"]));
|
|
38
|
+
_defineProperty(this, "intervalId", void 0);
|
|
39
|
+
_defineProperty(this, "provider", void 0);
|
|
40
|
+
_defineProperty(this, "currentChainId", void 0);
|
|
41
|
+
_defineProperty(this, "getNetworkIdentifier", void 0);
|
|
42
|
+
_defineProperty(this, "getProvider", void 0);
|
|
43
|
+
_defineProperty(this, "fetchGasEstimates", void 0);
|
|
44
|
+
_defineProperty(this, "fetchGasEstimatesViaEthFeeHistory", void 0);
|
|
45
|
+
_defineProperty(this, "fetchEthGasPriceEstimate", void 0);
|
|
46
|
+
_defineProperty(this, "fetchLegacyGasPriceEstimates", void 0);
|
|
47
|
+
_defineProperty(this, "getCurrentNetworkEIP1559Compatibility", void 0);
|
|
48
|
+
_defineProperty(this, "getCurrentAccountEIP1559Compatibility", void 0);
|
|
49
|
+
_defineProperty(this, "getCurrentNetworkLegacyGasAPICompatibility", void 0);
|
|
50
|
+
this.getNetworkIdentifier = getNetworkIdentifier;
|
|
51
|
+
this.getProvider = getProvider;
|
|
52
|
+
this.fetchGasEstimates = fetchGasEstimates;
|
|
53
|
+
this.fetchEthGasPriceEstimate = fetchEthGasPriceEstimate;
|
|
54
|
+
this.fetchLegacyGasPriceEstimates = fetchLegacyGasPriceEstimates;
|
|
55
|
+
this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility;
|
|
56
|
+
this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility;
|
|
57
|
+
this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility;
|
|
58
|
+
this.fetchGasEstimatesViaEthFeeHistory = fetchGasEstimatesViaEthFeeHistory;
|
|
59
|
+
this.defaultConfig = {
|
|
60
|
+
interval: 30000,
|
|
61
|
+
legacyAPIEndpoint: LEGACY_GAS_PRICES_API_URL,
|
|
62
|
+
EIP1559APIEndpoint: GAS_FEE_API
|
|
63
|
+
};
|
|
64
|
+
this.defaultState = {
|
|
65
|
+
gasFeeEstimates: {},
|
|
66
|
+
estimatedGasFeeTimeBounds: {},
|
|
67
|
+
gasEstimateType: constants.GAS_ESTIMATE_TYPES.NONE
|
|
68
|
+
};
|
|
69
|
+
// Initialize.
|
|
70
|
+
this.currentChainId = this.getNetworkIdentifier();
|
|
71
|
+
this.provider = this.getProvider();
|
|
72
|
+
this.initialize();
|
|
73
|
+
onNetworkStateChange(() => {
|
|
74
|
+
this.onNetworkStateChange();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async onNetworkStateChange() {
|
|
78
|
+
this.provider = this.getProvider();
|
|
79
|
+
const newChainId = this.getNetworkIdentifier();
|
|
80
|
+
if (this.currentChainId !== newChainId) {
|
|
81
|
+
this.currentChainId = newChainId;
|
|
82
|
+
await this.fetchGasFeeEstimates();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async resetPolling() {
|
|
86
|
+
this.stopPolling();
|
|
87
|
+
await this.getGasFeeEstimatesAndStartPolling();
|
|
88
|
+
}
|
|
89
|
+
async fetchGasFeeEstimates() {
|
|
90
|
+
return this._fetchGasFeeEstimateData();
|
|
91
|
+
}
|
|
92
|
+
async getGasFeeEstimatesAndStartPolling() {
|
|
93
|
+
await this._fetchGasFeeEstimateData();
|
|
94
|
+
this._startPolling();
|
|
95
|
+
}
|
|
96
|
+
disconnectPoller() {
|
|
97
|
+
this.stopPolling();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Prepare to discard this controller.
|
|
102
|
+
*
|
|
103
|
+
* This stops any active polling.
|
|
104
|
+
*/
|
|
105
|
+
destroy() {
|
|
106
|
+
this.stopPolling();
|
|
107
|
+
}
|
|
108
|
+
stopPolling() {
|
|
109
|
+
if (this.intervalId) {
|
|
110
|
+
clearInterval(this.intervalId);
|
|
111
|
+
}
|
|
112
|
+
this.resetState();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Gets and sets gasFeeEstimates in state
|
|
117
|
+
*
|
|
118
|
+
* @returns GasFeeEstimates
|
|
119
|
+
*/
|
|
120
|
+
async _fetchGasFeeEstimateData() {
|
|
121
|
+
let isEIP1559Compatible;
|
|
122
|
+
const isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility();
|
|
123
|
+
const chainId = this.getNetworkIdentifier();
|
|
124
|
+
if (chainId === "loading") return;
|
|
125
|
+
let chainIdInt;
|
|
126
|
+
if (typeof chainId === "string" && util.isHexString(util.addHexPrefix(chainId))) {
|
|
127
|
+
chainIdInt = Number.parseInt(chainId, 16);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
isEIP1559Compatible = await this.getEIP1559Compatibility();
|
|
131
|
+
log.info("eip1559 compatible", isEIP1559Compatible);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
log.warn(error);
|
|
134
|
+
isEIP1559Compatible = false;
|
|
135
|
+
}
|
|
136
|
+
let newState = baseControllers.cloneDeep(this.defaultState);
|
|
137
|
+
try {
|
|
138
|
+
if (isEIP1559Compatible) {
|
|
139
|
+
let estimates;
|
|
140
|
+
try {
|
|
141
|
+
if (this.API_SUPPORTED_CHAINIDS.has(chainId)) {
|
|
142
|
+
estimates = await this.fetchGasEstimates(this.config.EIP1559APIEndpoint.replace("<chain_id>", `${chainIdInt}`));
|
|
143
|
+
} else {
|
|
144
|
+
throw new Error("ChainId not supported by api");
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
estimates = await this.fetchGasEstimatesViaEthFeeHistory(this.provider);
|
|
148
|
+
}
|
|
149
|
+
const {
|
|
150
|
+
suggestedMaxPriorityFeePerGas,
|
|
151
|
+
suggestedMaxFeePerGas
|
|
152
|
+
} = estimates.medium;
|
|
153
|
+
const estimatedGasFeeTimeBounds = this.getTimeEstimate(suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas);
|
|
154
|
+
newState = {
|
|
155
|
+
gasFeeEstimates: estimates,
|
|
156
|
+
estimatedGasFeeTimeBounds,
|
|
157
|
+
gasEstimateType: constants.GAS_ESTIMATE_TYPES.FEE_MARKET
|
|
158
|
+
};
|
|
159
|
+
} else if (isLegacyGasAPICompatible) {
|
|
160
|
+
const estimates = await this.fetchLegacyGasPriceEstimates(this.config.legacyAPIEndpoint.replace("<chain_id>", `${chainIdInt}`));
|
|
161
|
+
newState = {
|
|
162
|
+
gasFeeEstimates: estimates,
|
|
163
|
+
estimatedGasFeeTimeBounds: {},
|
|
164
|
+
gasEstimateType: constants.GAS_ESTIMATE_TYPES.LEGACY
|
|
165
|
+
};
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error("Main gas fee/price estimation failed. Use fallback");
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
try {
|
|
171
|
+
const estimates = await this.fetchEthGasPriceEstimate(this.provider);
|
|
172
|
+
newState = {
|
|
173
|
+
gasFeeEstimates: estimates,
|
|
174
|
+
estimatedGasFeeTimeBounds: {},
|
|
175
|
+
gasEstimateType: constants.GAS_ESTIMATE_TYPES.ETH_GASPRICE
|
|
176
|
+
};
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw new Error(`Gas fee/price estimation failed. Message: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
this.update(newState);
|
|
182
|
+
return newState;
|
|
183
|
+
}
|
|
184
|
+
async _startPolling() {
|
|
185
|
+
this._poll();
|
|
186
|
+
}
|
|
187
|
+
async _poll() {
|
|
188
|
+
if (this.intervalId) {
|
|
189
|
+
window.clearInterval(this.intervalId);
|
|
190
|
+
}
|
|
191
|
+
this.intervalId = window.setInterval(async () => {
|
|
192
|
+
if (!helpers.idleTimeTracker.checkIfIdle()) {
|
|
193
|
+
await this._fetchGasFeeEstimateData();
|
|
194
|
+
}
|
|
195
|
+
}, this.config.interval);
|
|
196
|
+
}
|
|
197
|
+
resetState() {
|
|
198
|
+
this.update(baseControllers.cloneDeep(this.defaultState));
|
|
199
|
+
}
|
|
200
|
+
async getEIP1559Compatibility() {
|
|
201
|
+
var _this$getCurrentAccou, _this$getCurrentAccou2;
|
|
202
|
+
const currentNetworkIsEIP1559Compatible = await this.getCurrentNetworkEIP1559Compatibility();
|
|
203
|
+
const currentAccountIsEIP1559Compatible = (_this$getCurrentAccou = (_this$getCurrentAccou2 = this.getCurrentAccountEIP1559Compatibility) === null || _this$getCurrentAccou2 === void 0 ? void 0 : _this$getCurrentAccou2.call(this)) !== null && _this$getCurrentAccou !== void 0 ? _this$getCurrentAccou : true;
|
|
204
|
+
return currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible;
|
|
205
|
+
}
|
|
206
|
+
getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) {
|
|
207
|
+
if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== constants.GAS_ESTIMATE_TYPES.FEE_MARKET) {
|
|
208
|
+
return {};
|
|
209
|
+
}
|
|
210
|
+
return gasUtil.calculateTimeEstimate(maxPriorityFeePerGas, maxFeePerGas, this.state.gasFeeEstimates);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
exports.GasFeeController = GasFeeController;
|