alloy_eips/eip1559/
helpers.rs

1use crate::eip1559::{constants::GAS_LIMIT_BOUND_DIVISOR, BaseFeeParams};
2
3/// Return type of EIP1155 gas fee estimator.
4///
5/// Contains EIP-1559 fields
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
9pub struct Eip1559Estimation {
10    /// The base fee per gas.
11    #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
12    pub max_fee_per_gas: u128,
13    /// The max priority fee per gas.
14    #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
15    pub max_priority_fee_per_gas: u128,
16}
17
18/// Calculate the base fee for the next block based on the EIP-1559 specification.
19///
20/// This function calculates the base fee for the next block according to the rules defined in the
21/// EIP-1559. EIP-1559 introduces a new transaction pricing mechanism that includes a
22/// fixed-per-block network fee that is burned and dynamically adjusts block sizes to handles
23/// transient congestion.
24///
25/// For each block, the base fee per gas is determined by the gas used in the parent block and the
26/// target gas (the block gas limit divided by the elasticity multiplier). The algorithm increases
27/// the base fee when blocks are congested and decreases it when they are under the target gas
28/// usage. The base fee per gas is always burned.
29///
30/// Parameters:
31/// - `gas_used`: The gas used in the current block.
32/// - `gas_limit`: The gas limit of the current block.
33/// - `base_fee`: The current base fee per gas.
34/// - `base_fee_params`: Base fee parameters such as elasticity multiplier and max change
35///   denominator.
36///
37/// Returns:
38/// The calculated base fee for the next block as a `u64`.
39///
40/// For more information, refer to the [EIP-1559 spec](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md).
41pub fn calc_next_block_base_fee(
42    gas_used: u64,
43    gas_limit: u64,
44    base_fee: u64,
45    base_fee_params: BaseFeeParams,
46) -> u64 {
47    // Calculate the target gas by dividing the gas limit by the elasticity multiplier.
48    let gas_target = gas_limit / base_fee_params.elasticity_multiplier as u64;
49
50    match gas_used.cmp(&gas_target) {
51        // If the gas used in the current block is equal to the gas target, the base fee remains the
52        // same (no increase).
53        core::cmp::Ordering::Equal => base_fee,
54        // If the gas used in the current block is greater than the gas target, calculate a new
55        // increased base fee.
56        core::cmp::Ordering::Greater => {
57            // Calculate the increase in base fee based on the formula defined by EIP-1559.
58            base_fee
59                + (core::cmp::max(
60                    // Ensure a minimum increase of 1.
61                    1,
62                    base_fee as u128 * (gas_used - gas_target) as u128
63                        / (gas_target as u128 * base_fee_params.max_change_denominator),
64                ) as u64)
65        }
66        // If the gas used in the current block is less than the gas target, calculate a new
67        // decreased base fee.
68        core::cmp::Ordering::Less => {
69            // Calculate the decrease in base fee based on the formula defined by EIP-1559.
70            base_fee.saturating_sub(
71                (base_fee as u128 * (gas_target - gas_used) as u128
72                    / (gas_target as u128 * base_fee_params.max_change_denominator))
73                    as u64,
74            )
75        }
76    }
77}
78
79/// Calculate the gas limit for the next block based on parent and desired gas limits.
80/// Ref: <https://github.com/ethereum/go-ethereum/blob/88cbfab332c96edfbe99d161d9df6a40721bd786/core/block_validator.go#L166>
81pub fn calculate_block_gas_limit(parent_gas_limit: u64, desired_gas_limit: u64) -> u64 {
82    let delta = (parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR).saturating_sub(1);
83    let min_gas_limit = parent_gas_limit - delta;
84    let max_gas_limit = parent_gas_limit + delta;
85    desired_gas_limit.clamp(min_gas_limit, max_gas_limit)
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::eip1559::constants::{MIN_PROTOCOL_BASE_FEE, MIN_PROTOCOL_BASE_FEE_U256};
92
93    #[test]
94    fn min_protocol_sanity() {
95        assert_eq!(MIN_PROTOCOL_BASE_FEE_U256.to::<u64>(), MIN_PROTOCOL_BASE_FEE);
96    }
97
98    #[test]
99    fn calculate_base_fee_success() {
100        let base_fee = [
101            1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
102            1, 2,
103        ];
104        let gas_used = [
105            10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
106            10000000,
107        ];
108        let gas_limit = [
109            10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
110            18000000, 18000000,
111        ];
112        let next_base_fee = [
113            1125000000, 1083333333, 1053571428, 1179939062, 1116028649, 918084097, 1063811730, 1,
114            2, 3,
115        ];
116
117        for i in 0..base_fee.len() {
118            assert_eq!(
119                next_base_fee[i],
120                calc_next_block_base_fee(
121                    gas_used[i],
122                    gas_limit[i],
123                    base_fee[i],
124                    BaseFeeParams::ethereum(),
125                )
126            );
127        }
128    }
129
130    #[test]
131    fn calculate_optimism_sepolia_base_fee_success() {
132        let base_fee = [
133            1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
134            1, 2,
135        ];
136        let gas_used = [
137            10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
138            10000000,
139        ];
140        let gas_limit = [
141            10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
142            18000000, 18000000,
143        ];
144        let next_base_fee = [
145            1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
146            2, 3,
147        ];
148
149        for i in 0..base_fee.len() {
150            assert_eq!(
151                next_base_fee[i],
152                calc_next_block_base_fee(
153                    gas_used[i],
154                    gas_limit[i],
155                    base_fee[i],
156                    BaseFeeParams::optimism_sepolia(),
157                )
158            );
159        }
160    }
161
162    #[test]
163    fn calculate_optimism_base_fee_success() {
164        let base_fee = [
165            1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
166            1, 2,
167        ];
168        let gas_used = [
169            10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
170            10000000,
171        ];
172        let gas_limit = [
173            10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
174            18000000, 18000000,
175        ];
176        let next_base_fee = [
177            1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
178            2, 3,
179        ];
180
181        for i in 0..base_fee.len() {
182            assert_eq!(
183                next_base_fee[i],
184                calc_next_block_base_fee(
185                    gas_used[i],
186                    gas_limit[i],
187                    base_fee[i],
188                    BaseFeeParams::optimism(),
189                )
190            );
191        }
192    }
193
194    #[test]
195    fn calculate_optimism_canyon_base_fee_success() {
196        let base_fee = [
197            1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
198            1, 2,
199        ];
200        let gas_used = [
201            10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
202            10000000,
203        ];
204        let gas_limit = [
205            10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
206            18000000, 18000000,
207        ];
208        let next_base_fee = [
209            1020000009, 1016000000, 1013142859, 1091550909, 1073187043, 1045042012, 1059031864, 1,
210            2, 3,
211        ];
212
213        for i in 0..base_fee.len() {
214            assert_eq!(
215                next_base_fee[i],
216                calc_next_block_base_fee(
217                    gas_used[i],
218                    gas_limit[i],
219                    base_fee[i],
220                    BaseFeeParams::optimism_canyon(),
221                )
222            );
223        }
224    }
225
226    #[test]
227    fn calculate_base_sepolia_base_fee_success() {
228        let base_fee = [
229            1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
230            1, 2,
231        ];
232        let gas_used = [
233            10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
234            10000000,
235        ];
236        let gas_limit = [
237            10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
238            18000000, 18000000,
239        ];
240        let next_base_fee = [
241            1180000000, 1146666666, 1122857142, 1244299375, 1189416692, 1028254188, 1144836295, 1,
242            2, 3,
243        ];
244
245        for i in 0..base_fee.len() {
246            assert_eq!(
247                next_base_fee[i],
248                calc_next_block_base_fee(
249                    gas_used[i],
250                    gas_limit[i],
251                    base_fee[i],
252                    BaseFeeParams::base_sepolia(),
253                )
254            );
255        }
256    }
257}