@metamask/gas-fee-controller 1.0.0

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +20 -0
  3. package/README.md +15 -0
  4. package/dist/GasFeeController.d.ts +229 -0
  5. package/dist/GasFeeController.js +264 -0
  6. package/dist/GasFeeController.js.map +1 -0
  7. package/dist/determineGasFeeCalculations.d.ts +40 -0
  8. package/dist/determineGasFeeCalculations.js +87 -0
  9. package/dist/determineGasFeeCalculations.js.map +1 -0
  10. package/dist/fetchBlockFeeHistory.d.ts +115 -0
  11. package/dist/fetchBlockFeeHistory.js +202 -0
  12. package/dist/fetchBlockFeeHistory.js.map +1 -0
  13. package/dist/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.d.ts +16 -0
  14. package/dist/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.js +89 -0
  15. package/dist/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.js.map +1 -0
  16. package/dist/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.d.ts +10 -0
  17. package/dist/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.js +32 -0
  18. package/dist/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.js.map +1 -0
  19. package/dist/fetchGasEstimatesViaEthFeeHistory/medianOf.d.ts +10 -0
  20. package/dist/fetchGasEstimatesViaEthFeeHistory/medianOf.js +17 -0
  21. package/dist/fetchGasEstimatesViaEthFeeHistory/medianOf.js.map +1 -0
  22. package/dist/fetchGasEstimatesViaEthFeeHistory/types.d.ts +10 -0
  23. package/dist/fetchGasEstimatesViaEthFeeHistory/types.js +3 -0
  24. package/dist/fetchGasEstimatesViaEthFeeHistory/types.js.map +1 -0
  25. package/dist/fetchGasEstimatesViaEthFeeHistory.d.ts +21 -0
  26. package/dist/fetchGasEstimatesViaEthFeeHistory.js +53 -0
  27. package/dist/fetchGasEstimatesViaEthFeeHistory.js.map +1 -0
  28. package/dist/gas-util.d.ts +41 -0
  29. package/dist/gas-util.js +140 -0
  30. package/dist/gas-util.js.map +1 -0
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.js +18 -0
  33. package/dist/index.js.map +1 -0
  34. package/package.json +64 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.0.0]
