1use crate::{
2 constants::{
3 BASE_FEE_SCALAR_OFFSET, BLOB_BASE_FEE_SCALAR_OFFSET, ECOTONE_L1_BLOB_BASE_FEE_SLOT,
4 ECOTONE_L1_FEE_SCALARS_SLOT, EMPTY_SCALARS, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT,
5 L1_OVERHEAD_SLOT, L1_SCALAR_SLOT, NON_ZERO_BYTE_COST, OPERATOR_FEE_CONSTANT_OFFSET,
6 OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE_SCALAR_DECIMAL, OPERATOR_FEE_SCALAR_OFFSET,
7 ZERO_BYTE_COST,
8 },
9 transaction::estimate_tx_compressed_size,
10 OpSpecId,
11};
12use core::ops::Mul;
13use revm::{
14 database_interface::Database, interpreter::Gas, primitives::hardfork::SpecId, primitives::U256,
15};
16
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
29pub struct L1BlockInfo {
30 pub l2_block: u64,
33 pub l1_base_fee: U256,
35 pub l1_fee_overhead: Option<U256>,
37 pub l1_base_fee_scalar: U256,
39 pub l1_blob_base_fee: Option<U256>,
41 pub l1_blob_base_fee_scalar: Option<U256>,
43 pub operator_fee_scalar: Option<U256>,
45 pub operator_fee_constant: Option<U256>,
47 pub(crate) empty_ecotone_scalars: bool,
49 pub tx_l1_cost: Option<U256>,
51}
52
53impl L1BlockInfo {
54 pub fn try_fetch<DB: Database>(
56 db: &mut DB,
57 l2_block: u64,
58 spec_id: OpSpecId,
59 ) -> Result<L1BlockInfo, DB::Error> {
60 if spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN) {
63 let _ = db.basic(L1_BLOCK_CONTRACT)?;
64 }
65
66 let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?;
67
68 if !spec_id.is_enabled_in(OpSpecId::ECOTONE) {
69 let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?;
70 let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?;
71
72 Ok(L1BlockInfo {
73 l1_base_fee,
74 l1_fee_overhead: Some(l1_fee_overhead),
75 l1_base_fee_scalar: l1_fee_scalar,
76 ..Default::default()
77 })
78 } else {
79 let l1_blob_base_fee = db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?;
80 let l1_fee_scalars = db
81 .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)?
82 .to_be_bytes::<32>();
83
84 let l1_base_fee_scalar = U256::from_be_slice(
85 l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
86 );
87 let l1_blob_base_fee_scalar = U256::from_be_slice(
88 l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
89 .as_ref(),
90 );
91
92 let empty_ecotone_scalars = l1_blob_base_fee.is_zero()
95 && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
96 == EMPTY_SCALARS;
97 let l1_fee_overhead = empty_ecotone_scalars
98 .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT))
99 .transpose()?;
100
101 if spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
102 let operator_fee_scalars = db
103 .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)?
104 .to_be_bytes::<32>();
105
106 let operator_fee_scalar = U256::from_be_slice(
110 operator_fee_scalars
111 [OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4]
112 .as_ref(),
113 );
114 let operator_fee_constant = U256::from_be_slice(
117 operator_fee_scalars
118 [OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8]
119 .as_ref(),
120 );
121 Ok(L1BlockInfo {
122 l2_block,
123 l1_base_fee,
124 l1_base_fee_scalar,
125 l1_blob_base_fee: Some(l1_blob_base_fee),
126 l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
127 empty_ecotone_scalars,
128 l1_fee_overhead,
129 operator_fee_scalar: Some(operator_fee_scalar),
130 operator_fee_constant: Some(operator_fee_constant),
131 tx_l1_cost: None,
132 })
133 } else {
134 Ok(L1BlockInfo {
136 l1_base_fee,
137 l1_base_fee_scalar,
138 l1_blob_base_fee: Some(l1_blob_base_fee),
139 l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
140 empty_ecotone_scalars,
141 l1_fee_overhead,
142 ..Default::default()
143 })
144 }
145 }
146 }
147
148 pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256) -> U256 {
152 if input.first() == Some(&0x7E) {
154 return U256::ZERO;
155 }
156
157 self.operator_fee_charge_inner(gas_limit)
158 }
159
160 fn operator_fee_charge_inner(&self, gas: U256) -> U256 {
162 let operator_fee_scalar = self
163 .operator_fee_scalar
164 .expect("Missing operator fee scalar for isthmus L1 Block");
165 let operator_fee_constant = self
166 .operator_fee_constant
167 .expect("Missing operator fee constant for isthmus L1 Block");
168
169 let product =
170 gas.saturating_mul(operator_fee_scalar) / (U256::from(OPERATOR_FEE_SCALAR_DECIMAL));
171
172 product.saturating_add(operator_fee_constant)
173 }
174
175 pub fn operator_fee_refund(&self, gas: &Gas, spec_id: OpSpecId) -> U256 {
179 if !spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
180 return U256::ZERO;
181 }
182
183 let operator_cost_gas_limit = self.operator_fee_charge_inner(U256::from(gas.limit()));
184 let operator_cost_gas_used = self.operator_fee_charge_inner(U256::from(
185 gas.limit() - (gas.remaining() + gas.refunded() as u64),
186 ));
187
188 operator_cost_gas_limit.saturating_sub(operator_cost_gas_used)
189 }
190
191 pub fn data_gas(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
199 if spec_id.is_enabled_in(OpSpecId::FJORD) {
200 let estimated_size = self.tx_estimated_size_fjord(input);
201
202 return estimated_size
203 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
204 .wrapping_div(U256::from(1_000_000));
205 };
206
207 let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| {
208 acc + if *byte == 0x00 {
209 ZERO_BYTE_COST
210 } else {
211 NON_ZERO_BYTE_COST
212 }
213 }));
214
215 if !spec_id.is_enabled_in(OpSpecId::REGOLITH) {
217 rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68));
218 }
219
220 rollup_data_gas_cost
221 }
222
223 fn tx_estimated_size_fjord(&self, input: &[u8]) -> U256 {
227 U256::from(estimate_tx_compressed_size(input))
228 }
229
230 pub fn clear_tx_l1_cost(&mut self) {
232 self.tx_l1_cost = None;
233 }
234
235 pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 {
237 if let Some(tx_l1_cost) = self.tx_l1_cost {
238 return tx_l1_cost;
239 }
240 let tx_l1_cost = if input.is_empty() || input.first() == Some(&0x7E) {
242 return U256::ZERO;
243 } else if spec_id.is_enabled_in(OpSpecId::FJORD) {
244 self.calculate_tx_l1_cost_fjord(input)
245 } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) {
246 self.calculate_tx_l1_cost_ecotone(input, spec_id)
247 } else {
248 self.calculate_tx_l1_cost_bedrock(input, spec_id)
249 };
250
251 self.tx_l1_cost = Some(tx_l1_cost);
252 tx_l1_cost
253 }
254
255 fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
257 let rollup_data_gas_cost = self.data_gas(input, spec_id);
258 rollup_data_gas_cost
259 .saturating_add(self.l1_fee_overhead.unwrap_or_default())
260 .saturating_mul(self.l1_base_fee)
261 .saturating_mul(self.l1_base_fee_scalar)
262 .wrapping_div(U256::from(1_000_000))
263 }
264
265 fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
276 if self.empty_ecotone_scalars {
280 return self.calculate_tx_l1_cost_bedrock(input, spec_id);
281 }
282
283 let rollup_data_gas_cost = self.data_gas(input, spec_id);
284 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
285
286 l1_fee_scaled
287 .saturating_mul(rollup_data_gas_cost)
288 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
289 }
290
291 fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 {
296 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
297 let estimated_size = self.tx_estimated_size_fjord(input);
298
299 estimated_size
300 .saturating_mul(l1_fee_scaled)
301 .wrapping_div(U256::from(1_000_000_000_000u64))
302 }
303
304 fn calculate_l1_fee_scaled_ecotone(&self) -> U256 {
306 let calldata_cost_per_byte = self
307 .l1_base_fee
308 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
309 .saturating_mul(self.l1_base_fee_scalar);
310 let blob_cost_per_byte = self
311 .l1_blob_base_fee
312 .unwrap_or_default()
313 .saturating_mul(self.l1_blob_base_fee_scalar.unwrap_or_default());
314
315 calldata_cost_per_byte.saturating_add(blob_cost_per_byte)
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use revm::primitives::{bytes, hex};
323
324 #[test]
325 fn test_data_gas_non_zero_bytes() {
326 let l1_block_info = L1BlockInfo {
327 l1_base_fee: U256::from(1_000_000),
328 l1_fee_overhead: Some(U256::from(1_000_000)),
329 l1_base_fee_scalar: U256::from(1_000_000),
330 ..Default::default()
331 };
332
333 let input = bytes!("FACADE");
340 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
341 assert_eq!(bedrock_data_gas, U256::from(1136));
342
343 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
346 assert_eq!(regolith_data_gas, U256::from(48));
347
348 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
351 assert_eq!(fjord_data_gas, U256::from(1600));
352 }
353
354 #[test]
355 fn test_data_gas_zero_bytes() {
356 let l1_block_info = L1BlockInfo {
357 l1_base_fee: U256::from(1_000_000),
358 l1_fee_overhead: Some(U256::from(1_000_000)),
359 l1_base_fee_scalar: U256::from(1_000_000),
360 ..Default::default()
361 };
362
363 let input = bytes!("FA00CA00DE");
370 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
371 assert_eq!(bedrock_data_gas, U256::from(1144));
372
373 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
376 assert_eq!(regolith_data_gas, U256::from(56));
377
378 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
381 assert_eq!(fjord_data_gas, U256::from(1600));
382 }
383
384 #[test]
385 fn test_calculate_tx_l1_cost() {
386 let mut l1_block_info = L1BlockInfo {
387 l1_base_fee: U256::from(1_000),
388 l1_fee_overhead: Some(U256::from(1_000)),
389 l1_base_fee_scalar: U256::from(1_000),
390 ..Default::default()
391 };
392
393 let input = bytes!("FACADE");
394 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
395 assert_eq!(gas_cost, U256::from(1048));
396 l1_block_info.clear_tx_l1_cost();
397
398 let input = bytes!("");
400 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
401 assert_eq!(gas_cost, U256::ZERO);
402 l1_block_info.clear_tx_l1_cost();
403
404 let input = bytes!("7EFACADE");
406 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
407 assert_eq!(gas_cost, U256::ZERO);
408 }
409
410 #[test]
411 fn test_calculate_tx_l1_cost_ecotone() {
412 let mut l1_block_info = L1BlockInfo {
413 l1_base_fee: U256::from(1_000),
414 l1_base_fee_scalar: U256::from(1_000),
415 l1_blob_base_fee: Some(U256::from(1_000)),
416 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
417 l1_fee_overhead: Some(U256::from(1_000)),
418 ..Default::default()
419 };
420
421 let input = bytes!("FACADE");
425 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
426 assert_eq!(gas_cost, U256::from(51));
427 l1_block_info.clear_tx_l1_cost();
428
429 let input = bytes!("");
431 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
432 assert_eq!(gas_cost, U256::ZERO);
433 l1_block_info.clear_tx_l1_cost();
434
435 let input = bytes!("7EFACADE");
437 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
438 assert_eq!(gas_cost, U256::ZERO);
439 l1_block_info.clear_tx_l1_cost();
440
441 l1_block_info.empty_ecotone_scalars = true;
443 let input = bytes!("FACADE");
444 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
445 assert_eq!(gas_cost, U256::from(1048));
446 }
447
448 #[test]
449 fn calculate_tx_l1_cost_ecotone() {
450 let l1_block_info = L1BlockInfo {
459 l1_base_fee: U256::from_be_bytes(hex!(
460 "0000000000000000000000000000000000000000000000000000000af39ac327"
461 )), l1_base_fee_scalar: U256::from(1368),
463 l1_blob_base_fee: Some(U256::from_be_bytes(hex!(
464 "0000000000000000000000000000000000000000000000000000000d5ea528d2"
465 ))), l1_blob_base_fee_scalar: Some(U256::from(810949)),
467 ..Default::default()
468 };
469
470 const TX: &[u8] = &hex!("02f8b30a832253fc8402d11f39842c8a46398301388094dc6ff44d5d932cbd77b52e5612ba0529dc6226f180b844a9059cbb000000000000000000000000d43e02db81f4d46cdf8521f623d21ea0ec7562a50000000000000000000000000000000000000000000000008ac7230489e80000c001a02947e24750723b48f886931562c55d9e07f856d8e06468e719755e18bbc3a570a0784da9ce59fd7754ea5be6e17a86b348e441348cd48ace59d174772465eadbd1");
473
474 let expected_l1_gas_used = U256::from(2456);
477 let expected_l1_fee = U256::from_be_bytes(hex!(
478 "000000000000000000000000000000000000000000000000000006a510bd7431" ));
480
481 let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE);
484
485 assert_eq!(gas_used, expected_l1_gas_used);
486
487 let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE);
488
489 assert_eq!(l1_fee, expected_l1_fee)
490 }
491
492 #[test]
493 fn test_calculate_tx_l1_cost_fjord() {
494 let mut l1_block_info = L1BlockInfo {
498 l1_base_fee: U256::from(1_000),
499 l1_base_fee_scalar: U256::from(1_000),
500 l1_blob_base_fee: Some(U256::from(1_000)),
501 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
502 ..Default::default()
503 };
504
505 let input = bytes!("FACADE");
510 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
514 assert_eq!(gas_cost, U256::from(1700));
515 l1_block_info.clear_tx_l1_cost();
516
517 let input = bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e");
522 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
526 assert_eq!(gas_cost, U256::from(2148));
527 l1_block_info.clear_tx_l1_cost();
528
529 let input = bytes!("");
531 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
532 assert_eq!(gas_cost, U256::ZERO);
533 l1_block_info.clear_tx_l1_cost();
534
535 let input = bytes!("7EFACADE");
537 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
538 assert_eq!(gas_cost, U256::ZERO);
539 }
540
541 #[test]
542 fn calculate_tx_l1_cost_fjord() {
543 let l1_block_info = L1BlockInfo {
548 l1_base_fee: U256::from(1055991687),
549 l1_base_fee_scalar: U256::from(5227),
550 l1_blob_base_fee_scalar: Some(U256::from(1014213)),
551 l1_blob_base_fee: Some(U256::from(1)),
552 ..Default::default() };
554
555 const TX: &[u8] = &hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
558
559 let expected_data_gas = U256::from(4471);
562 let expected_l1_fee = U256::from_be_bytes(hex!(
563 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
564 ));
565
566 let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD);
569
570 assert_eq!(data_gas, expected_data_gas);
571
572 let l1_fee = l1_block_info.calculate_tx_l1_cost_fjord(TX);
573
574 assert_eq!(l1_fee, expected_l1_fee)
575 }
576
577 #[test]
578 fn test_operator_fee_refund() {
579 let gas = Gas::new(50000);
580
581 let l1_block_info = L1BlockInfo {
582 l1_base_fee: U256::from(1055991687),
583 l1_base_fee_scalar: U256::from(5227),
584 operator_fee_scalar: Some(U256::from(2000)),
585 operator_fee_constant: Some(U256::from(5)),
586 ..Default::default()
587 };
588
589 let refunded = l1_block_info.operator_fee_refund(&gas, OpSpecId::ISTHMUS);
590
591 assert_eq!(refunded, U256::from(100))
592 }
593}