Before EIP-1559, the gas fee on ETH used a simple auction model, and the transactions of the highest bidder were verified first. Such a calculation model will cause gas fees to fluctuate wildly due to human factors (bidding). EIP-1559 is designed to solve the problem of unpredictable and volatile gas fees.
Many apps like to give users the option to set their own gas bids, including “slow,” “average,” and “fast” options. In this article, we’ll look at how to build these options using EIP-1559 API.
First, the base fee is based on the base fee of the previous block. The calculation is automatically completed by the eth node, which is essentially different from the previous bidding mode. Roughly speaking, if the transaction volume of the previous block was larger, the base fee of the current block will increase, and vice versa. Such dynamic adjustments. In other words, this basic fee can be accurately calculated.
EIP-1559 source codeThe above code is based on golang, we convert it into a simple python code:
from web3 import Web3 eth_json_rpc_endpoint = "https://services.tokenview.io/vipapi/nodeservice/eth?apikey=xxx" ElasticityMultiplier = 2 # EIP-1559 The block size has been expanded, the maximum multiple is 2 BaseFeeChangeDenominator = 8 # The amount the base fee can change between blocks def calc_base_fee_of_next_block(parent_block): parent_gas_target = parent_block.gasLimit // ElasticityMultiplier print('parent_gas_target',parent_gas_target) print('parent_block.gasUsed',parent_block.gasUsed) print('parent_block.baseFeePerGas',parent_block.baseFeePerGas) if parent_block.gasUsed == parent_gas_target: # parent block's gasUsed is the same as the target, baseFee remains unchanged return parent_block.baseFeePerGas if parent_block.gasUsed > parent_gas_target: # parent block uses gas greater than the target value, baseFee increase gas_used_delta = parent_block.gasUsed - parent_gas_target x = parent_block.baseFeePerGas * gas_used_delta y = x // parent_gas_target base_fee_delta = max( y // BaseFeeChangeDenominator, 1 ) return parent_block.baseFeePerGas + base_fee_delta else: # the gas used by the parent block is less than the target value, baseFee reduce gas_used_delta = parent_gas_target - parent_block.gasUsed x = parent_block.baseFeePerGas * gas_used_delta y = x // parent_gas_target base_fee_delta = y // BaseFeeChangeDenominator return max( parent_block.baseFeePerGas - base_fee_delta, 0 ) def main(): ethClient = Web3(Web3.HTTPProvider(eth_json_rpc_endpoint)) block_number = ethClient.eth.block_number block = ethClient.eth.get_block(block_number) base_fee_of_next_block = calc_base_fee_of_next_block(block) print(f"Base fee for block {block_number + 1} will be {base_fee_of_next_block}")
Unlike base fees, priority fees are artificially set values. For transactions that need to be executed first in the same block, a higher tip is required. To predict priority fees, you have to scan blocks over time to see what fees others are using.
We will use this API:eth_feeHistory
from web3 import Web3 eth_json_rpc_endpoint = "https://services.tokenview.io/vipapi/nodeservice/eth?apikey=xxx" ethClient = Web3(Web3.HTTPProvider(eth_json_rpc_endpoint)) print(ethClient.eth.fee_history(4,"pending", [25, 50, 75]))
The above call means, “Give me the fee history information starting from the pending block and looking backward 4 blocks. For each block also give me the 25th, 50th, and 75th percentiles of priority fees for transactions in the block”. The raw result looks like this:
{ "oldestBlock": 17913327, "reward": [ [39519672,100000000,2000000000], [100000000,1000000000,3000000000], [100000000,365064718,1000000000], 100000000,570000000,3000000000] ], "baseFeePerGas": [ 21121728416, 21666906452, 20742307151, 19782866894, 17762883032 ], "gasUsedRatio": [ 0.6032449666666667, 0.3293066333333333, 0.31497906666666664, 0.09156903333333333 ] }
To make our calculations easier, let’s write a formatting method that groups the above results into chunks:
def format_fee_history(result, include_pending): block_num = result['oldestBlock'] index = 0 blocks = [] historical_blocks = len(result['reward']) while block_num < result['oldestBlock'] + historical_blocks: blocks.append({ 'number': block_num, 'baseFeePerGas': float(result['baseFeePerGas'][index]), 'gasUsedRatio': float(result['gasUsedRatio'][index]), 'priorityFeePerGas': [float(x) for x in result['reward'][index]], }) block_num += 1 index += 1 if include_pending: blocks.append({ 'number': 'pending', 'baseFeePerGas': float(result['baseFeePerGas'][historical_blocks]), 'gasUsedRatio': float('nan'), 'priorityFeePerGas': [], }) return blocks
Next we can get the data in this format:
blocks = format_fee_history(ethClient.eth.fee_history(4,"latest", [25, 50, 75]),False) print(blocks) ->> [ { number: 17913335, baseFeePerGas: 315777006840, gasUsedRatio: 0.9922326809477219, priorityFeePerGas: [ 34222993160, 34222993160, 63222993160 ] }, { number: 17913336, baseFeePerGas: 354635947504, gasUsedRatio: 0.22772779167798343, priorityFeePerGas: [ 20000000000, 38044555767, 38364052496 ] }, { number: 17913337, baseFeePerGas: 330496570085, gasUsedRatio: 0.8876034775653597, priorityFeePerGas: [ 9503429915, 19503429915, 36503429915 ] }, { number: 17913338, baseFeePerGas: 362521975057, gasUsedRatio: 0.9909446241177369, priorityFeePerGas: [ 18478024943, 37478024943, 81478024943 ] } ]
Readability is greatly improved! Among them, baseFeePerGas and gasUsedRatio are used to calculate the base fee. We only focus on priorityFeePerGas now.
Continuing with our requirements, to estimate the priority fee we need to determine two values:
i.How much historical data should be used to estimate
ii.How much priority do you want your transactions to have?
For i , we set the value to 4 blocks, a reasonable length of time of about a minute.
For ii, we set expectations as 25% 50% 75% corresponding to low, medium and high tips.
blocks = format_fee_history(ethClient.eth.fee_history(4,"latest", [25, 50, 75]),False) low_percential_priority_fees = [b['priorityFeePerGas'][0] for b in blocks] mid_percential_priority_fees = [b['priorityFeePerGas'][1] for b in blocks] high_percential_priority_fees = [b['priorityFeePerGas'][2] for b in blocks] low_average = sum(low_percential_priority_fees) / len(low_percential_priority_fees) mid_average = sum(mid_percential_priority_fees) / len(mid_percential_priority_fees) high_average = sum(high_percential_priority_fees) / len(high_percential_priority_fees) print(low_average,' | ',mid_average,' | ',high_average)
The algorithm used by Tokenview is to simply average the priorityFeePerGas of the past 4 historical blocks. Of course, if you have a better estimation method, such as increasing the number of historical blocks to 20 blocks, or only considering the cheapest successful transaction. You can build your own gas cost estimator
Thank you for reading from start to finish 🎉🎉🎉