10
+ ### Added
11
+ - Initial release
12
+ - As a result of converting our shared controllers repo into a monorepo ([#831](https://github.com/MetaMask/controllers/pull/831)), we've created this package from select parts of [`@metamask/controllers` v33.0.0](https://github.com/MetaMask/controllers/tree/v33.0.0), namely:
13
+ - Everything in `src/gas`
14
+
15
+ All changes listed after this point were applied to this package following the monorepo conversion.
16
+
17
+ [Unreleased]: https://github.com/MetaMask/controllers/compare/@metamask/gas-fee-controller@1.0.0...HEAD
18
+ [1.0.0]: https://github.com/MetaMask/controllers/releases/tag/@metamask/gas-fee-controller@1.0.0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 MetaMask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@metamask/gas-fee-controller`
2
+
3
+ Periodically calculates gas fee estimates based on various gas limits as well as other data displayed on transaction confirm screens.
4
+
5
+ ## Installation
6
+
7
+ `yarn add @metamask/gas-fee-controller`
8
+
9
+ or
10
+
11
+ `npm install @metamask/gas-fee-controller`
12
+
13
+ ## Contributing
14
+
15
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/controllers#readme).
@@ -0,0 +1,229 @@
1
+ import type { Patch } from 'immer';
2
+ import { BaseControllerV2, RestrictedControllerMessenger } from '@metamask/base-controller';
3
+ import type { NetworkControllerGetEthQueryAction, NetworkControllerGetProviderConfigAction, NetworkControllerProviderChangeEvent, NetworkController, NetworkState } from '@metamask/network-controller';
4
+ export declare const LEGACY_GAS_PRICES_API_URL = "https://api.metaswap.codefi.network/gasPrices";
5
+ export declare type unknownString = 'unknown';
6
+ export declare type FeeMarketEstimateType = 'fee-market';
7
+ export declare type LegacyEstimateType = 'legacy';
8
+ export declare type EthGasPriceEstimateType = 'eth_gasPrice';
9
+ export declare type NoEstimateType = 'none';
10
+ /**
11
+ * Indicates which type of gasEstimate the controller is currently returning.
12
+ * This is useful as a way of asserting that the shape of gasEstimates matches
13
+ * expectations. NONE is a special case indicating that no previous gasEstimate
14
+ * has been fetched.
15
+ */
16
+ export declare const GAS_ESTIMATE_TYPES: {
17
+ FEE_MARKET: "fee-market";
18
+ LEGACY: "legacy";
19
+ ETH_GASPRICE: "eth_gasPrice";
20
+ NONE: "none";
21
+ };
22
+ export declare type GasEstimateType = FeeMarketEstimateType | EthGasPriceEstimateType | LegacyEstimateType | NoEstimateType;
23
+ export declare type EstimatedGasFeeTimeBounds = {
24
+ lowerTimeBound: number | null;
25
+ upperTimeBound: number | unknownString;
26
+ };
27
+ /**
28
+ * @type EthGasPriceEstimate
29
+ *
30
+ * A single gas price estimate for networks and accounts that don't support EIP-1559
31
+ * This estimate comes from eth_gasPrice but is converted to dec gwei to match other
32
+ * return values
33
+ * @property gasPrice - A GWEI dec string
34
+ */
35
+ export declare type EthGasPriceEstimate = {
36
+ gasPrice: string;
37
+ };
38
+ /**
39
+ * @type LegacyGasPriceEstimate
40
+ *
41
+ * A set of gas price estimates for networks and accounts that don't support EIP-1559
42
+ * These estimates include low, medium and high all as strings representing gwei in
43
+ * decimal format.
44
+ * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion
45
+ * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion
46
+ * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion
47
+ */
48
+ export declare type LegacyGasPriceEstimate = {
49
+ high: string;
50
+ medium: string;
51
+ low: string;
52
+ };
53
+ /**
54
+ * @type Eip1559GasFee
55
+ *
56
+ * Data necessary to provide an estimate of a gas fee with a specific tip
57
+ * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds
58
+ * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds
59
+ * @property suggestedMaxPriorityFeePerGas - A suggested "tip", a GWEI hex number
60
+ * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number
61
+ */
62
+ export declare type Eip1559GasFee = {
63
+ minWaitTimeEstimate: number;
64
+ maxWaitTimeEstimate: number;
65
+ suggestedMaxPriorityFeePerGas: string;
66
+ suggestedMaxFeePerGas: string;
67
+ };
68
+ /**
69
+ * @type GasFeeEstimates
70
+ *
71
+ * Data necessary to provide multiple GasFee estimates, and supporting information, to the user
72
+ * @property low - A GasFee for a minimum necessary combination of tip and maxFee
73
+ * @property medium - A GasFee for a recommended combination of tip and maxFee
74
+ * @property high - A GasFee for a high combination of tip and maxFee
75
+ * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number
76
+ * @property networkCongestion - A normalized number that can be used to gauge the congestion
77
+ * level of the network, with 0 meaning not congested and 1 meaning extremely congested
78
+ */
79
+ export declare type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;
80
+ declare type SourcedGasFeeEstimates = {
81
+ low: Eip1559GasFee;
82
+ medium: Eip1559GasFee;
83
+ high: Eip1559GasFee;
84
+ estimatedBaseFee: string;
85
+ historicalBaseFeeRange: [string, string];
86
+ baseFeeTrend: 'up' | 'down' | 'level';
87
+ latestPriorityFeeRange: [string, string];
88
+ historicalPriorityFeeRange: [string, string];
89
+ priorityFeeTrend: 'up' | 'down' | 'level';
90
+ networkCongestion: number;
91
+ };
92
+ declare type FallbackGasFeeEstimates = {
93
+ low: Eip1559GasFee;
94
+ medium: Eip1559GasFee;
95
+ high: Eip1559GasFee;
96
+ estimatedBaseFee: string;
97
+ historicalBaseFeeRange: null;
98
+ baseFeeTrend: null;
99
+ latestPriorityFeeRange: null;
100
+ historicalPriorityFeeRange: null;
101
+ priorityFeeTrend: null;
102
+ networkCongestion: null;
103
+ };
104
+ export declare type GasFeeStateEthGasPrice = {
105
+ gasFeeEstimates: EthGasPriceEstimate;
106
+ estimatedGasFeeTimeBounds: Record<string, never>;
107
+ gasEstimateType: EthGasPriceEstimateType;
108
+ };
109
+ export declare type GasFeeStateFeeMarket = {
110
+ gasFeeEstimates: GasFeeEstimates;
111
+ estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record<string, never>;
112
+ gasEstimateType: FeeMarketEstimateType;
113
+ };
114
+ export declare type GasFeeStateLegacy = {
115
+ gasFeeEstimates: LegacyGasPriceEstimate;
116
+ estimatedGasFeeTimeBounds: Record<string, never>;
117
+ gasEstimateType: LegacyEstimateType;
118
+ };
119
+ export declare type GasFeeStateNoEstimates = {
120
+ gasFeeEstimates: Record<string, never>;
121
+ estimatedGasFeeTimeBounds: Record<string, never>;
122
+ gasEstimateType: NoEstimateType;
123
+ };
124
+ export declare type FetchGasFeeEstimateOptions = {
125
+ shouldUpdateState?: boolean;
126
+ };
127
+ /**
128
+ * @type GasFeeState
129
+ *
130
+ * Gas Fee controller state
131
+ * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties
132
+ * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum
133
+ */
134
+ export declare type GasFeeState = GasFeeStateEthGasPrice | GasFeeStateFeeMarket | GasFeeStateLegacy | GasFeeStateNoEstimates;
135
+ declare const name = "GasFeeController";
136
+ export declare type GasFeeStateChange = {
137
+ type: `${typeof name}:stateChange`;
138
+ payload: [GasFeeState, Patch[]];
139
+ };
140
+ export declare type GetGasFeeState = {
141
+ type: `${typeof name}:getState`;
142
+ handler: () => GasFeeState;
143
+ };
144
+ declare type GasFeeMessenger = RestrictedControllerMessenger<typeof name, GetGasFeeState | NetworkControllerGetProviderConfigAction | NetworkControllerGetEthQueryAction, GasFeeStateChange | NetworkControllerProviderChangeEvent, NetworkControllerGetProviderConfigAction['type'] | NetworkControllerGetEthQueryAction['type'], NetworkControllerProviderChangeEvent['type']>;
145
+ export declare type ChainID = `0x${string}` | `${number}` | number;
146
+ /**
147
+ * Controller that retrieves gas fee estimate data and polls for updated data on a set interval
148
+ */
149
+ export declare class GasFeeController extends BaseControllerV2<typeof name, GasFeeState, GasFeeMessenger> {
150
+ private intervalId?;
151
+ private intervalDelay;
152
+ private pollTokens;
153
+ private legacyAPIEndpoint;
154
+ private EIP1559APIEndpoint;
155
+ private getCurrentNetworkEIP1559Compatibility;
156
+ private getCurrentNetworkLegacyGasAPICompatibility;
157
+ private getCurrentAccountEIP1559Compatibility;
158
+ private currentChainId;
159
+ private ethQuery;
160
+ private clientId?;
161
+ /**
162
+ * Creates a GasFeeController instance.
163
+ *
164
+ * @param options - The controller options.
165
+ * @param options.interval - The time in milliseconds to wait between polls.
166
+ * @param options.messenger - The controller messenger.
167
+ * @param options.state - The initial state.
168
+ * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current
169
+ * network is EIP-1559 compatible.
170
+ * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the
171
+ * current network is compatible with the legacy gas price API.
172
+ * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current
173
+ * account is EIP-1559 compatible.
174
+ * @param options.getChainId - Returns the current chain ID.
175
+ * @param options.getProvider - Returns a network provider for the current network.
176
+ * @param options.onNetworkStateChange - A function for registering an event handler for the
177
+ * network state change event.
178
+ * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for
179
+ * testing purposes.
180
+ * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. This option is primarily
181
+ * for testing purposes.
182
+ * @param options.clientId - The client ID used to identify to the gas estimation API who is
183
+ * asking for estimates.
184
+ */
185
+ constructor({ interval, messenger, state, getCurrentNetworkEIP1559Compatibility, getCurrentAccountEIP1559Compatibility, getChainId, getCurrentNetworkLegacyGasAPICompatibility, getProvider, onNetworkStateChange, legacyAPIEndpoint, EIP1559APIEndpoint, clientId, }: {
186
+ interval?: number;
187
+ messenger: GasFeeMessenger;
188
+ state?: GasFeeState;
189
+ getCurrentNetworkEIP1559Compatibility: () => Promise<boolean>;
190
+ getCurrentNetworkLegacyGasAPICompatibility: () => boolean;
191
+ getCurrentAccountEIP1559Compatibility?: () => boolean;
192
+ getChainId?: () => `0x${string}` | `${number}` | number;
193
+ getProvider: () => NetworkController['provider'];
194
+ onNetworkStateChange?: (listener: (state: NetworkState) => void) => void;
195
+ legacyAPIEndpoint?: string;
196
+ EIP1559APIEndpoint?: string;
197
+ clientId?: string;
198
+ });
199
+ resetPolling(): Promise<void>;
200
+ fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions): Promise<GasFeeState>;
201
+ getGasFeeEstimatesAndStartPolling(pollToken: string | undefined): Promise<string>;
202
+ /**
203
+ * Gets and sets gasFeeEstimates in state.
204
+ *
205
+ * @param options - The gas fee estimate options.
206
+ * @param options.shouldUpdateState - Determines whether the state should be updated with the
207
+ * updated gas estimates.
208
+ * @returns The gas fee estimates.
209
+ */
210
+ _fetchGasFeeEstimateData(options?: FetchGasFeeEstimateOptions): Promise<GasFeeState>;
211
+ /**
212
+ * Remove the poll token, and stop polling if the set of poll tokens is empty.
213
+ *
214
+ * @param pollToken - The poll token to disconnect.
215
+ */
216
+ disconnectPoller(pollToken: string): void;
217
+ stopPolling(): void;
218
+ /**
219
+ * Prepare to discard this controller.
220
+ *
221
+ * This stops any active polling.
222
+ */
223
+ destroy(): void;
224
+ private _poll;
225
+ private resetState;
226
+ private getEIP1559Compatibility;
227
+ getTimeEstimate(maxPriorityFeePerGas: string, maxFeePerGas: string): EstimatedGasFeeTimeBounds | Record<string, never>;
228
+ }
229
+ export default GasFeeController;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GasFeeController = exports.GAS_ESTIMATE_TYPES = exports.LEGACY_GAS_PRICES_API_URL = void 0;
16
+ const eth_query_1 = __importDefault(require("eth-query"));
17
+ const uuid_1 = require("uuid");
18
+ const ethereumjs_util_1 = require("ethereumjs-util");
19
+ const base_controller_1 = require("@metamask/base-controller");
20
+ const controller_utils_1 = require("@metamask/controller-utils");
21
+ const gas_util_1 = require("./gas-util");
22
+ const determineGasFeeCalculations_1 = __importDefault(require("./determineGasFeeCalculations"));
23
+ const fetchGasEstimatesViaEthFeeHistory_1 = __importDefault(require("./fetchGasEstimatesViaEthFeeHistory"));
24
+ const GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/';
25
+ exports.LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;
26
+ /**
27
+ * Indicates which type of gasEstimate the controller is currently returning.
28
+ * This is useful as a way of asserting that the shape of gasEstimates matches
29
+ * expectations. NONE is a special case indicating that no previous gasEstimate
30
+ * has been fetched.
31
+ */
32
+ exports.GAS_ESTIMATE_TYPES = {
33
+ FEE_MARKET: 'fee-market',
34
+ LEGACY: 'legacy',
35
+ ETH_GASPRICE: 'eth_gasPrice',
36
+ NONE: 'none',
37
+ };
38
+ const metadata = {
39
+ gasFeeEstimates: { persist: true, anonymous: false },
40
+ estimatedGasFeeTimeBounds: { persist: true, anonymous: false },
41
+ gasEstimateType: { persist: true, anonymous: false },
42
+ };
43
+ const name = 'GasFeeController';
44
+ const defaultState = {
45
+ gasFeeEstimates: {},
46
+ estimatedGasFeeTimeBounds: {},
47
+ gasEstimateType: exports.GAS_ESTIMATE_TYPES.NONE,
48
+ };
49
+ /**
50
+ * Controller that retrieves gas fee estimate data and polls for updated data on a set interval
51
+ */
52
+ class GasFeeController extends base_controller_1.BaseControllerV2 {
53
+ /**
54
+ * Creates a GasFeeController instance.
55
+ *
56
+ * @param options - The controller options.
57
+ * @param options.interval - The time in milliseconds to wait between polls.
58
+ * @param options.messenger - The controller messenger.
59
+ * @param options.state - The initial state.
60
+ * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current
61
+ * network is EIP-1559 compatible.
62
+ * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the
63
+ * current network is compatible with the legacy gas price API.
64
+ * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current
65
+ * account is EIP-1559 compatible.
66
+ * @param options.getChainId - Returns the current chain ID.
67
+ * @param options.getProvider - Returns a network provider for the current network.
68
+ * @param options.onNetworkStateChange - A function for registering an event handler for the
69
+ * network state change event.
70
+ * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for
71
+ * testing purposes.
72
+ * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. This option is primarily
73
+ * for testing purposes.
74
+ * @param options.clientId - The client ID used to identify to the gas estimation API who is
75
+ * asking for estimates.
76
+ */
77
+ constructor({ interval = 15000, messenger, state, getCurrentNetworkEIP1559Compatibility, getCurrentAccountEIP1559Compatibility, getChainId, getCurrentNetworkLegacyGasAPICompatibility, getProvider, onNetworkStateChange, legacyAPIEndpoint = exports.LEGACY_GAS_PRICES_API_URL, EIP1559APIEndpoint = GAS_FEE_API, clientId, }) {
78
+ super({
79
+ name,
80
+ metadata,
81
+ messenger,
82
+ state: Object.assign(Object.assign({}, defaultState), state),
83
+ });
84
+ this.intervalDelay = interval;
85
+ this.pollTokens = new Set();
86
+ this.getCurrentNetworkEIP1559Compatibility =
87
+ getCurrentNetworkEIP1559Compatibility;
88
+ this.getCurrentNetworkLegacyGasAPICompatibility =
89
+ getCurrentNetworkLegacyGasAPICompatibility;
90
+ this.getCurrentAccountEIP1559Compatibility =
91
+ getCurrentAccountEIP1559Compatibility;
92
+ this.EIP1559APIEndpoint = EIP1559APIEndpoint;
93
+ this.legacyAPIEndpoint = legacyAPIEndpoint;
94
+ this.clientId = clientId;
95
+ if (onNetworkStateChange && getChainId) {
96
+ const initialProvider = getProvider();
97
+ this.ethQuery = new eth_query_1.default(initialProvider);
98
+ this.currentChainId = getChainId();
99
+ onNetworkStateChange(() => __awaiter(this, void 0, void 0, function* () {
100
+ const newProvider = getProvider();
101
+ const newChainId = getChainId();
102
+ this.ethQuery = new eth_query_1.default(newProvider);
103
+ if (this.currentChainId !== newChainId) {
104
+ this.currentChainId = newChainId;
105
+ yield this.resetPolling();
106
+ }
107
+ }));
108
+ }
109
+ else {
110
+ const providerConfig = this.messagingSystem.call('NetworkController:getProviderConfig');
111
+ this.currentChainId = providerConfig.chainId;
112
+ this.ethQuery = this.messagingSystem.call('NetworkController:getEthQuery');
113
+ this.messagingSystem.subscribe('NetworkController:providerChange', (provider) => __awaiter(this, void 0, void 0, function* () {
114
+ this.ethQuery = this.messagingSystem.call('NetworkController:getEthQuery');
115
+ if (this.currentChainId !== provider.chainId) {
116
+ this.currentChainId = provider.chainId;
117
+ yield this.resetPolling();
118
+ }
119
+ }));
120
+ }
121
+ }
122
+ resetPolling() {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ if (this.pollTokens.size !== 0) {
125
+ const tokens = Array.from(this.pollTokens);
126
+ this.stopPolling();
127
+ yield this.getGasFeeEstimatesAndStartPolling(tokens[0]);
128
+ tokens.slice(1).forEach((token) => {
129
+ this.pollTokens.add(token);
130
+ });
131
+ }
132
+ });
133
+ }
134
+ fetchGasFeeEstimates(options) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ return yield this._fetchGasFeeEstimateData(options);
137
+ });
138
+ }
139
+ getGasFeeEstimatesAndStartPolling(pollToken) {
140
+ return __awaiter(this, void 0, void 0, function* () {
141
+ const _pollToken = pollToken || (0, uuid_1.v1)();
142
+ this.pollTokens.add(_pollToken);
143
+ if (this.pollTokens.size === 1) {
144
+ yield this._fetchGasFeeEstimateData();
145
+ this._poll();
146
+ }
147
+ return _pollToken;
148
+ });
149
+ }
150
+ /**
151
+ * Gets and sets gasFeeEstimates in state.
152
+ *
153
+ * @param options - The gas fee estimate options.
154
+ * @param options.shouldUpdateState - Determines whether the state should be updated with the
155
+ * updated gas estimates.
156
+ * @returns The gas fee estimates.
157
+ */
158
+ _fetchGasFeeEstimateData(options = {}) {
159
+ return __awaiter(this, void 0, void 0, function* () {
160
+ const { shouldUpdateState = true } = options;
161
+ let isEIP1559Compatible;
162
+ const isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility();
163
+ let chainId;
164
+ if (typeof this.currentChainId === 'string') {
165
+ if ((0, ethereumjs_util_1.isHexString)(this.currentChainId)) {
166
+ chainId = parseInt(this.currentChainId, 16);
167
+ }
168
+ else {
169
+ chainId = parseInt(this.currentChainId, 10);
170
+ }
171
+ }
172
+ else {
173
+ chainId = this.currentChainId;
174
+ }
175
+ try {
176
+ isEIP1559Compatible = yield this.getEIP1559Compatibility();
177
+ }
178
+ catch (e) {
179
+ console.error(e);
180
+ isEIP1559Compatible = false;
181
+ }
182
+ const gasFeeCalculations = yield (0, determineGasFeeCalculations_1.default)({
183
+ isEIP1559Compatible,
184
+ isLegacyGasAPICompatible,
185
+ fetchGasEstimates: gas_util_1.fetchGasEstimates,
186
+ fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace('<chain_id>', `${chainId}`),
187
+ fetchGasEstimatesViaEthFeeHistory: fetchGasEstimatesViaEthFeeHistory_1.default,
188
+ fetchLegacyGasPriceEstimates: gas_util_1.fetchLegacyGasPriceEstimates,
189
+ fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace('<chain_id>', `${chainId}`),
190
+ fetchEthGasPriceEstimate: gas_util_1.fetchEthGasPriceEstimate,
191
+ calculateTimeEstimate: gas_util_1.calculateTimeEstimate,
192
+ clientId: this.clientId,
193
+ ethQuery: this.ethQuery,
194
+ });
195
+ if (shouldUpdateState) {
196
+ this.update((state) => {
197
+ state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;
198
+ state.estimatedGasFeeTimeBounds =
199
+ gasFeeCalculations.estimatedGasFeeTimeBounds;
200
+ state.gasEstimateType = gasFeeCalculations.gasEstimateType;
201
+ });
202
+ }
203
+ return gasFeeCalculations;
204
+ });
205
+ }
206
+ /**
207
+ * Remove the poll token, and stop polling if the set of poll tokens is empty.
208
+ *
209
+ * @param pollToken - The poll token to disconnect.
210
+ */
211
+ disconnectPoller(pollToken) {
212
+ this.pollTokens.delete(pollToken);
213
+ if (this.pollTokens.size === 0) {
214
+ this.stopPolling();
215
+ }
216
+ }
217
+ stopPolling() {
218
+ if (this.intervalId) {
219
+ clearInterval(this.intervalId);
220
+ }
221
+ this.pollTokens.clear();
222
+ this.resetState();
223
+ }
224
+ /**
225
+ * Prepare to discard this controller.
226
+ *
227
+ * This stops any active polling.
228
+ */
229
+ destroy() {
230
+ super.destroy();
231
+ this.stopPolling();
232
+ }
233
+ _poll() {
234
+ if (this.intervalId) {
235
+ clearInterval(this.intervalId);
236
+ }
237
+ this.intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
238
+ yield (0, controller_utils_1.safelyExecute)(() => this._fetchGasFeeEstimateData());
239
+ }), this.intervalDelay);
240
+ }
241
+ resetState() {
242
+ this.update(() => {
243
+ return defaultState;
244
+ });
245
+ }
246
+ getEIP1559Compatibility() {
247
+ var _a, _b;
248
+ return __awaiter(this, void 0, void 0, function* () {
249
+ const currentNetworkIsEIP1559Compatible = yield this.getCurrentNetworkEIP1559Compatibility();
250
+ const currentAccountIsEIP1559Compatible = (_b = (_a = this.getCurrentAccountEIP1559Compatibility) === null || _a === void 0 ? void 0 : _a.call(this)) !== null && _b !== void 0 ? _b : true;
251
+ return (currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible);
252
+ });
253
+ }
254
+ getTimeEstimate(maxPriorityFeePerGas, maxFeePerGas) {
255
+ if (!this.state.gasFeeEstimates ||
256
+ this.state.gasEstimateType !== exports.GAS_ESTIMATE_TYPES.FEE_MARKET) {
257
+ return {};
258
+ }
259
+ return (0, gas_util_1.calculateTimeEstimate)(maxPriorityFeePerGas, maxFeePerGas, this.state.gasFeeEstimates);
260
+ }
261
+ }
262
+ exports.GasFeeController = GasFeeController;
263
+ exports.default = GasFeeController;
264
+ //# sourceMappingURL=GasFeeController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GasFeeController.js","sourceRoot":"","sources":["../src/GasFeeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Patch } from 'immer';\nimport EthQuery from 'eth-query';\nimport { v1 as random } from 'uuid';\nimport { isHexString } from 'ethereumjs-util';\nimport {\n BaseControllerV2,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type {\n NetworkControllerGetEthQueryAction,\n NetworkControllerGetProviderConfigAction,\n NetworkControllerProviderChangeEvent,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport {\n fetchGasEstimates,\n fetchLegacyGasPriceEstimates,\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n} from './gas-util';\nimport determineGasFeeCalculations from './determineGasFeeCalculations';\nimport fetchGasEstimatesViaEthFeeHistory from './fetchGasEstimatesViaEthFeeHistory';\n\nconst GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/';\nexport const LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;\n\nexport type unknownString = 'unknown';\n\n// Fee Market describes the way gas is set after the london hardfork, and was\n// defined by EIP-1559.\nexport type FeeMarketEstimateType = 'fee-market';\n// Legacy describes gasPrice estimates from before london hardfork, when the\n// user is connected to mainnet and are presented with fast/average/slow\n// estimate levels to choose from.\nexport type LegacyEstimateType = 'legacy';\n// EthGasPrice describes a gasPrice estimate received from eth_gasPrice. Post\n// london this value should only be used for legacy type transactions when on\n// networks that support EIP-1559. This type of estimate is the most accurate\n// to display on custom networks that don't support EIP-1559.\nexport type EthGasPriceEstimateType = 'eth_gasPrice';\n// NoEstimate describes the state of the controller before receiving its first\n// estimate.\nexport type NoEstimateType = 'none';\n\n/**\n * Indicates which type of gasEstimate the controller is currently returning.\n * This is useful as a way of asserting that the shape of gasEstimates matches\n * expectations. NONE is a special case indicating that no previous gasEstimate\n * has been fetched.\n */\nexport const GAS_ESTIMATE_TYPES = {\n FEE_MARKET: 'fee-market' as FeeMarketEstimateType,\n LEGACY: 'legacy' as LegacyEstimateType,\n ETH_GASPRICE: 'eth_gasPrice' as EthGasPriceEstimateType,\n NONE: 'none' as NoEstimateType,\n};\n\nexport type GasEstimateType =\n | FeeMarketEstimateType\n | EthGasPriceEstimateType\n | LegacyEstimateType\n | NoEstimateType;\n\nexport type EstimatedGasFeeTimeBounds = {\n lowerTimeBound: number | null;\n upperTimeBound: number | unknownString;\n};\n\n/**\n * @type EthGasPriceEstimate\n *\n * A single gas price estimate for networks and accounts that don't support EIP-1559\n * This estimate comes from eth_gasPrice but is converted to dec gwei to match other\n * return values\n * @property gasPrice - A GWEI dec string\n */\n\nexport type EthGasPriceEstimate = {\n gasPrice: string;\n};\n\n/**\n * @type LegacyGasPriceEstimate\n *\n * A set of gas price estimates for networks and accounts that don't support EIP-1559\n * These estimates include low, medium and high all as strings representing gwei in\n * decimal format.\n * @property high - gasPrice, in decimal gwei string format, suggested for fast inclusion\n * @property medium - gasPrice, in decimal gwei string format, suggested for avg inclusion\n * @property low - gasPrice, in decimal gwei string format, suggested for slow inclusion\n */\nexport type LegacyGasPriceEstimate = {\n high: string;\n medium: string;\n low: string;\n};\n\n/**\n * @type Eip1559GasFee\n *\n * Data necessary to provide an estimate of a gas fee with a specific tip\n * @property minWaitTimeEstimate - The fastest the transaction will take, in milliseconds\n * @property maxWaitTimeEstimate - The slowest the transaction will take, in milliseconds\n * @property suggestedMaxPriorityFeePerGas - A suggested \"tip\", a GWEI hex number\n * @property suggestedMaxFeePerGas - A suggested max fee, the most a user will pay. a GWEI hex number\n */\nexport type Eip1559GasFee = {\n minWaitTimeEstimate: number; // a time duration in milliseconds\n maxWaitTimeEstimate: number; // a time duration in milliseconds\n suggestedMaxPriorityFeePerGas: string; // a GWEI decimal number\n suggestedMaxFeePerGas: string; // a GWEI decimal number\n};\n\n/**\n * @type GasFeeEstimates\n *\n * Data necessary to provide multiple GasFee estimates, and supporting information, to the user\n * @property low - A GasFee for a minimum necessary combination of tip and maxFee\n * @property medium - A GasFee for a recommended combination of tip and maxFee\n * @property high - A GasFee for a high combination of tip and maxFee\n * @property estimatedBaseFee - An estimate of what the base fee will be for the pending/next block. A GWEI dec number\n * @property networkCongestion - A normalized number that can be used to gauge the congestion\n * level of the network, with 0 meaning not congested and 1 meaning extremely congested\n */\nexport type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;\n\ntype SourcedGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: [string, string];\n baseFeeTrend: 'up' | 'down' | 'level';\n latestPriorityFeeRange: [string, string];\n historicalPriorityFeeRange: [string, string];\n priorityFeeTrend: 'up' | 'down' | 'level';\n networkCongestion: number;\n};\n\ntype FallbackGasFeeEstimates = {\n low: Eip1559GasFee;\n medium: Eip1559GasFee;\n high: Eip1559GasFee;\n estimatedBaseFee: string;\n historicalBaseFeeRange: null;\n baseFeeTrend: null;\n latestPriorityFeeRange: null;\n historicalPriorityFeeRange: null;\n priorityFeeTrend: null;\n networkCongestion: null;\n};\n\nconst metadata = {\n gasFeeEstimates: { persist: true, anonymous: false },\n estimatedGasFeeTimeBounds: { persist: true, anonymous: false },\n gasEstimateType: { persist: true, anonymous: false },\n};\n\nexport type GasFeeStateEthGasPrice = {\n gasFeeEstimates: EthGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record<string, never>;\n gasEstimateType: EthGasPriceEstimateType;\n};\n\nexport type GasFeeStateFeeMarket = {\n gasFeeEstimates: GasFeeEstimates;\n estimatedGasFeeTimeBounds: EstimatedGasFeeTimeBounds | Record<string, never>;\n gasEstimateType: FeeMarketEstimateType;\n};\n\nexport type GasFeeStateLegacy = {\n gasFeeEstimates: LegacyGasPriceEstimate;\n estimatedGasFeeTimeBounds: Record<string, never>;\n gasEstimateType: LegacyEstimateType;\n};\n\nexport type GasFeeStateNoEstimates = {\n gasFeeEstimates: Record<string, never>;\n estimatedGasFeeTimeBounds: Record<string, never>;\n gasEstimateType: NoEstimateType;\n};\n\nexport type FetchGasFeeEstimateOptions = {\n shouldUpdateState?: boolean;\n};\n\n/**\n * @type GasFeeState\n *\n * Gas Fee controller state\n * @property gasFeeEstimates - Gas fee estimate data based on new EIP-1559 properties\n * @property estimatedGasFeeTimeBounds - Estimates representing the minimum and maximum\n */\nexport type GasFeeState =\n | GasFeeStateEthGasPrice\n | GasFeeStateFeeMarket\n | GasFeeStateLegacy\n | GasFeeStateNoEstimates;\n\nconst name = 'GasFeeController';\n\nexport type GasFeeStateChange = {\n type: `${typeof name}:stateChange`;\n payload: [GasFeeState, Patch[]];\n};\n\nexport type GetGasFeeState = {\n type: `${typeof name}:getState`;\n handler: () => GasFeeState;\n};\n\ntype GasFeeMessenger = RestrictedControllerMessenger<\n typeof name,\n | GetGasFeeState\n | NetworkControllerGetProviderConfigAction\n | NetworkControllerGetEthQueryAction,\n GasFeeStateChange | NetworkControllerProviderChangeEvent,\n | NetworkControllerGetProviderConfigAction['type']\n | NetworkControllerGetEthQueryAction['type'],\n NetworkControllerProviderChangeEvent['type']\n>;\n\nconst defaultState: GasFeeState = {\n gasFeeEstimates: {},\n estimatedGasFeeTimeBounds: {},\n gasEstimateType: GAS_ESTIMATE_TYPES.NONE,\n};\n\nexport type ChainID = `0x${string}` | `${number}` | number;\n\n/**\n * Controller that retrieves gas fee estimate data and polls for updated data on a set interval\n */\nexport class GasFeeController extends BaseControllerV2<\n typeof name,\n GasFeeState,\n GasFeeMessenger\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private intervalDelay;\n\n private pollTokens: Set<string>;\n\n private legacyAPIEndpoint: string;\n\n private EIP1559APIEndpoint: string;\n\n private getCurrentNetworkEIP1559Compatibility;\n\n private getCurrentNetworkLegacyGasAPICompatibility;\n\n private getCurrentAccountEIP1559Compatibility;\n\n private currentChainId;\n\n private ethQuery: any;\n\n private clientId?: string;\n\n /**\n * Creates a GasFeeController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The time in milliseconds to wait between polls.\n * @param options.messenger - The controller messenger.\n * @param options.state - The initial state.\n * @param options.getCurrentNetworkEIP1559Compatibility - Determines whether or not the current\n * network is EIP-1559 compatible.\n * @param options.getCurrentNetworkLegacyGasAPICompatibility - Determines whether or not the\n * current network is compatible with the legacy gas price API.\n * @param options.getCurrentAccountEIP1559Compatibility - Determines whether or not the current\n * account is EIP-1559 compatible.\n * @param options.getChainId - Returns the current chain ID.\n * @param options.getProvider - Returns a network provider for the current network.\n * @param options.onNetworkStateChange - A function for registering an event handler for the\n * network state change event.\n * @param options.legacyAPIEndpoint - The legacy gas price API URL. This option is primarily for\n * testing purposes.\n * @param options.EIP1559APIEndpoint - The EIP-1559 gas price API URL. This option is primarily\n * for testing purposes.\n * @param options.clientId - The client ID used to identify to the gas estimation API who is\n * asking for estimates.\n */\n constructor({\n interval = 15000,\n messenger,\n state,\n getCurrentNetworkEIP1559Compatibility,\n getCurrentAccountEIP1559Compatibility,\n getChainId,\n getCurrentNetworkLegacyGasAPICompatibility,\n getProvider,\n onNetworkStateChange,\n legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL,\n EIP1559APIEndpoint = GAS_FEE_API,\n clientId,\n }: {\n interval?: number;\n messenger: GasFeeMessenger;\n state?: GasFeeState;\n getCurrentNetworkEIP1559Compatibility: () => Promise<boolean>;\n getCurrentNetworkLegacyGasAPICompatibility: () => boolean;\n getCurrentAccountEIP1559Compatibility?: () => boolean;\n getChainId?: () => `0x${string}` | `${number}` | number;\n getProvider: () => NetworkController['provider'];\n onNetworkStateChange?: (listener: (state: NetworkState) => void) => void;\n legacyAPIEndpoint?: string;\n EIP1559APIEndpoint?: string;\n clientId?: string;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.pollTokens = new Set();\n this.getCurrentNetworkEIP1559Compatibility =\n getCurrentNetworkEIP1559Compatibility;\n\n this.getCurrentNetworkLegacyGasAPICompatibility =\n getCurrentNetworkLegacyGasAPICompatibility;\n\n this.getCurrentAccountEIP1559Compatibility =\n getCurrentAccountEIP1559Compatibility;\n this.EIP1559APIEndpoint = EIP1559APIEndpoint;\n this.legacyAPIEndpoint = legacyAPIEndpoint;\n this.clientId = clientId;\n if (onNetworkStateChange && getChainId) {\n const initialProvider = getProvider();\n this.ethQuery = new EthQuery(initialProvider);\n this.currentChainId = getChainId();\n onNetworkStateChange(async () => {\n const newProvider = getProvider();\n const newChainId = getChainId();\n this.ethQuery = new EthQuery(newProvider);\n if (this.currentChainId !== newChainId) {\n this.currentChainId = newChainId;\n await this.resetPolling();\n }\n });\n } else {\n const providerConfig = this.messagingSystem.call(\n 'NetworkController:getProviderConfig',\n );\n this.currentChainId = providerConfig.chainId;\n this.ethQuery = this.messagingSystem.call(\n 'NetworkController:getEthQuery',\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:providerChange',\n async (provider) => {\n this.ethQuery = this.messagingSystem.call(\n 'NetworkController:getEthQuery',\n );\n\n if (this.currentChainId !== provider.chainId) {\n this.currentChainId = provider.chainId;\n await this.resetPolling();\n }\n },\n );\n }\n }\n\n async resetPolling() {\n if (this.pollTokens.size !== 0) {\n const tokens = Array.from(this.pollTokens);\n this.stopPolling();\n await this.getGasFeeEstimatesAndStartPolling(tokens[0]);\n tokens.slice(1).forEach((token) => {\n this.pollTokens.add(token);\n });\n }\n }\n\n async fetchGasFeeEstimates(options?: FetchGasFeeEstimateOptions) {\n return await this._fetchGasFeeEstimateData(options);\n }\n\n async getGasFeeEstimatesAndStartPolling(\n pollToken: string | undefined,\n ): Promise<string> {\n const _pollToken = pollToken || random();\n\n this.pollTokens.add(_pollToken);\n\n if (this.pollTokens.size === 1) {\n await this._fetchGasFeeEstimateData();\n this._poll();\n }\n\n return _pollToken;\n }\n\n /**\n * Gets and sets gasFeeEstimates in state.\n *\n * @param options - The gas fee estimate options.\n * @param options.shouldUpdateState - Determines whether the state should be updated with the\n * updated gas estimates.\n * @returns The gas fee estimates.\n */\n async _fetchGasFeeEstimateData(\n options: FetchGasFeeEstimateOptions = {},\n ): Promise<GasFeeState> {\n const { shouldUpdateState = true } = options;\n let isEIP1559Compatible;\n const isLegacyGasAPICompatible =\n this.getCurrentNetworkLegacyGasAPICompatibility();\n\n let chainId: number;\n if (typeof this.currentChainId === 'string') {\n if (isHexString(this.currentChainId)) {\n chainId = parseInt(this.currentChainId, 16);\n } else {\n chainId = parseInt(this.currentChainId, 10);\n }\n } else {\n chainId = this.currentChainId;\n }\n\n try {\n isEIP1559Compatible = await this.getEIP1559Compatibility();\n } catch (e) {\n console.error(e);\n isEIP1559Compatible = false;\n }\n\n const gasFeeCalculations = await determineGasFeeCalculations({\n isEIP1559Compatible,\n isLegacyGasAPICompatible,\n fetchGasEstimates,\n fetchGasEstimatesUrl: this.EIP1559APIEndpoint.replace(\n '<chain_id>',\n `${chainId}`,\n ),\n fetchGasEstimatesViaEthFeeHistory,\n fetchLegacyGasPriceEstimates,\n fetchLegacyGasPriceEstimatesUrl: this.legacyAPIEndpoint.replace(\n '<chain_id>',\n `${chainId}`,\n ),\n fetchEthGasPriceEstimate,\n calculateTimeEstimate,\n clientId: this.clientId,\n ethQuery: this.ethQuery,\n });\n\n if (shouldUpdateState) {\n this.update((state) => {\n state.gasFeeEstimates = gasFeeCalculations.gasFeeEstimates;\n state.estimatedGasFeeTimeBounds =\n gasFeeCalculations.estimatedGasFeeTimeBounds;\n state.gasEstimateType = gasFeeCalculations.gasEstimateType;\n });\n }\n\n return gasFeeCalculations;\n }\n\n /**\n * Remove the poll token, and stop polling if the set of poll tokens is empty.\n *\n * @param pollToken - The poll token to disconnect.\n */\n disconnectPoller(pollToken: string) {\n this.pollTokens.delete(pollToken);\n if (this.pollTokens.size === 0) {\n this.stopPolling();\n }\n }\n\n stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n this.pollTokens.clear();\n this.resetState();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n private _poll() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this._fetchGasFeeEstimateData());\n }, this.intervalDelay);\n }\n\n private resetState() {\n this.update(() => {\n return defaultState;\n });\n }\n\n private async getEIP1559Compatibility() {\n const currentNetworkIsEIP1559Compatible =\n await this.getCurrentNetworkEIP1559Compatibility();\n const currentAccountIsEIP1559Compatible =\n this.getCurrentAccountEIP1559Compatibility?.() ?? true;\n\n return (\n currentNetworkIsEIP1559Compatible && currentAccountIsEIP1559Compatible\n );\n }\n\n getTimeEstimate(\n maxPriorityFeePerGas: string,\n maxFeePerGas: string,\n ): EstimatedGasFeeTimeBounds | Record<string, never> {\n if (\n !this.state.gasFeeEstimates ||\n this.state.gasEstimateType !== GAS_ESTIMATE_TYPES.FEE_MARKET\n ) {\n return {};\n }\n return calculateTimeEstimate(\n maxPriorityFeePerGas,\n maxFeePerGas,\n this.state.gasFeeEstimates,\n );\n }\n}\n\nexport default GasFeeController;\n"]}
@@ -0,0 +1,40 @@
1
+ import { EstimatedGasFeeTimeBounds, EthGasPriceEstimate, GasFeeEstimates, GasFeeState as GasFeeCalculations, LegacyGasPriceEstimate } from './GasFeeController';
2
+ /**
3
+ * Obtains a set of max base and priority fee estimates along with time estimates so that we
4
+ * can present them to users when they are sending transactions or making swaps.
5
+ *
6
+ * @param args - The arguments.
7
+ * @param args.isEIP1559Compatible - Governs whether or not we can use an EIP-1559-only method to
8
+ * produce estimates.
9
+ * @param args.isLegacyGasAPICompatible - Governs whether or not we can use a non-EIP-1559 method to
10
+ * produce estimates (for instance, testnets do not support estimates altogether).
11
+ * @param args.fetchGasEstimates - A function that fetches gas estimates using an EIP-1559-specific
12
+ * API.
13
+ * @param args.fetchGasEstimatesUrl - The URL for the API we can use to obtain EIP-1559-specific
14
+ * estimates.
15
+ * @param args.fetchGasEstimatesViaEthFeeHistory - A function that fetches gas estimates using
16
+ * `eth_feeHistory` (an EIP-1559 feature).
17
+ * @param args.fetchLegacyGasPriceEstimates - A function that fetches gas estimates using an
18
+ * non-EIP-1559-specific API.
19
+ * @param args.fetchLegacyGasPriceEstimatesUrl - The URL for the API we can use to obtain
20
+ * non-EIP-1559-specific estimates.
21
+ * @param args.fetchEthGasPriceEstimate - A function that fetches gas estimates using
22
+ * `eth_gasPrice`.
23
+ * @param args.calculateTimeEstimate - A function that determine time estimate bounds.
24
+ * @param args.clientId - An identifier that an API can use to know who is asking for estimates.
25
+ * @param args.ethQuery - An EthQuery instance we can use to talk to Ethereum directly.
26
+ * @returns The gas fee calculations.
27
+ */
28
+ export default function determineGasFeeCalculations({ isEIP1559Compatible, isLegacyGasAPICompatible, fetchGasEstimates, fetchGasEstimatesUrl, fetchGasEstimatesViaEthFeeHistory, fetchLegacyGasPriceEstimates, fetchLegacyGasPriceEstimatesUrl, fetchEthGasPriceEstimate, calculateTimeEstimate, clientId, ethQuery, }: {
29
+ isEIP1559Compatible: boolean;
30
+ isLegacyGasAPICompatible: boolean;
31
+ fetchGasEstimates: (url: string, clientId?: string) => Promise<GasFeeEstimates>;
32
+ fetchGasEstimatesUrl: string;
33
+ fetchGasEstimatesViaEthFeeHistory: (ethQuery: any) => Promise<GasFeeEstimates>;
34
+ fetchLegacyGasPriceEstimates: (url: string, clientId?: string) => Promise<LegacyGasPriceEstimate>;
35
+ fetchLegacyGasPriceEstimatesUrl: string;
36
+ fetchEthGasPriceEstimate: (ethQuery: any) => Promise<EthGasPriceEstimate>;
37
+ calculateTimeEstimate: (maxPriorityFeePerGas: string, maxFeePerGas: string, gasFeeEstimates: GasFeeEstimates) => EstimatedGasFeeTimeBounds;
38
+ clientId: string | undefined;
39
+ ethQuery: any;
40
+ }): Promise<GasFeeCalculations>;