Understanding Ethereum's gas fee structure is crucial for developers and users alike. The introduction of EIP-1559 fundamentally changed how transaction fees work on the network, replacing the previous auction model with a more predictable system. This guide explains how to leverage the EIP-1559 API to build user-friendly gas fee options like "slow," "average," and "fast" for your applications.
Understanding the EIP-1559 Gas Fee Formula
The current Ethereum gas fee calculation follows a specific formula:
Units of gas used × (Base fee + Priority fee)
Let's break down each component:
- Units of gas used: The amount of computational work required for a transaction. A standard ETH transfer typically consumes 21,000 units of gas
- Base fee: The fundamental gas cost measured in gwei (1 gwei = 0.000000001 ETH), automatically adjusted by the network
- Priority fee: Also measured in gwei, this optional tip incentivizes miners to prioritize your transaction
This new mechanism creates a more stable fee market compared to the previous bidding system, where gas prices could fluctuate wildly due to speculative bidding.
How the Base Fee Mechanism Works
The base fee represents the most significant change introduced by EIP-1559. Unlike the previous system where users bid against each other, the base fee is algorithmically determined based on network congestion from the previous block.
The base fee automatically adjusts according to these principles:
- If the previous block exceeded the gas target, the base fee increases
- If the previous block had lower usage, the base fee decreases
- This adjustment happens automatically at the protocol level
This dynamic adjustment mechanism ensures that base fees can be accurately predicted, providing much-needed stability to Ethereum's fee market.
Calculating the Base Fee Programmatically
The base fee calculation follows a specific algorithm implemented in Ethereum clients. Here's a Python implementation that demonstrates this calculation:
from web3 import Web3
eth_json_rpc_endpoint = "YOUR_ETHEREUM_NODE_ENDPOINT"
ElasticityMultiplier = 2 # Maximum block size multiplier under EIP-1559
BaseFeeChangeDenominator = 8 # Base fee change parameter
def calc_base_fee_of_next_block(parent_block):
parent_gas_target = parent_block.gasLimit // ElasticityMultiplier
if parent_block.gasUsed == parent_gas_target:
return parent_block.baseFeePerGas
if parent_block.gasUsed > parent_gas_target:
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:
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}")This code connects to an Ethereum node, retrieves the latest block information, and calculates what the base fee will be for the subsequent block.
Determining Priority Fees for Transaction Speed
While base fees are algorithmically determined, priority fees (tips) remain user-defined. To estimate appropriate priority fees, you need to analyze historical data to understand what tips others are paying for timely transaction inclusion.
Using the eth_feeHistory API
The eth_feeHistory API provides essential data for estimating priority fees. This method returns historical gas information that helps determine appropriate tip levels:
from web3 import Web3
eth_json_rpc_endpoint = "YOUR_ETHEREUM_NODE_ENDPOINT"
ethClient = Web3(Web3.HTTPProvider(eth_json_rpc_endpoint))
fee_history = ethClient.eth.fee_history(4, "pending", [25, 50, 75])
print(fee_history)This call requests fee history starting from the pending block and looks backward 4 blocks, returning the 25th, 50th, and 75th percentiles of priority fees for each block.
Processing the Fee History Data
The raw data from eth_feeHistory requires processing to become truly useful. Here's a formatting function that structures the data for easier analysis:
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 blocksThis formatter organizes the raw data into a more readable structure, grouping information by block number with corresponding base fees, gas usage ratios, and priority fee percentiles.
Calculating Priority Fee Estimates
With properly formatted data, you can now calculate average priority fees for different speed options:
blocks = format_fee_history(ethClient.eth.fee_history(4, "latest", [25, 50, 75]), False)
low_priority_fees = [b['priorityFeePerGas'][0] for b in blocks]
mid_priority_fees = [b['priorityFeePerGas'][1] for b in blocks]
high_priority_fees = [b['priorityFeePerGas'][2] for b in blocks]
low_average = sum(low_priority_fees) / len(low_priority_fees)
mid_average = sum(mid_priority_fees) / len(mid_priority_fees)
high_average = sum(high_priority_fees) / len(high_priority_fees)
print(f"Slow: {low_average} gwei | Average: {mid_average} gwei | Fast: {high_average} gwei")This approach uses a simple averaging method across four historical blocks, which provides a reasonable estimate for current network conditions. The three averages correspond to different confirmation speed expectations:
- Slow (25th percentile): Lower cost, but potentially slower confirmation
- Average (50th percentile): Balanced cost and confirmation time
- Fast (75th percentile): Higher cost for quicker confirmation
Customizing Your Gas Estimation Strategy
While the averaging method described above works well for many applications, you might want to customize your estimation strategy based on specific requirements:
- Increase historical data points: Using 20 blocks instead of 4 provides a broader perspective on network conditions
- Weight recent blocks more heavily: Recent blocks might better reflect current network congestion
- Consider only successful transactions: Filtering for only included transactions might provide better estimates
- Adapt to different time periods: Network activity varies throughout the day and week
👉 Explore more strategies for optimizing your gas fee estimation algorithms and providing the best user experience.
Implementing Gas Options in Your Application
When implementing gas options in your dApp or wallet, consider these best practices:
- Clear labeling: Use intuitive terms like "Slow," "Standard," and "Fast" that users understand
- Time estimates: Provide expected confirmation times for each option when possible
- Cost transparency: Show the total estimated cost in ETH and USD equivalents
- Custom options: Allow advanced users to manually adjust fees
- Network status indicators: Show when the network is congested and fees are generally higher
Frequently Asked Questions
What is EIP-1559 and how did it change Ethereum's fee structure?
EIP-1559 introduced a base fee mechanism that automatically adjusts based on network demand, replacing the previous auction system. This created more predictable gas fees and introduced a token burning mechanism that reduces ETH supply over time.
How often does the base fee change?
The base fee adjusts with every block, which occurs approximately every 12 seconds on Ethereum. The change depends on how full the previous block was compared to the network's target capacity.
What's the difference between base fee and priority fee?
The base fee is burned and required for all transactions, while the priority fee (tip) goes to miners and incentivizes them to include your transaction. Users can set the priority fee to zero, but this may result in slower confirmation times.
How many historical blocks should I use for fee estimation?
Most implementations use 4-10 historical blocks for estimation, which represents about 1-2 minutes of network activity. Using more blocks provides a broader perspective but may not reflect recent congestion changes.
Can I set a zero priority fee?
Technically yes, but transactions with zero priority fee may take much longer to confirm, especially during network congestion. Miners typically prioritize transactions with higher tips.
How accurate are these gas estimation methods?
While these methods provide good estimates, they can't guarantee precise predictions due to the dynamic nature of the Ethereum network. Sudden spikes in demand can quickly change fee requirements.
Conclusion
Building effective gas fee options with EIP-1559 API requires understanding both the base fee mechanism and priority fee estimation. By implementing the techniques described in this guide, you can provide users with clear choices that balance cost and confirmation speed according to their needs.
The key components include calculating the predictable base fee, analyzing historical priority fee data, and creating averaged estimates for different confirmation speeds. Remember that these are estimation techniques—actual network conditions may vary, so providing users with clear information about potential variance is important for a good user experience.
As Ethereum continues to evolve, staying updated on network changes and optimizing your fee estimation algorithms will ensure your application provides the best possible experience for users managing their transaction costs.