1use crate::{
3 constants::{
4 BASE_FEE_SCALAR_OFFSET, BLOB_BASE_FEE_SCALAR_OFFSET, DA_FOOTPRINT_GAS_SCALAR_OFFSET,
5 DA_FOOTPRINT_GAS_SCALAR_SLOT, ECOTONE_L1_BLOB_BASE_FEE_SLOT, ECOTONE_L1_FEE_SCALARS_SLOT,
6 EMPTY_SCALARS, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT, L1_SCALAR_SLOT,
7 NON_ZERO_BYTE_COST, OPERATOR_FEE_CONSTANT_OFFSET, OPERATOR_FEE_JOVIAN_MULTIPLIER,
8 OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE_SCALAR_DECIMAL, OPERATOR_FEE_SCALAR_OFFSET,
9 },
10 transaction::{estimate_tx_compressed_size, OpTxTr},
11 OpSpecId,
12};
13use revm::{
14 database_interface::Database,
15 interpreter::{
16 gas::{get_tokens_in_calldata, NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST},
17 Gas,
18 },
19 primitives::U256,
20};
21
22#[derive(Clone, Debug, Default, PartialEq, Eq)]
34pub struct L1BlockInfo {
35 pub l2_block: Option<U256>,
38 pub l1_base_fee: U256,
40 pub l1_fee_overhead: Option<U256>,
42 pub l1_base_fee_scalar: U256,
44 pub l1_blob_base_fee: Option<U256>,
46 pub l1_blob_base_fee_scalar: Option<U256>,
48 pub operator_fee_scalar: Option<U256>,
50 pub operator_fee_constant: Option<U256>,
52 pub da_footprint_gas_scalar: Option<u16>,
54 pub empty_ecotone_scalars: bool,
56 pub tx_l1_cost: Option<U256>,
58}
59
60impl L1BlockInfo {
61 pub fn fetch_da_footprint_gas_scalar<DB: Database>(db: &mut DB) -> Result<u16, DB::Error> {
63 let da_footprint_gas_scalar_slot = db
64 .storage(L1_BLOCK_CONTRACT, DA_FOOTPRINT_GAS_SCALAR_SLOT)?
65 .to_be_bytes::<32>();
66
67 let bytes = [
69 da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET],
70 da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET + 1],
71 ];
72 Ok(u16::from_be_bytes(bytes))
73 }
74
75 fn try_fetch_jovian<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
77 self.da_footprint_gas_scalar = Some(Self::fetch_da_footprint_gas_scalar(db)?);
78
79 Ok(())
80 }
81
82 fn try_fetch_isthmus<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
84 let operator_fee_scalars = db
86 .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)?
87 .to_be_bytes::<32>();
88
89 self.operator_fee_scalar = Some(U256::from_be_slice(
92 operator_fee_scalars[OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4]
93 .as_ref(),
94 ));
95 self.operator_fee_constant = Some(U256::from_be_slice(
98 operator_fee_scalars[OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8]
99 .as_ref(),
100 ));
101
102 Ok(())
103 }
104
105 fn try_fetch_ecotone<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
107 self.l1_blob_base_fee = Some(db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?);
108
109 let l1_fee_scalars = db
110 .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)?
111 .to_be_bytes::<32>();
112
113 self.l1_base_fee_scalar = U256::from_be_slice(
114 l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
115 );
116
117 let l1_blob_base_fee = U256::from_be_slice(
118 l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
119 );
120 self.l1_blob_base_fee_scalar = Some(l1_blob_base_fee);
121
122 self.empty_ecotone_scalars = l1_blob_base_fee.is_zero()
125 && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
126 == EMPTY_SCALARS;
127 self.l1_fee_overhead = self
128 .empty_ecotone_scalars
129 .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT))
130 .transpose()?;
131
132 Ok(())
133 }
134
135 pub fn try_fetch<DB: Database>(
137 db: &mut DB,
138 l2_block: U256,
139 spec_id: OpSpecId,
140 ) -> Result<L1BlockInfo, DB::Error> {
141 let _ = db.basic(L1_BLOCK_CONTRACT)?;
143
144 let mut out = L1BlockInfo {
145 l2_block: Some(l2_block),
146 l1_base_fee: db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?,
147 ..Default::default()
148 };
149
150 if !spec_id.is_enabled_in(OpSpecId::ECOTONE) {
152 out.l1_base_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?;
153 out.l1_fee_overhead = Some(db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?);
154
155 return Ok(out);
156 }
157
158 out.try_fetch_ecotone(db)?;
159
160 if spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
162 out.try_fetch_isthmus(db)?;
163 }
164
165 if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
167 out.try_fetch_jovian(db)?;
168 }
169
170 Ok(out)
171 }
172
173 pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256, spec_id: OpSpecId) -> U256 {
177 if input.is_empty() || input.first() == Some(&0x7E) {
179 return U256::ZERO;
180 }
181
182 self.operator_fee_charge_inner(gas_limit, spec_id)
183 }
184
185 fn operator_fee_charge_inner(&self, gas: U256, spec_id: OpSpecId) -> U256 {
187 let operator_fee_scalar = self
188 .operator_fee_scalar
189 .expect("Missing operator fee scalar for isthmus L1 Block");
190 let operator_fee_constant = self
191 .operator_fee_constant
192 .expect("Missing operator fee constant for isthmus L1 Block");
193
194 let product = if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
195 gas.saturating_mul(operator_fee_scalar)
196 .saturating_mul(U256::from(OPERATOR_FEE_JOVIAN_MULTIPLIER))
197 } else {
198 gas.saturating_mul(operator_fee_scalar) / U256::from(OPERATOR_FEE_SCALAR_DECIMAL)
199 };
200
201 product.saturating_add(operator_fee_constant)
202 }
203
204 pub fn operator_fee_refund(&self, gas: &Gas, spec_id: OpSpecId) -> U256 {
208 if !spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
209 return U256::ZERO;
210 }
211
212 let operator_cost_gas_limit =
213 self.operator_fee_charge_inner(U256::from(gas.limit()), spec_id);
214 let operator_cost_gas_used = self.operator_fee_charge_inner(
215 U256::from(gas.limit() - (gas.remaining() + gas.refunded() as u64)),
216 spec_id,
217 );
218
219 operator_cost_gas_limit.saturating_sub(operator_cost_gas_used)
220 }
221
222 pub fn data_gas(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
230 if spec_id.is_enabled_in(OpSpecId::FJORD) {
231 let estimated_size = self.tx_estimated_size_fjord(input);
232
233 return estimated_size
234 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
235 .wrapping_div(U256::from(1_000_000));
236 };
237
238 let mut tokens_in_transaction_data = get_tokens_in_calldata(input, true);
240
241 if !spec_id.is_enabled_in(OpSpecId::REGOLITH) {
243 tokens_in_transaction_data += 68 * NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
244 }
245
246 U256::from(tokens_in_transaction_data.saturating_mul(STANDARD_TOKEN_COST))
247 }
248
249 fn tx_estimated_size_fjord(&self, input: &[u8]) -> U256 {
253 U256::from(estimate_tx_compressed_size(input))
254 }
255
256 pub fn clear_tx_l1_cost(&mut self) {
258 self.tx_l1_cost = None;
259 }
260
261 pub fn tx_cost_with_tx(&mut self, tx: impl OpTxTr, spec: OpSpecId) -> Option<U256> {
265 let enveloped_tx = tx.enveloped_tx()?;
267 let gas_limit = U256::from(tx.gas_limit());
268 Some(self.tx_cost(enveloped_tx, gas_limit, spec))
269 }
270
271 #[inline]
273 pub fn tx_cost(&mut self, enveloped_tx: &[u8], gas_limit: U256, spec: OpSpecId) -> U256 {
274 let mut additional_cost = self.calculate_tx_l1_cost(enveloped_tx, spec);
276
277 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
279 let operator_fee_charge = self.operator_fee_charge(enveloped_tx, gas_limit, spec);
280 additional_cost = additional_cost.saturating_add(operator_fee_charge);
281 }
282
283 additional_cost
284 }
285
286 pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 {
288 if let Some(tx_l1_cost) = self.tx_l1_cost {
289 return tx_l1_cost;
290 }
291 let tx_l1_cost = if input.is_empty() || input.first() == Some(&0x7E) {
293 return U256::ZERO;
294 } else if spec_id.is_enabled_in(OpSpecId::FJORD) {
295 self.calculate_tx_l1_cost_fjord(input)
296 } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) {
297 self.calculate_tx_l1_cost_ecotone(input, spec_id)
298 } else {
299 self.calculate_tx_l1_cost_bedrock(input, spec_id)
300 };
301
302 self.tx_l1_cost = Some(tx_l1_cost);
303 tx_l1_cost
304 }
305
306 fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
308 let rollup_data_gas_cost = self.data_gas(input, spec_id);
309 rollup_data_gas_cost
310 .saturating_add(self.l1_fee_overhead.unwrap_or_default())
311 .saturating_mul(self.l1_base_fee)
312 .saturating_mul(self.l1_base_fee_scalar)
313 .wrapping_div(U256::from(1_000_000))
314 }
315
316 fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
327 if self.empty_ecotone_scalars {
331 return self.calculate_tx_l1_cost_bedrock(input, spec_id);
332 }
333
334 let rollup_data_gas_cost = self.data_gas(input, spec_id);
335 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
336
337 l1_fee_scaled
338 .saturating_mul(rollup_data_gas_cost)
339 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
340 }
341
342 fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 {
347 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
348 if l1_fee_scaled.is_zero() {
349 return U256::ZERO;
350 }
351
352 let estimated_size = self.tx_estimated_size_fjord(input);
353
354 estimated_size
355 .saturating_mul(l1_fee_scaled)
356 .wrapping_div(U256::from(1_000_000_000_000u64))
357 }
358
359 fn calculate_l1_fee_scaled_ecotone(&self) -> U256 {
361 let calldata_cost_per_byte = self
362 .l1_base_fee
363 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
364 .saturating_mul(self.l1_base_fee_scalar);
365 let blob_cost_per_byte = self
366 .l1_blob_base_fee
367 .unwrap_or_default()
368 .saturating_mul(self.l1_blob_base_fee_scalar.unwrap_or_default());
369
370 calldata_cost_per_byte.saturating_add(blob_cost_per_byte)
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use revm::primitives::{bytes, hex};
378
379 #[test]
380 fn test_data_gas_non_zero_bytes() {
381 let l1_block_info = L1BlockInfo {
382 l1_base_fee: U256::from(1_000_000),
383 l1_fee_overhead: Some(U256::from(1_000_000)),
384 l1_base_fee_scalar: U256::from(1_000_000),
385 ..Default::default()
386 };
387
388 let input = bytes!("FACADE");
395 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
396 assert_eq!(bedrock_data_gas, U256::from(1136));
397
398 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
401 assert_eq!(regolith_data_gas, U256::from(48));
402
403 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
406 assert_eq!(fjord_data_gas, U256::from(1600));
407 }
408
409 #[test]
410 fn test_data_gas_zero_bytes() {
411 let l1_block_info = L1BlockInfo {
412 l1_base_fee: U256::from(1_000_000),
413 l1_fee_overhead: Some(U256::from(1_000_000)),
414 l1_base_fee_scalar: U256::from(1_000_000),
415 ..Default::default()
416 };
417
418 let input = bytes!("FA00CA00DE");
425 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
426 assert_eq!(bedrock_data_gas, U256::from(1144));
427
428 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
431 assert_eq!(regolith_data_gas, U256::from(56));
432
433 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
436 assert_eq!(fjord_data_gas, U256::from(1600));
437 }
438
439 #[test]
440 fn test_calculate_tx_l1_cost() {
441 let mut l1_block_info = L1BlockInfo {
442 l1_base_fee: U256::from(1_000),
443 l1_fee_overhead: Some(U256::from(1_000)),
444 l1_base_fee_scalar: U256::from(1_000),
445 ..Default::default()
446 };
447
448 let input = bytes!("FACADE");
449 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
450 assert_eq!(gas_cost, U256::from(1048));
451 l1_block_info.clear_tx_l1_cost();
452
453 let input = bytes!("");
455 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
456 assert_eq!(gas_cost, U256::ZERO);
457 l1_block_info.clear_tx_l1_cost();
458
459 let input = bytes!("7EFACADE");
461 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
462 assert_eq!(gas_cost, U256::ZERO);
463 }
464
465 #[test]
466 fn test_calculate_tx_l1_cost_ecotone() {
467 let mut l1_block_info = L1BlockInfo {
468 l1_base_fee: U256::from(1_000),
469 l1_base_fee_scalar: U256::from(1_000),
470 l1_blob_base_fee: Some(U256::from(1_000)),
471 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
472 l1_fee_overhead: Some(U256::from(1_000)),
473 ..Default::default()
474 };
475
476 let input = bytes!("FACADE");
480 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
481 assert_eq!(gas_cost, U256::from(51));
482 l1_block_info.clear_tx_l1_cost();
483
484 let input = bytes!("");
486 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
487 assert_eq!(gas_cost, U256::ZERO);
488 l1_block_info.clear_tx_l1_cost();
489
490 let input = bytes!("7EFACADE");
492 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
493 assert_eq!(gas_cost, U256::ZERO);
494 l1_block_info.clear_tx_l1_cost();
495
496 l1_block_info.empty_ecotone_scalars = true;
498 let input = bytes!("FACADE");
499 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
500 assert_eq!(gas_cost, U256::from(1048));
501 }
502
503 #[test]
504 fn calculate_tx_l1_cost_ecotone() {
505 let l1_block_info = L1BlockInfo {
514 l1_base_fee: U256::from_be_bytes(hex!(
515 "0000000000000000000000000000000000000000000000000000000af39ac327"
516 )), l1_base_fee_scalar: U256::from(1368),
518 l1_blob_base_fee: Some(U256::from_be_bytes(hex!(
519 "0000000000000000000000000000000000000000000000000000000d5ea528d2"
520 ))), l1_blob_base_fee_scalar: Some(U256::from(810949)),
522 ..Default::default()
523 };
524
525 const TX: &[u8] = &hex!("02f8b30a832253fc8402d11f39842c8a46398301388094dc6ff44d5d932cbd77b52e5612ba0529dc6226f180b844a9059cbb000000000000000000000000d43e02db81f4d46cdf8521f623d21ea0ec7562a50000000000000000000000000000000000000000000000008ac7230489e80000c001a02947e24750723b48f886931562c55d9e07f856d8e06468e719755e18bbc3a570a0784da9ce59fd7754ea5be6e17a86b348e441348cd48ace59d174772465eadbd1");
528
529 let expected_l1_gas_used = U256::from(2456);
532 let expected_l1_fee = U256::from_be_bytes(hex!(
533 "000000000000000000000000000000000000000000000000000006a510bd7431" ));
535
536 let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE);
539
540 assert_eq!(gas_used, expected_l1_gas_used);
541
542 let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE);
543
544 assert_eq!(l1_fee, expected_l1_fee)
545 }
546
547 #[test]
548 fn test_calculate_tx_l1_cost_fjord() {
549 let mut l1_block_info = L1BlockInfo {
553 l1_base_fee: U256::from(1_000),
554 l1_base_fee_scalar: U256::from(1_000),
555 l1_blob_base_fee: Some(U256::from(1_000)),
556 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
557 ..Default::default()
558 };
559
560 let input = bytes!("FACADE");
565 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
569 assert_eq!(gas_cost, U256::from(1700));
570 l1_block_info.clear_tx_l1_cost();
571
572 let input = bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e");
577 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
581 assert_eq!(gas_cost, U256::from(2148));
582 l1_block_info.clear_tx_l1_cost();
583
584 let input = bytes!("");
586 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
587 assert_eq!(gas_cost, U256::ZERO);
588 l1_block_info.clear_tx_l1_cost();
589
590 let input = bytes!("7EFACADE");
592 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
593 assert_eq!(gas_cost, U256::ZERO);
594 }
595
596 #[test]
597 fn calculate_tx_l1_cost_fjord() {
598 let l1_block_info = L1BlockInfo {
603 l1_base_fee: U256::from(1055991687),
604 l1_base_fee_scalar: U256::from(5227),
605 l1_blob_base_fee_scalar: Some(U256::from(1014213)),
606 l1_blob_base_fee: Some(U256::from(1)),
607 ..Default::default() };
609
610 const TX: &[u8] = &hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
613
614 let expected_data_gas = U256::from(4471);
617 let expected_l1_fee = U256::from_be_bytes(hex!(
618 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
619 ));
620
621 let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD);
624
625 assert_eq!(data_gas, expected_data_gas);
626
627 let l1_fee = l1_block_info.calculate_tx_l1_cost_fjord(TX);
628
629 assert_eq!(l1_fee, expected_l1_fee)
630 }
631
632 #[test]
633 fn test_operator_fee_charge_formulas() {
634 let l1_block_info = L1BlockInfo {
635 operator_fee_scalar: Some(U256::from(1_000u64)),
636 operator_fee_constant: Some(U256::from(10u64)),
637 ..Default::default()
638 };
639
640 let input = [0x01u8];
641
642 let isthmus_fee =
643 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::ISTHMUS);
644 assert_eq!(isthmus_fee, U256::from(11u64));
645
646 let jovian_fee =
647 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::JOVIAN);
648 assert_eq!(jovian_fee, U256::from(100_000_010u64));
649 }
650
651 #[test]
652 fn test_operator_fee_refund() {
653 let gas = Gas::new(50000);
654
655 let l1_block_info = L1BlockInfo {
656 l1_base_fee: U256::from(1055991687),
657 l1_base_fee_scalar: U256::from(5227),
658 operator_fee_scalar: Some(U256::from(2000)),
659 operator_fee_constant: Some(U256::from(5)),
660 ..Default::default()
661 };
662
663 let refunded = l1_block_info.operator_fee_refund(&gas, OpSpecId::ISTHMUS);
664
665 assert_eq!(refunded, U256::from(100))
666 }
667}