1use crate::eip1559::{constants::GAS_LIMIT_BOUND_DIVISOR, BaseFeeParams};
2
3#[inline]
14pub fn calc_effective_gas_price(
15 max_fee_per_gas: u128,
16 max_priority_fee_per_gas: u128,
17 base_fee: Option<u64>,
18) -> u128 {
19 base_fee.map_or(max_fee_per_gas, |base_fee| {
20 let tip = max_fee_per_gas.saturating_sub(base_fee as u128);
23 if tip > max_priority_fee_per_gas {
24 max_priority_fee_per_gas + base_fee as u128
25 } else {
26 max_fee_per_gas
28 }
29 })
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
38pub struct Eip1559Estimation {
39 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
41 pub max_fee_per_gas: u128,
42 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
44 pub max_priority_fee_per_gas: u128,
45}
46
47impl Eip1559Estimation {
48 pub const fn scale_by_pct(&mut self, pct: u64) {
58 self.max_fee_per_gas = self.max_fee_per_gas * (100 + pct as u128) / 100;
59 self.max_priority_fee_per_gas = self.max_priority_fee_per_gas * (100 + pct as u128) / 100;
60 }
61
62 pub const fn scaled_by_pct(mut self, pct: u64) -> Self {
64 self.scale_by_pct(pct);
65 self
66 }
67}
68
69pub fn calc_next_block_base_fee(
93 gas_used: u64,
94 gas_limit: u64,
95 base_fee: u64,
96 base_fee_params: BaseFeeParams,
97) -> u64 {
98 let elasticity = base_fee_params.elasticity_multiplier;
99 let max_change_denominator = base_fee_params.max_change_denominator;
100
101 if elasticity == 0 || max_change_denominator == 0 {
107 return base_fee;
108 }
109
110 let gas_target = (gas_limit as u128 / elasticity) as u64;
112
113 if gas_target == 0 {
114 return base_fee;
115 }
116
117 match gas_used.cmp(&gas_target) {
118 core::cmp::Ordering::Equal => base_fee,
121 core::cmp::Ordering::Greater => {
124 base_fee
126 + (core::cmp::max(
127 1,
129 base_fee as u128 * (gas_used - gas_target) as u128
130 / (gas_target as u128 * base_fee_params.max_change_denominator),
131 ) as u64)
132 }
133 core::cmp::Ordering::Less => {
136 base_fee.saturating_sub(
138 (base_fee as u128 * (gas_target - gas_used) as u128
139 / (gas_target as u128 * base_fee_params.max_change_denominator))
140 as u64,
141 )
142 }
143 }
144}
145
146pub fn calculate_block_gas_limit(parent_gas_limit: u64, desired_gas_limit: u64) -> u64 {
149 calculate_block_gas_limit_with_bound_divisor(
150 parent_gas_limit,
151 desired_gas_limit,
152 GAS_LIMIT_BOUND_DIVISOR,
153 )
154}
155
156pub fn calculate_block_gas_limit_with_bound_divisor(
163 parent_gas_limit: u64,
164 desired_gas_limit: u64,
165 gas_limit_bound_divisor: u64,
166) -> u64 {
167 assert!(gas_limit_bound_divisor != 0, "gas limit bound divisor must be non-zero");
168
169 let delta = (parent_gas_limit / gas_limit_bound_divisor).saturating_sub(1);
170 let min_gas_limit = parent_gas_limit.saturating_sub(delta);
171 let max_gas_limit = parent_gas_limit.saturating_add(delta);
172 desired_gas_limit.clamp(min_gas_limit, max_gas_limit)
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::eip1559::constants::{MIN_PROTOCOL_BASE_FEE, MIN_PROTOCOL_BASE_FEE_U256};
179
180 #[test]
181 fn min_protocol_sanity() {
182 assert_eq!(MIN_PROTOCOL_BASE_FEE_U256.to::<u64>(), MIN_PROTOCOL_BASE_FEE);
183 }
184
185 #[test]
186 fn calculate_block_gas_limit_bounds_desired_limit() {
187 let parent_gas_limit = 30_000_000;
188 let min_gas_limit = 29_970_705;
189 let max_gas_limit = 30_029_295;
190
191 assert_eq!(calculate_block_gas_limit(parent_gas_limit, 20_000_000), min_gas_limit);
192 assert_eq!(calculate_block_gas_limit(parent_gas_limit, 30_000_000), 30_000_000);
193 assert_eq!(calculate_block_gas_limit(parent_gas_limit, 40_000_000), max_gas_limit);
194 }
195
196 #[test]
197 fn calculate_block_gas_limit_uses_custom_bound_divisor() {
198 assert_eq!(calculate_block_gas_limit_with_bound_divisor(1_000, 2_000, 10), 1_099);
199 }
200
201 #[test]
202 #[should_panic(expected = "gas limit bound divisor must be non-zero")]
203 fn calculate_block_gas_limit_rejects_zero_bound_divisor() {
204 calculate_block_gas_limit_with_bound_divisor(1_000, 2_000, 0);
205 }
206
207 #[test]
208 fn calculate_base_fee_success() {
209 let base_fee = [
210 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
211 1, 2,
212 ];
213 let gas_used = [
214 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
215 10000000,
216 ];
217 let gas_limit = [
218 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
219 18000000, 18000000,
220 ];
221 let next_base_fee = [
222 1125000000, 1083333333, 1053571428, 1179939062, 1116028649, 918084097, 1063811730, 1,
223 2, 3,
224 ];
225
226 for i in 0..base_fee.len() {
227 assert_eq!(
228 next_base_fee[i],
229 calc_next_block_base_fee(
230 gas_used[i],
231 gas_limit[i],
232 base_fee[i],
233 BaseFeeParams::ethereum(),
234 )
235 );
236 }
237 }
238
239 #[test]
240 fn calculate_optimism_sepolia_base_fee_success() {
241 let base_fee = [
242 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
243 1, 2,
244 ];
245 let gas_used = [
246 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
247 10000000,
248 ];
249 let gas_limit = [
250 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
251 18000000, 18000000,
252 ];
253 let next_base_fee = [
254 1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
255 2, 3,
256 ];
257
258 for i in 0..base_fee.len() {
259 assert_eq!(
260 next_base_fee[i],
261 calc_next_block_base_fee(
262 gas_used[i],
263 gas_limit[i],
264 base_fee[i],
265 BaseFeeParams::optimism_sepolia(),
266 )
267 );
268 }
269 }
270
271 #[test]
272 fn calculate_optimism_base_fee_success() {
273 let base_fee = [
274 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
275 1, 2,
276 ];
277 let gas_used = [
278 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
279 10000000,
280 ];
281 let gas_limit = [
282 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
283 18000000, 18000000,
284 ];
285 let next_base_fee = [
286 1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
287 2, 3,
288 ];
289
290 for i in 0..base_fee.len() {
291 assert_eq!(
292 next_base_fee[i],
293 calc_next_block_base_fee(
294 gas_used[i],
295 gas_limit[i],
296 base_fee[i],
297 BaseFeeParams::optimism(),
298 )
299 );
300 }
301 }
302
303 #[test]
304 fn calculate_optimism_canyon_base_fee_success() {
305 let base_fee = [
306 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
307 1, 2,
308 ];
309 let gas_used = [
310 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
311 10000000,
312 ];
313 let gas_limit = [
314 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
315 18000000, 18000000,
316 ];
317 let next_base_fee = [
318 1020000009, 1016000000, 1013142859, 1091550909, 1073187043, 1045042012, 1059031864, 1,
319 2, 3,
320 ];
321
322 for i in 0..base_fee.len() {
323 assert_eq!(
324 next_base_fee[i],
325 calc_next_block_base_fee(
326 gas_used[i],
327 gas_limit[i],
328 base_fee[i],
329 BaseFeeParams::optimism_canyon(),
330 )
331 );
332 }
333 }
334
335 #[test]
336 fn calculate_base_sepolia_base_fee_success() {
337 let base_fee = [
338 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
339 1, 2,
340 ];
341 let gas_used = [
342 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
343 10000000,
344 ];
345 let gas_limit = [
346 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
347 18000000, 18000000,
348 ];
349 let next_base_fee = [
350 1180000000, 1146666666, 1122857142, 1244299375, 1189416692, 1028254188, 1144836295, 1,
351 2, 3,
352 ];
353
354 for i in 0..base_fee.len() {
355 assert_eq!(
356 next_base_fee[i],
357 calc_next_block_base_fee(
358 gas_used[i],
359 gas_limit[i],
360 base_fee[i],
361 BaseFeeParams::base_sepolia(),
362 )
363 );
364 }
365 }
366
367 #[test]
368 fn next_base_fee_no_panic_zero_elasticity() {
369 let p = BaseFeeParams::new(8, 0);
370 assert_eq!(calc_next_block_base_fee(1, 30_000_000, 1_000_000_000, p), 1_000_000_000);
371 }
372
373 #[test]
374 fn next_base_fee_no_panic_zero_denominator() {
375 let p = BaseFeeParams::new(0, 2);
376 assert_eq!(
377 calc_next_block_base_fee(15_000_000, 30_000_000, 1_000_000_000, p),
378 1_000_000_000
379 );
380 }
381
382 #[test]
383 fn next_base_fee_no_panic_gas_limit_below_elasticity() {
384 let p = BaseFeeParams::ethereum();
385 assert_eq!(calc_next_block_base_fee(1, 1, 1_000_000_000, p), 1_000_000_000);
387 }
388}