1use crate::{
3 constants::{
4 BASE_FEE_SCALAR_OFFSET, BLOB_BASE_FEE_SCALAR_OFFSET, ECOTONE_L1_BLOB_BASE_FEE_SLOT,
5 ECOTONE_L1_FEE_SCALARS_SLOT, EMPTY_SCALARS, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT,
6 L1_OVERHEAD_SLOT, L1_SCALAR_SLOT, NON_ZERO_BYTE_COST, OPERATOR_FEE_CONSTANT_OFFSET,
7 OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE_SCALAR_DECIMAL, OPERATOR_FEE_SCALAR_OFFSET,
8 },
9 transaction::estimate_tx_compressed_size,
10 OpSpecId,
11};
12use revm::{
13 database_interface::Database,
14 interpreter::{
15 gas::{get_tokens_in_calldata, NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST},
16 Gas,
17 },
18 primitives::{hardfork::SpecId, U256},
19};
20
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
33pub struct L1BlockInfo {
34 pub l2_block: U256,
37 pub l1_base_fee: U256,
39 pub l1_fee_overhead: Option<U256>,
41 pub l1_base_fee_scalar: U256,
43 pub l1_blob_base_fee: Option<U256>,
45 pub l1_blob_base_fee_scalar: Option<U256>,
47 pub operator_fee_scalar: Option<U256>,
49 pub operator_fee_constant: Option<U256>,
51 pub(crate) empty_ecotone_scalars: bool,
53 pub tx_l1_cost: Option<U256>,
55}
56
57impl L1BlockInfo {
58 pub fn try_fetch<DB: Database>(
60 db: &mut DB,
61 l2_block: U256,
62 spec_id: OpSpecId,
63 ) -> Result<L1BlockInfo, DB::Error> {
64 if spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN) {
67 let _ = db.basic(L1_BLOCK_CONTRACT)?;
68 }
69
70 let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?;
71
72 if !spec_id.is_enabled_in(OpSpecId::ECOTONE) {
73 let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?;
74 let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?;
75
76 Ok(L1BlockInfo {
77 l1_base_fee,
78 l1_fee_overhead: Some(l1_fee_overhead),
79 l1_base_fee_scalar: l1_fee_scalar,
80 ..Default::default()
81 })
82 } else {
83 let l1_blob_base_fee = db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?;
84 let l1_fee_scalars = db
85 .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)?
86 .to_be_bytes::<32>();
87
88 let l1_base_fee_scalar = U256::from_be_slice(
89 l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
90 );
91 let l1_blob_base_fee_scalar = U256::from_be_slice(
92 l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
93 .as_ref(),
94 );
95
96 let empty_ecotone_scalars = l1_blob_base_fee.is_zero()
99 && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
100 == EMPTY_SCALARS;
101 let l1_fee_overhead = empty_ecotone_scalars
102 .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT))
103 .transpose()?;
104
105 if spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
106 let operator_fee_scalars = db
107 .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)?
108 .to_be_bytes::<32>();
109
110 let operator_fee_scalar = U256::from_be_slice(
114 operator_fee_scalars
115 [OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4]
116 .as_ref(),
117 );
118 let operator_fee_constant = U256::from_be_slice(
121 operator_fee_scalars
122 [OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8]
123 .as_ref(),
124 );
125 Ok(L1BlockInfo {
126 l2_block,
127 l1_base_fee,
128 l1_base_fee_scalar,
129 l1_blob_base_fee: Some(l1_blob_base_fee),
130 l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
131 empty_ecotone_scalars,
132 l1_fee_overhead,
133 operator_fee_scalar: Some(operator_fee_scalar),
134 operator_fee_constant: Some(operator_fee_constant),
135 tx_l1_cost: None,
136 })
137 } else {
138 Ok(L1BlockInfo {
140 l1_base_fee,
141 l1_base_fee_scalar,
142 l1_blob_base_fee: Some(l1_blob_base_fee),
143 l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
144 empty_ecotone_scalars,
145 l1_fee_overhead,
146 ..Default::default()
147 })
148 }
149 }
150 }
151
152 pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256) -> U256 {
156 if input.first() == Some(&0x7E) {
158 return U256::ZERO;
159 }
160
161 self.operator_fee_charge_inner(gas_limit)
162 }
163
164 fn operator_fee_charge_inner(&self, gas: U256) -> U256 {
166 let operator_fee_scalar = self
167 .operator_fee_scalar
168 .expect("Missing operator fee scalar for isthmus L1 Block");
169 let operator_fee_constant = self
170 .operator_fee_constant
171 .expect("Missing operator fee constant for isthmus L1 Block");
172
173 let product =
174 gas.saturating_mul(operator_fee_scalar) / (U256::from(OPERATOR_FEE_SCALAR_DECIMAL));
175
176 product.saturating_add(operator_fee_constant)
177 }
178
179 pub fn operator_fee_refund(&self, gas: &Gas, spec_id: OpSpecId) -> U256 {
183 if !spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
184 return U256::ZERO;
185 }
186
187 let operator_cost_gas_limit = self.operator_fee_charge_inner(U256::from(gas.limit()));
188 let operator_cost_gas_used = self.operator_fee_charge_inner(U256::from(
189 gas.limit() - (gas.remaining() + gas.refunded() as u64),
190 ));
191
192 operator_cost_gas_limit.saturating_sub(operator_cost_gas_used)
193 }
194
195 pub fn data_gas(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
203 if spec_id.is_enabled_in(OpSpecId::FJORD) {
204 let estimated_size = self.tx_estimated_size_fjord(input);
205
206 return estimated_size
207 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
208 .wrapping_div(U256::from(1_000_000));
209 };
210
211 let mut tokens_in_transaction_data = get_tokens_in_calldata(input, true);
213
214 if !spec_id.is_enabled_in(OpSpecId::REGOLITH) {
216 tokens_in_transaction_data += 68 * NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
217 }
218
219 U256::from(tokens_in_transaction_data.saturating_mul(STANDARD_TOKEN_COST))
220 }
221
222 fn tx_estimated_size_fjord(&self, input: &[u8]) -> U256 {
226 U256::from(estimate_tx_compressed_size(input))
227 }
228
229 pub fn clear_tx_l1_cost(&mut self) {
231 self.tx_l1_cost = None;
232 }
233
234 pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 {
236 if let Some(tx_l1_cost) = self.tx_l1_cost {
237 return tx_l1_cost;
238 }
239 let tx_l1_cost = if input.is_empty() || input.first() == Some(&0x7E) {
241 return U256::ZERO;
242 } else if spec_id.is_enabled_in(OpSpecId::FJORD) {
243 self.calculate_tx_l1_cost_fjord(input)
244 } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) {
245 self.calculate_tx_l1_cost_ecotone(input, spec_id)
246 } else {
247 self.calculate_tx_l1_cost_bedrock(input, spec_id)
248 };
249
250 self.tx_l1_cost = Some(tx_l1_cost);
251 tx_l1_cost
252 }
253
254 fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
256 let rollup_data_gas_cost = self.data_gas(input, spec_id);
257 rollup_data_gas_cost
258 .saturating_add(self.l1_fee_overhead.unwrap_or_default())
259 .saturating_mul(self.l1_base_fee)
260 .saturating_mul(self.l1_base_fee_scalar)
261 .wrapping_div(U256::from(1_000_000))
262 }
263
264 fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
275 if self.empty_ecotone_scalars {
279 return self.calculate_tx_l1_cost_bedrock(input, spec_id);
280 }
281
282 let rollup_data_gas_cost = self.data_gas(input, spec_id);
283 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
284
285 l1_fee_scaled
286 .saturating_mul(rollup_data_gas_cost)
287 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
288 }
289
290 fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 {
295 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
296 let estimated_size = self.tx_estimated_size_fjord(input);
297
298 estimated_size
299 .saturating_mul(l1_fee_scaled)
300 .wrapping_div(U256::from(1_000_000_000_000u64))
301 }
302
303 fn calculate_l1_fee_scaled_ecotone(&self) -> U256 {
305 let calldata_cost_per_byte = self
306 .l1_base_fee
307 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
308 .saturating_mul(self.l1_base_fee_scalar);
309 let blob_cost_per_byte = self
310 .l1_blob_base_fee
311 .unwrap_or_default()
312 .saturating_mul(self.l1_blob_base_fee_scalar.unwrap_or_default());
313
314 calldata_cost_per_byte.saturating_add(blob_cost_per_byte)
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use revm::primitives::{bytes, hex};
322
323 #[test]
324 fn test_data_gas_non_zero_bytes() {
325 let l1_block_info = L1BlockInfo {
326 l1_base_fee: U256::from(1_000_000),
327 l1_fee_overhead: Some(U256::from(1_000_000)),
328 l1_base_fee_scalar: U256::from(1_000_000),
329 ..Default::default()
330 };
331
332 let input = bytes!("FACADE");
339 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
340 assert_eq!(bedrock_data_gas, U256::from(1136));
341
342 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
345 assert_eq!(regolith_data_gas, U256::from(48));
346
347 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
350 assert_eq!(fjord_data_gas, U256::from(1600));
351 }
352
353 #[test]
354 fn test_data_gas_zero_bytes() {
355 let l1_block_info = L1BlockInfo {
356 l1_base_fee: U256::from(1_000_000),
357 l1_fee_overhead: Some(U256::from(1_000_000)),
358 l1_base_fee_scalar: U256::from(1_000_000),
359 ..Default::default()
360 };
361
362 let input = bytes!("FA00CA00DE");
369 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
370 assert_eq!(bedrock_data_gas, U256::from(1144));
371
372 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
375 assert_eq!(regolith_data_gas, U256::from(56));
376
377 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
380 assert_eq!(fjord_data_gas, U256::from(1600));
381 }
382
383 #[test]
384 fn test_calculate_tx_l1_cost() {
385 let mut l1_block_info = L1BlockInfo {
386 l1_base_fee: U256::from(1_000),
387 l1_fee_overhead: Some(U256::from(1_000)),
388 l1_base_fee_scalar: U256::from(1_000),
389 ..Default::default()
390 };
391
392 let input = bytes!("FACADE");
393 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
394 assert_eq!(gas_cost, U256::from(1048));
395 l1_block_info.clear_tx_l1_cost();
396
397 let input = bytes!("");
399 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
400 assert_eq!(gas_cost, U256::ZERO);
401 l1_block_info.clear_tx_l1_cost();
402
403 let input = bytes!("7EFACADE");
405 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
406 assert_eq!(gas_cost, U256::ZERO);
407 }
408
409 #[test]
410 fn test_calculate_tx_l1_cost_ecotone() {
411 let mut l1_block_info = L1BlockInfo {
412 l1_base_fee: U256::from(1_000),
413 l1_base_fee_scalar: U256::from(1_000),
414 l1_blob_base_fee: Some(U256::from(1_000)),
415 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
416 l1_fee_overhead: Some(U256::from(1_000)),
417 ..Default::default()
418 };
419
420 let input = bytes!("FACADE");
424 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
425 assert_eq!(gas_cost, U256::from(51));
426 l1_block_info.clear_tx_l1_cost();
427
428 let input = bytes!("");
430 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
431 assert_eq!(gas_cost, U256::ZERO);
432 l1_block_info.clear_tx_l1_cost();
433
434 let input = bytes!("7EFACADE");
436 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
437 assert_eq!(gas_cost, U256::ZERO);
438 l1_block_info.clear_tx_l1_cost();
439
440 l1_block_info.empty_ecotone_scalars = true;
442 let input = bytes!("FACADE");
443 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
444 assert_eq!(gas_cost, U256::from(1048));
445 }
446
447 #[test]
448 fn calculate_tx_l1_cost_ecotone() {
449 let l1_block_info = L1BlockInfo {
458 l1_base_fee: U256::from_be_bytes(hex!(
459 "0000000000000000000000000000000000000000000000000000000af39ac327"
460 )), l1_base_fee_scalar: U256::from(1368),
462 l1_blob_base_fee: Some(U256::from_be_bytes(hex!(
463 "0000000000000000000000000000000000000000000000000000000d5ea528d2"
464 ))), l1_blob_base_fee_scalar: Some(U256::from(810949)),
466 ..Default::default()
467 };
468
469 const TX: &[u8] = &hex!("02f8b30a832253fc8402d11f39842c8a46398301388094dc6ff44d5d932cbd77b52e5612ba0529dc6226f180b844a9059cbb000000000000000000000000d43e02db81f4d46cdf8521f623d21ea0ec7562a50000000000000000000000000000000000000000000000008ac7230489e80000c001a02947e24750723b48f886931562c55d9e07f856d8e06468e719755e18bbc3a570a0784da9ce59fd7754ea5be6e17a86b348e441348cd48ace59d174772465eadbd1");
472
473 let expected_l1_gas_used = U256::from(2456);
476 let expected_l1_fee = U256::from_be_bytes(hex!(
477 "000000000000000000000000000000000000000000000000000006a510bd7431" ));
479
480 let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE);
483
484 assert_eq!(gas_used, expected_l1_gas_used);
485
486 let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE);
487
488 assert_eq!(l1_fee, expected_l1_fee)
489 }
490
491 #[test]
492 fn test_calculate_tx_l1_cost_fjord() {
493 let mut l1_block_info = L1BlockInfo {
497 l1_base_fee: U256::from(1_000),
498 l1_base_fee_scalar: U256::from(1_000),
499 l1_blob_base_fee: Some(U256::from(1_000)),
500 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
501 ..Default::default()
502 };
503
504 let input = bytes!("FACADE");
509 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
513 assert_eq!(gas_cost, U256::from(1700));
514 l1_block_info.clear_tx_l1_cost();
515
516 let input = bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e");
521 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
525 assert_eq!(gas_cost, U256::from(2148));
526 l1_block_info.clear_tx_l1_cost();
527
528 let input = bytes!("");
530 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
531 assert_eq!(gas_cost, U256::ZERO);
532 l1_block_info.clear_tx_l1_cost();
533
534 let input = bytes!("7EFACADE");
536 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
537 assert_eq!(gas_cost, U256::ZERO);
538 }
539
540 #[test]
541 fn calculate_tx_l1_cost_fjord() {
542 let l1_block_info = L1BlockInfo {
547 l1_base_fee: U256::from(1055991687),
548 l1_base_fee_scalar: U256::from(5227),
549 l1_blob_base_fee_scalar: Some(U256::from(1014213)),
550 l1_blob_base_fee: Some(U256::from(1)),
551 ..Default::default() };
553
554 const TX: &[u8] = &hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
557
558 let expected_data_gas = U256::from(4471);
561 let expected_l1_fee = U256::from_be_bytes(hex!(
562 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
563 ));
564
565 let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD);
568
569 assert_eq!(data_gas, expected_data_gas);
570
571 let l1_fee = l1_block_info.calculate_tx_l1_cost_fjord(TX);
572
573 assert_eq!(l1_fee, expected_l1_fee)
574 }
575
576 #[test]
577 fn test_operator_fee_refund() {
578 let gas = Gas::new(50000);
579
580 let l1_block_info = L1BlockInfo {
581 l1_base_fee: U256::from(1055991687),
582 l1_base_fee_scalar: U256::from(5227),
583 operator_fee_scalar: Some(U256::from(2000)),
584 operator_fee_constant: Some(U256::from(5)),
585 ..Default::default()
586 };
587
588 let refunded = l1_block_info.operator_fee_refund(&gas, OpSpecId::ISTHMUS);
589
590 assert_eq!(refunded, U256::from(100))
591 }
592}