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 let delta = (parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR).saturating_sub(1);
150 let min_gas_limit = parent_gas_limit - delta;
151 let max_gas_limit = parent_gas_limit + delta;
152 desired_gas_limit.clamp(min_gas_limit, max_gas_limit)
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::eip1559::constants::{MIN_PROTOCOL_BASE_FEE, MIN_PROTOCOL_BASE_FEE_U256};
159
160 #[test]
161 fn min_protocol_sanity() {
162 assert_eq!(MIN_PROTOCOL_BASE_FEE_U256.to::<u64>(), MIN_PROTOCOL_BASE_FEE);
163 }
164
165 #[test]
166 fn calculate_base_fee_success() {
167 let base_fee = [
168 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
169 1, 2,
170 ];
171 let gas_used = [
172 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
173 10000000,
174 ];
175 let gas_limit = [
176 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
177 18000000, 18000000,
178 ];
179 let next_base_fee = [
180 1125000000, 1083333333, 1053571428, 1179939062, 1116028649, 918084097, 1063811730, 1,
181 2, 3,
182 ];
183
184 for i in 0..base_fee.len() {
185 assert_eq!(
186 next_base_fee[i],
187 calc_next_block_base_fee(
188 gas_used[i],
189 gas_limit[i],
190 base_fee[i],
191 BaseFeeParams::ethereum(),
192 )
193 );
194 }
195 }
196
197 #[test]
198 fn calculate_optimism_sepolia_base_fee_success() {
199 let base_fee = [
200 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
201 1, 2,
202 ];
203 let gas_used = [
204 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
205 10000000,
206 ];
207 let gas_limit = [
208 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
209 18000000, 18000000,
210 ];
211 let next_base_fee = [
212 1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
213 2, 3,
214 ];
215
216 for i in 0..base_fee.len() {
217 assert_eq!(
218 next_base_fee[i],
219 calc_next_block_base_fee(
220 gas_used[i],
221 gas_limit[i],
222 base_fee[i],
223 BaseFeeParams::optimism_sepolia(),
224 )
225 );
226 }
227 }
228
229 #[test]
230 fn calculate_optimism_base_fee_success() {
231 let base_fee = [
232 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
233 1, 2,
234 ];
235 let gas_used = [
236 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
237 10000000,
238 ];
239 let gas_limit = [
240 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
241 18000000, 18000000,
242 ];
243 let next_base_fee = [
244 1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
245 2, 3,
246 ];
247
248 for i in 0..base_fee.len() {
249 assert_eq!(
250 next_base_fee[i],
251 calc_next_block_base_fee(
252 gas_used[i],
253 gas_limit[i],
254 base_fee[i],
255 BaseFeeParams::optimism(),
256 )
257 );
258 }
259 }
260
261 #[test]
262 fn calculate_optimism_canyon_base_fee_success() {
263 let base_fee = [
264 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
265 1, 2,
266 ];
267 let gas_used = [
268 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
269 10000000,
270 ];
271 let gas_limit = [
272 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
273 18000000, 18000000,
274 ];
275 let next_base_fee = [
276 1020000009, 1016000000, 1013142859, 1091550909, 1073187043, 1045042012, 1059031864, 1,
277 2, 3,
278 ];
279
280 for i in 0..base_fee.len() {
281 assert_eq!(
282 next_base_fee[i],
283 calc_next_block_base_fee(
284 gas_used[i],
285 gas_limit[i],
286 base_fee[i],
287 BaseFeeParams::optimism_canyon(),
288 )
289 );
290 }
291 }
292
293 #[test]
294 fn calculate_base_sepolia_base_fee_success() {
295 let base_fee = [
296 1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
297 1, 2,
298 ];
299 let gas_used = [
300 10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
301 10000000,
302 ];
303 let gas_limit = [
304 10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
305 18000000, 18000000,
306 ];
307 let next_base_fee = [
308 1180000000, 1146666666, 1122857142, 1244299375, 1189416692, 1028254188, 1144836295, 1,
309 2, 3,
310 ];
311
312 for i in 0..base_fee.len() {
313 assert_eq!(
314 next_base_fee[i],
315 calc_next_block_base_fee(
316 gas_used[i],
317 gas_limit[i],
318 base_fee[i],
319 BaseFeeParams::base_sepolia(),
320 )
321 );
322 }
323 }
324
325 #[test]
326 fn next_base_fee_no_panic_zero_elasticity() {
327 let p = BaseFeeParams::new(8, 0);
328 assert_eq!(calc_next_block_base_fee(1, 30_000_000, 1_000_000_000, p), 1_000_000_000);
329 }
330
331 #[test]
332 fn next_base_fee_no_panic_zero_denominator() {
333 let p = BaseFeeParams::new(0, 2);
334 assert_eq!(
335 calc_next_block_base_fee(15_000_000, 30_000_000, 1_000_000_000, p),
336 1_000_000_000
337 );
338 }
339
340 #[test]
341 fn next_base_fee_no_panic_gas_limit_below_elasticity() {
342 let p = BaseFeeParams::ethereum();
343 assert_eq!(calc_next_block_base_fee(1, 1, 1_000_000_000, p), 1_000_000_000);
345 }
346}