UNPKG

sparrow-controllers

Version:

Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets

243 lines 11.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GasFeeController = exports.GAS_ESTIMATE_TYPES = exports.LEGACY_GAS_PRICES_API_URL = void 0; const eth_query_1 = __importDefault(require("eth-query")); const uuid_1 = require("uuid"); const ethereumjs_util_1 = require("ethereumjs-util"); const BaseControllerV2_1 = require("../BaseControllerV2"); const util_1 = require("../util"); const gas_util_1 = require("./gas-util"); const determineGasFeeCalculations_1 = __importDefault(require("./determineGasFeeCalculations")); const fetchGasEstimatesViaEthFeeHistory_1 = __importDefault(require("./fetchGasEstimatesViaEthFeeHistory")); const GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/'; exports.LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`; /** * Indicates which type of gasEstimate the controller is currently returning. * This is useful as a way of asserting that the shape of gasEstimates matches * expectations. NONE is a special case indicating that no previous gasEstimate * has been fetched. */ exports.GAS_ESTIMATE_TYPES = { FEE_MARKET: 'fee-market', LEGACY: 'legacy', ETH_GASPRICE: 'eth_gasPrice', NONE: 'none', }; const metadata = { gasFeeEstimates: { persist: true, anonymous: false }, estimatedGasFeeTimeBounds: { persist: true, anonymous: false }, gasEstimateType: { persist: true, anonymous: false }, }; const name = 'GasFeeController'; const defaultState = { gasFeeEstimates: {}, estimatedGasFeeTimeBounds: {}, gasEstimateType: exports.GAS_ESTIMATE_TYPES.NONE, }; /** * Controller that retrieves gas fee estimate data and polls for updated data on a set interval */ class GasFeeController extends BaseControllerV2_1.BaseController { /** * Creates a GasFeeController instance. * * @param options - The controller options. * @param options.interval - The time in milliseconds to wait between polls. * @param options.messenger - The controller messenger. * @param options.state - The initial state. * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current * network is EIP-1559 compatible. * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the * current network is compatible with the legacy gas price API. * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current * account is EIP-1559 compatible. * @param options.getChainId - Returns the current chain ID. * @param options.getProvider - Returns a network provider for the current network. * @param options.onNetworkStateChange - A function for registering an event handler for the * network state change event. * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for * testing purposes. * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. This option is primarily * for testing purposes. * @param options.clientId - The client ID used to identify to the gas estimation API who is * asking for estimates. */ constructor({ interval = 15000, messenger, state, getCurrentNetworkEIP1559Compatibility, getCurrentAccountEIP1559Compatibility, getChainId, getCurrentNetworkLegacyGasAPICompatibility, getProvider, onNetworkStateChange, legacyAPIEndpoint = exports.LEGACY_GAS_PRICES_API_URL, EIP1559APIEndpoint = GAS_FEE_API, clientId, }) { super({ name, metadata, messenger, state: Object.assign(Object.assign({}, defaultState), state), }); this.intervalDelay = interval; this.pollTokens = new Set(); this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility; this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility; this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility; this.EIP1559APIEndpoint = EIP1559APIEndpoint; this.legacyAPIEndpoint = legacyAPIEndpoint; this.getChainId = getChainId; this.currentChainId = this.getChainId(); const provider = getProvider(); this.ethQuery = new eth_query_1.default(provider); this.clientId = clientId; onNetworkStateChange(() => __awaiter(this, void 0, void 0, function* () { const newProvider = getProvider(); const newChainId = this.getChainId(); this.ethQuery = new eth_query_1.default(newProvider); if (this.currentChainId !== newChainId) { this.currentChainId = newChainId; yield this.resetPolling(); } })); } resetPolling() { return __awaiter(this, void 0, void 0, function* () { if (this.pollTokens.size !== 0) { const tokens = Array.from(this.pollTokens); this.stopPolling(); yield this.getGasFeeEstimatesAndStartPolling(tokens[0]); tokens.slice(1).forEach((token) => { this.pollTokens.add(token); }); } }); } fetchGasFeeEstimates(options) { return __awaiter(this, void 0, void 0, function* () { return yield this._fetchGasFeeEstimateData(options); }); } getGasFeeEstimatesAndStartPolling(pollToken) { return __awaiter(this, void 0, void 0, function* () { const _pollToken = pollToken || (0, uuid_1.v1)(); this.pollTokens.add(_pollToken); if (this.pollTokens.size === 1) { yield this._fetchGasFeeEstimateData(); this._poll(); } return _pollToken; }); } /** * Gets and sets gasFeeEstimates in state. * * @param options - The gas fee estimate options. * @param options.shouldUpdateState - Determines whether the state should be updated with the * updated gas estimates. * @returns The gas fee estimates. */ _fetchGasFeeEstimateData(options = {}) { return __awaiter(this, void 0, void 0, function* () { const { shouldUpdateState = true } = options; let isEIP1559Compatible; const isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility(); let chainId = this.getChainId(); if (typeof chainId === 'string' && (0, ethereumjs_util_1.isHexString)(chainId)) { chainId = parseInt(chainId, 16); } try { isEIP1559Compatible = yield this.getEIP1559Compatibility(); } catch (e) { console.error(e); isEIP1559Compatible = false; } const gasFeeCalculations = yield (0, determineGasFeeCalculations_1.default)({ isEIP1559Compatible, isLegacyGasAPICompatible, fetchGasEstimates: gas_util_1.fetchGasEstimates, fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace('<chain_id>', `${chainId}`), fetchGasEstimatesViaEthFeeHistory: fetchGasEstimatesViaEthFeeHistory_1.default, fetchLegacyGasPriceEstimates: gas_util_1.fetchLegacyGasPriceEstimates, fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace('<chain_id>', `${chainId}`), fetchEthGasPriceEstimate: gas_util_1.fetchEthGasPriceEstimate, calculateTimeEstimate: gas_util_1.calculateTimeEstimate, clientId: this.clientId, ethQuery: this.ethQuery, }); if (shouldUpdateState) { this.update((state) => { state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates; state.estimatedGasFeeTimeBounds = gasFeeCalculations.estimatedGasFeeTimeBounds; state.gasEstimateType = gasFeeCalculations.gasEstimateType; }); } return gasFeeCalculations; }); } /** * Remove the poll token, and stop polling if the set of poll tokens is empty. * * @param pollToken - The poll token to disconnect. */ disconnectPoller(pollToken) { this.pollTokens.delete(pollToken); if (this.pollTokens.size === 0) { this.stopPolling(); } } stopPolling() { if (this.intervalId) { clearInterval(this.intervalId); } this.pollTokens.clear(); this.resetState(); } /** * Prepare to discard this controller. * * This stops any active polling. */ destroy() { super.destroy(); this.stopPolling(); } _poll() { if (this.intervalId) { clearInterval(this.intervalId); } this.intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () { yield (0, util_1.safelyExecute)(() => this._fetchGasFeeEstimateData()); }), this.intervalDelay); } resetState() { this.update(() => { return defaultState; }); } getEIP1559Compatibility() { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const currentNetworkIsEIP1559Compatible = yield this.getCurrentNetworkEIP1559Compatibility(); const currentAccountIsEIP1559Compatible = (_b = (_a = this.getCurrentAccountEIP1559Compatibility) === null || _a === void 0 ? void 0 : _a.call(this)) !== null && _b !== void 0 ? _b : true; return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible); }); } getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) { if (!this.state.gasFeeEstimates || this.state.gasEstimateType !== exports.GAS_ESTIMATE_TYPES.FEE_MARKET) { return {}; } return (0, gas_util_1.calculateTimeEstimate)(maxPriorityFeePerGas, maxFeePerGas, this.state.gasFeeEstimates); } } exports.GasFeeController = GasFeeController; exports.default = GasFeeController; //# sourceMappingURL=GasFeeController.js.map