Skip to content

Commit

Permalink
Remove non-critical gas estimate fallback data (#712)
Browse files Browse the repository at this point in the history
If a request to the Gas API fails for any reason, we attempt to hit
Infura directly and compute the same pieces of data that the Gas API
computes. However, considering that this data supports the new gas fee
UI design in the extension, it is not necessary for us to emulate the
API 100%. We only need data that is critical to allow users to see and
select fees for different priority levels in order to make transactions.
Everything else — a snapshot of recent base and priority fees, whether
the base and priority fees have risen or fallen recently, and how busy
the network is — is not important.

This commit removes these extra computations so as to further limit the
amount of requests that are made to Infura.
  • Loading branch information
mcmire authored Mar 14, 2022
1 parent b3edff2 commit dd48bba
Show file tree
Hide file tree
Showing 17 changed files with 75 additions and 804 deletions.
17 changes: 16 additions & 1 deletion src/gas/GasFeeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ export type Eip1559GasFee = {
* level of the network, with 0 meaning not congested and 1 meaning extremely congested
*/

export type GasFeeEstimates = {
export type GasFeeEstimates = SourcedGasFeeEstimates | FallbackGasFeeEstimates;

type SourcedGasFeeEstimates = {
low: Eip1559GasFee;
medium: Eip1559GasFee;
high: Eip1559GasFee;
Expand All @@ -135,6 +137,19 @@ export type GasFeeEstimates = {
networkCongestion: number;
};

type FallbackGasFeeEstimates = {
low: Eip1559GasFee;
medium: Eip1559GasFee;
high: Eip1559GasFee;
estimatedBaseFee: string;
historicalBaseFeeRange: null;
baseFeeTrend: null;
latestPriorityFeeRange: null;
historicalPriorityFeeRange: null;
priorityFeeTrend: null;
networkCongestion: null;
};

const metadata = {
gasFeeEstimates: { persist: true, anonymous: false },
estimatedGasFeeTimeBounds: { persist: true, anonymous: false },
Expand Down
4 changes: 2 additions & 2 deletions src/gas/fetchBlockFeeHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export type EthFeeHistoryResponse = {
* used for the block, indexed by those percentiles. (See docs for {@link fetchBlockFeeHistory} for more
* on how this works.)
*/
export type ExistingFeeHistoryBlock<Percentile extends number> = {
type ExistingFeeHistoryBlock<Percentile extends number> = {
number: BN;
baseFeePerGas: BN;
gasUsedRatio: number;
Expand All @@ -64,7 +64,7 @@ export type ExistingFeeHistoryBlock<Percentile extends number> = {
* @property number - The number of the block, as a BN.
* @property baseFeePerGas - The estimated base fee per gas for the block in WEI, as a BN.
*/
export type NextFeeHistoryBlock = {
type NextFeeHistoryBlock = {
number: BN;
baseFeePerGas: BN;
};
Expand Down
165 changes: 30 additions & 135 deletions src/gas/fetchGasEstimatesViaEthFeeHistory.test.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,22 @@
import { BN } from 'ethereumjs-util';
import { mocked } from 'ts-jest/utils';
import { when } from 'jest-when';
import BlockFeeHistoryDatasetFetcher from './fetchGasEstimatesViaEthFeeHistory/BlockFeeHistoryDatasetFetcher';
import fetchBlockFeeHistory from './fetchBlockFeeHistory';
import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels';
import calculateBaseFeeRange from './fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeRange';
import calculateBaseFeeTrend from './fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeTrend';
import calculatePriorityFeeRange from './fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeRange';
import calculatePriorityFeeTrend from './fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeTrend';
import calculateNetworkCongestion from './fetchGasEstimatesViaEthFeeHistory/calculateNetworkCongestion';
import fetchLatestBlock from './fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock';
import fetchGasEstimatesViaEthFeeHistory from './fetchGasEstimatesViaEthFeeHistory';

jest.mock('./fetchGasEstimatesViaEthFeeHistory/BlockFeeHistoryDatasetFetcher');
jest.mock('./fetchBlockFeeHistory');
jest.mock(
'./fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels',
);
jest.mock('./fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeRange');
jest.mock('./fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeTrend');
jest.mock('./fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeRange');
jest.mock('./fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeTrend');
jest.mock('./fetchGasEstimatesViaEthFeeHistory/calculateNetworkCongestion');
jest.mock('./fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock');

const mockedBlockFeeHistoryDatasetFetcherConstructor = mocked(
BlockFeeHistoryDatasetFetcher,
true,
);
const mockedFetchBlockFeeHistory = mocked(fetchBlockFeeHistory, true);
const mockedCalculateGasFeeEstimatesForPriorityLevels = mocked(
calculateGasFeeEstimatesForPriorityLevels,
true,
);
const mockedCalculateBaseFeeRange = mocked(calculateBaseFeeRange, true);
const mockedCalculateBaseFeeTrend = mocked(calculateBaseFeeTrend, true);
const mockedCalculatePriorityFeeRange = mocked(calculatePriorityFeeRange, true);
const mockedCalculatePriorityFeeTrend = mocked(calculatePriorityFeeTrend, true);
const mockedCalculateNetworkCongestion = mocked(
calculateNetworkCongestion,
true,
);
const mockedFetchLatestBlock = mocked(fetchLatestBlock, true);

describe('fetchGasEstimatesViaEthFeeHistory', () => {
Expand All @@ -50,80 +29,19 @@ describe('fetchGasEstimatesViaEthFeeHistory', () => {
getBlockByNumber: async () => latestBlock,
};

it('calculates target fees for low, medium, and high transaction priority levels, as well as the network congestion level', async () => {
const blocksByDataset = {
mediumRange: [
{
number: new BN(2),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
10: new BN('0'),
95: new BN('0'),
},
},
],
smallRange: [
{
number: new BN(3),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
10: new BN('0'),
20: new BN('0'),
30: new BN('0'),
},
},
],
tinyRange: [
{
number: new BN(4),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
50: new BN('0'),
},
},
],
tinyRangeWithNextBlock: [
{
number: new BN(5),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
50: new BN('0'),
},
},
],
latest: [
{
number: new BN(6),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
10: new BN('0'),
95: new BN('0'),
},
},
],
latestWithNextBlock: [
{
number: new BN(6),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
10: new BN('0'),
95: new BN('0'),
},
it('calculates target fees for low, medium, and high transaction priority levels', async () => {
const blocks = [
{
number: new BN(3),
baseFeePerGas: new BN(1),
gasUsedRatio: 1,
priorityFeesByPercentile: {
10: new BN('0'),
20: new BN('0'),
30: new BN('0'),
},
{
number: new BN(7),
baseFeePerGas: new BN(1),
gasUsedRatio: null,
priorityFeesByPercentile: null,
},
],
};
},
];
const levelSpecificEstimates = {
low: {
minWaitTimeEstimate: 15_000,
Expand All @@ -144,55 +62,32 @@ describe('fetchGasEstimatesViaEthFeeHistory', () => {
suggestedMaxFeePerGas: '252.94',
},
};
const historicalBaseFeeRange: [string, string] = ['100', '200'];
const baseFeeTrend = 'up';
const latestPriorityFeeRange: [string, string] = ['1', '2'];
const historicalPriorityFeeRange: [string, string] = ['2', '4'];
const priorityFeeTrend = 'down';
const networkCongestion = 0.5;

mockedFetchLatestBlock.mockResolvedValue(latestBlock);
mockedBlockFeeHistoryDatasetFetcherConstructor.prototype.forAll.mockResolvedValue(
blocksByDataset,
);
when(mockedFetchBlockFeeHistory)
.calledWith({
ethQuery,
endBlock: latestBlock.number,
numberOfBlocks: 5,
percentiles: [10, 20, 30],
})
.mockResolvedValue(blocks);

when(mockedCalculateGasFeeEstimatesForPriorityLevels)
.calledWith(blocksByDataset.smallRange)
.calledWith(blocks)
.mockReturnValue(levelSpecificEstimates);

when(mockedCalculateBaseFeeRange)
.calledWith(blocksByDataset.mediumRange)
.mockReturnValue(historicalBaseFeeRange);

when(mockedCalculateBaseFeeTrend)
.calledWith(blocksByDataset.latestWithNextBlock)
.mockReturnValue(baseFeeTrend);

when(mockedCalculatePriorityFeeRange)
.calledWith(blocksByDataset.latest)
.mockReturnValue(latestPriorityFeeRange)
.calledWith(blocksByDataset.mediumRange)
.mockReturnValue(historicalPriorityFeeRange);

when(mockedCalculatePriorityFeeTrend)
.calledWith(blocksByDataset.tinyRange)
.mockReturnValue(priorityFeeTrend);

when(mockedCalculateNetworkCongestion)
.calledWith([])
.mockReturnValue(networkCongestion);

const gasFeeEstimates = await fetchGasEstimatesViaEthFeeHistory(ethQuery);

expect(gasFeeEstimates).toStrictEqual({
...levelSpecificEstimates,
estimatedBaseFee: '100',
historicalBaseFeeRange,
baseFeeTrend,
latestPriorityFeeRange,
historicalPriorityFeeRange,
priorityFeeTrend,
networkCongestion,
historicalBaseFeeRange: null,
baseFeeTrend: null,
latestPriorityFeeRange: null,
historicalPriorityFeeRange: null,
priorityFeeTrend: null,
networkCongestion: null,
});
});
});
49 changes: 17 additions & 32 deletions src/gas/fetchGasEstimatesViaEthFeeHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,9 @@ import { fromWei } from 'ethjs-unit';
import { GWEI } from '../constants';
import { GasFeeEstimates } from './GasFeeController';
import { EthQuery } from './fetchGasEstimatesViaEthFeeHistory/types';
import BlockFeeHistoryDatasetFetcher from './fetchGasEstimatesViaEthFeeHistory/BlockFeeHistoryDatasetFetcher';
import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels';
import calculateBaseFeeRange from './fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeRange';
import calculateBaseFeeTrend from './fetchGasEstimatesViaEthFeeHistory/calculateBaseFeeTrend';
import calculatePriorityFeeRange from './fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeRange';
import calculatePriorityFeeTrend from './fetchGasEstimatesViaEthFeeHistory/calculatePriorityFeeTrend';
import calculateNetworkCongestion from './fetchGasEstimatesViaEthFeeHistory/calculateNetworkCongestion';
import fetchBlockFeeHistory from './fetchBlockFeeHistory';
import fetchLatestBlock from './fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock';
import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels';

/**
* Generates gas fee estimates based on gas fees that have been used in the recent past so that
Expand All @@ -22,6 +17,9 @@ import fetchLatestBlock from './fetchGasEstimatesViaEthFeeHistory/fetchLatestBlo
* calculate reasonable max priority and max fees for three different priority levels (higher
* priority = higher fee).
*
* Note that properties are returned for other data that are normally obtained via the API; however,
* to prevent extra requests to Infura, these properties are empty.
*
* @param ethQuery - An EthQuery instance.
* @returns Base and priority fee estimates, categorized by priority level, as well as an estimate
* for the next block's base fee.
Expand All @@ -30,39 +28,26 @@ export default async function fetchGasEstimatesViaEthFeeHistory(
ethQuery: EthQuery,
): Promise<GasFeeEstimates> {
const latestBlock = await fetchLatestBlock(ethQuery);
const fetcher = new BlockFeeHistoryDatasetFetcher({
const blocks = await fetchBlockFeeHistory({
ethQuery,
endBlockNumber: latestBlock.number,
endBlock: latestBlock.number,
numberOfBlocks: 5,
percentiles: [10, 20, 30],
});
const blocksByDataset = await fetcher.forAll();
const estimatedBaseFee = fromWei(latestBlock.baseFeePerGas, GWEI);

const levelSpecificEstimates = calculateGasFeeEstimatesForPriorityLevels(
blocksByDataset.smallRange,
);
const estimatedBaseFee = fromWei(latestBlock.baseFeePerGas, GWEI);
const historicalBaseFeeRange = calculateBaseFeeRange(
blocksByDataset.mediumRange,
);
const baseFeeTrend = calculateBaseFeeTrend(
blocksByDataset.latestWithNextBlock,
);
const latestPriorityFeeRange = calculatePriorityFeeRange(
blocksByDataset.latest,
);
const historicalPriorityFeeRange = calculatePriorityFeeRange(
blocksByDataset.mediumRange,
blocks,
);
const priorityFeeTrend = calculatePriorityFeeTrend(blocksByDataset.tinyRange);
const networkCongestion = calculateNetworkCongestion([]);

return {
...levelSpecificEstimates,
estimatedBaseFee,
historicalBaseFeeRange,
baseFeeTrend,
latestPriorityFeeRange,
historicalPriorityFeeRange,
priorityFeeTrend,
networkCongestion,
historicalBaseFeeRange: null,
baseFeeTrend: null,
latestPriorityFeeRange: null,
historicalPriorityFeeRange: null,
priorityFeeTrend: null,
networkCongestion: null,
};
}
Loading

0 comments on commit dd48bba

Please sign in to comment.