on master help sign in
bridge/src/lib/getSuggestedGasPrice.js
110 lines 3.5 kB history raw
1import { NETWORK_TYPES } from './network';
2import { DEFAULT_GAS_PRICE_GWEI, MAX_GAS_PRICE_GWEI } from './constants';
3import Web3 from 'web3';
5// ethgasstation returns values in floating point, one order of magitude
6// more than gwei. see: https://docs.ethgasstation.info
7// we don't want to charge users more than the gas tank funds
8const minGas = gas => Math.min(gas, MAX_GAS_PRICE_GWEI);
10const feeToWei = fee => Web3.utils.toHex(Web3.utils.toWei(String(fee), 'gwei'));
12// Gwei with milligwei precision, rounded up so we never underpay
13const roundGwei = gwei => Math.ceil(gwei * 1000) / 1000;
15const weiToGwei = wei => roundGwei(wei / 1_000_000_000);
17const calculateMaxFee = (baseFee, maxPriorityFee) =>
18 feeToWei(Math.round(2 * baseFee + maxPriorityFee));
20// Convert seconds to minutes prettily
21const formatWait = wait => Math.round((wait * 100) / 60) / 100;
23export const defaultGasValues = value => ({
24 fast: {
25 price: value,
26 wait: '1',
27 maxFeePerGas: value,
28 maxPriorityFeePerGas: Math.min(1, value),
29 suggestedBaseFeePerGas: value > 2 ? value - 1 : 1,
30 },
31 average: {
32 price: value,
33 wait: '1',
34 maxFeePerGas: value,
35 maxPriorityFeePerGas: Math.min(1, value),
36 suggestedBaseFeePerGas: value > 2 ? value - 1 : 1,
37 },
38 low: {
39 price: value,
40 wait: '1',
41 maxFeePerGas: value,
42 maxPriorityFeePerGas: Math.min(1, value),
43 suggestedBaseFeePerGas: value > 2 ? value - 1 : 1,
44 },
45});
47const median = values => {
48 const sorted = [...values].sort((a, b) => a - b);
49 const mid = Math.floor(sorted.length / 2);
50 return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
51};
53// Number of recent blocks to sample, and the priority fee percentiles
54// used for the low, average and fast tiers respectively
55const FEE_HISTORY_BLOCKS = 20;
56const REWARD_PERCENTILES = [10, 50, 90];
57// Floor for the priority fee, since zero tip transactions may never be mined
58const MIN_PRIORITY_FEE_GWEI = 0.001;
60const tierFor = (baseFeeGwei, priorityFeeWei, wait) => {
61 const maxPriorityFeePerGas = Math.max(
62 weiToGwei(priorityFeeWei),
63 MIN_PRIORITY_FEE_GWEI
64 );
65 const maxFeePerGas = roundGwei(baseFeeGwei + maxPriorityFeePerGas);
66 return {
67 price: minGas(maxFeePerGas),
68 wait: formatWait(wait),
69 maxFeePerGas,
70 maxPriorityFeePerGas,
71 suggestedBaseFeePerGas: baseFeeGwei,
72 };
73};
75// Estimates fees from the connected node via eth_feeHistory, in the same
76// manner as wallets do. See: https://docs.alchemy.com/docs/how-to-build-a-gas-fee-estimator-using-eip-1559
77const getGasForNetwork = async web3 => {
78 try {
79 const history = await web3.eth.getFeeHistory(
80 FEE_HISTORY_BLOCKS,
81 'latest',
82 REWARD_PERCENTILES
83 );
85 // baseFeePerGas includes one extra entry: the base fee of the next block
86 const nextBaseFeeWei = Number(history.baseFeePerGas.at(-1));
87 const baseFeeGwei = nextBaseFeeWei / 1_000_000_000;
89 const [low, average, fast] = REWARD_PERCENTILES.map((_, i) =>
90 median(history.reward.map(rewards => Number(rewards[i])))
91 );
93 return {
94 fast: tierFor(baseFeeGwei, fast, 15),
95 average: tierFor(baseFeeGwei, average, 60),
96 low: tierFor(baseFeeGwei, low, 180),
97 };
98 } catch (e) {
99 console.warn(e);
100 return defaultGasValues(DEFAULT_GAS_PRICE_GWEI);
101 }
102};
104export default async function getSuggestedGasPrice(networkType, web3) {
105 if (networkType === NETWORK_TYPES.OFFLINE || !web3) {
106 return defaultGasValues(DEFAULT_GAS_PRICE_GWEI);
107 }
109 return getGasForNetwork(web3);