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 context_interface::cfg::gas::{NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST},
15 database_interface::Database,
16 interpreter::{gas::get_tokens_in_calldata_istanbul, Gas},
17 primitives::U256,
18};
19
20#[derive(Clone, Debug, Default, PartialEq, Eq)]
32pub struct L1BlockInfo {
33 pub l2_block: Option<U256>,
36 pub l1_base_fee: U256,
38 pub l1_fee_overhead: Option<U256>,
40 pub l1_base_fee_scalar: U256,
42 pub l1_blob_base_fee: Option<U256>,
44 pub l1_blob_base_fee_scalar: Option<U256>,
46 pub operator_fee_scalar: Option<U256>,
48 pub operator_fee_constant: Option<U256>,
50 pub da_footprint_gas_scalar: Option<u16>,
52 pub empty_ecotone_scalars: bool,
54 pub tx_l1_cost: Option<U256>,
56}
57
58impl L1BlockInfo {
59 pub fn fetch_da_footprint_gas_scalar<DB: Database>(db: &mut DB) -> Result<u16, DB::Error> {
61 let da_footprint_gas_scalar_slot = db
62 .storage(L1_BLOCK_CONTRACT, DA_FOOTPRINT_GAS_SCALAR_SLOT)?
63 .to_be_bytes::<32>();
64
65 let bytes = [
67 da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET],
68 da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET + 1],
69 ];
70 Ok(u16::from_be_bytes(bytes))
71 }
72
73 fn try_fetch_jovian<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
75 self.da_footprint_gas_scalar = Some(Self::fetch_da_footprint_gas_scalar(db)?);
76
77 Ok(())
78 }
79
80 fn try_fetch_isthmus<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
82 let operator_fee_scalars = db
84 .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)?
85 .to_be_bytes::<32>();
86
87 self.operator_fee_scalar = Some(U256::from_be_slice(
90 operator_fee_scalars[OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4]
91 .as_ref(),
92 ));
93 self.operator_fee_constant = Some(U256::from_be_slice(
96 operator_fee_scalars[OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8]
97 .as_ref(),
98 ));
99
100 Ok(())
101 }
102
103 fn try_fetch_ecotone<DB: Database>(&mut self, db: &mut DB) -> Result<(), DB::Error> {
105 self.l1_blob_base_fee = Some(db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?);
106
107 let l1_fee_scalars = db
108 .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)?
109 .to_be_bytes::<32>();
110
111 self.l1_base_fee_scalar = U256::from_be_slice(
112 l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
113 );
114
115 let l1_blob_base_fee = U256::from_be_slice(
116 l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4].as_ref(),
117 );
118 self.l1_blob_base_fee_scalar = Some(l1_blob_base_fee);
119
120 self.empty_ecotone_scalars = l1_blob_base_fee.is_zero()
123 && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4]
124 == EMPTY_SCALARS;
125 self.l1_fee_overhead = self
126 .empty_ecotone_scalars
127 .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT))
128 .transpose()?;
129
130 Ok(())
131 }
132
133 pub fn try_fetch<DB: Database>(
135 db: &mut DB,
136 l2_block: U256,
137 spec_id: OpSpecId,
138 ) -> Result<L1BlockInfo, DB::Error> {
139 let _ = db.basic(L1_BLOCK_CONTRACT)?;
141
142 let mut out = L1BlockInfo {
143 l2_block: Some(l2_block),
144 l1_base_fee: db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?,
145 ..Default::default()
146 };
147
148 if !spec_id.is_enabled_in(OpSpecId::ECOTONE) {
150 out.l1_base_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?;
151 out.l1_fee_overhead = Some(db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?);
152
153 return Ok(out);
154 }
155
156 out.try_fetch_ecotone(db)?;
157
158 if spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
160 out.try_fetch_isthmus(db)?;
161 }
162
163 if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
165 out.try_fetch_jovian(db)?;
166 }
167
168 Ok(out)
169 }
170
171 pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256, spec_id: OpSpecId) -> U256 {
175 if input.is_empty() || input.first() == Some(&0x7E) {
177 return U256::ZERO;
178 }
179
180 self.operator_fee_charge_inner(gas_limit, spec_id)
181 }
182
183 fn operator_fee_charge_inner(&self, gas: U256, spec_id: OpSpecId) -> U256 {
185 let operator_fee_scalar = self
186 .operator_fee_scalar
187 .expect("Missing operator fee scalar for isthmus L1 Block");
188 let operator_fee_constant = self
189 .operator_fee_constant
190 .expect("Missing operator fee constant for isthmus L1 Block");
191
192 let product = if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
193 gas.saturating_mul(operator_fee_scalar)
194 .saturating_mul(U256::from(OPERATOR_FEE_JOVIAN_MULTIPLIER))
195 } else {
196 gas.saturating_mul(operator_fee_scalar) / U256::from(OPERATOR_FEE_SCALAR_DECIMAL)
197 };
198
199 product.saturating_add(operator_fee_constant)
200 }
201
202 pub fn operator_fee_refund(&self, gas: &Gas, spec_id: OpSpecId) -> U256 {
206 if !spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
207 return U256::ZERO;
208 }
209
210 let operator_cost_gas_limit =
211 self.operator_fee_charge_inner(U256::from(gas.limit()), spec_id);
212 let operator_cost_gas_used = self.operator_fee_charge_inner(
213 U256::from(gas.limit() - (gas.remaining() + gas.refunded() as u64)),
214 spec_id,
215 );
216
217 operator_cost_gas_limit.saturating_sub(operator_cost_gas_used)
218 }
219
220 pub fn data_gas(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
228 if spec_id.is_enabled_in(OpSpecId::FJORD) {
229 let estimated_size = self.tx_estimated_size_fjord(input);
230
231 return estimated_size
232 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
233 .wrapping_div(U256::from(1_000_000));
234 };
235
236 let mut tokens_in_transaction_data = get_tokens_in_calldata_istanbul(input);
238
239 if !spec_id.is_enabled_in(OpSpecId::REGOLITH) {
241 tokens_in_transaction_data += 68 * NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
242 }
243
244 U256::from(tokens_in_transaction_data.saturating_mul(STANDARD_TOKEN_COST))
245 }
246
247 fn tx_estimated_size_fjord(&self, input: &[u8]) -> U256 {
251 U256::from(estimate_tx_compressed_size(input))
252 }
253
254 pub fn clear_tx_l1_cost(&mut self) {
256 self.tx_l1_cost = None;
257 }
258
259 pub fn tx_cost_with_tx(&mut self, tx: impl OpTxTr, spec: OpSpecId) -> Option<U256> {
263 let enveloped_tx = tx.enveloped_tx()?;
265 let gas_limit = U256::from(tx.gas_limit());
266 Some(self.tx_cost(enveloped_tx, gas_limit, spec))
267 }
268
269 #[inline]
271 pub fn tx_cost(&mut self, enveloped_tx: &[u8], gas_limit: U256, spec: OpSpecId) -> U256 {
272 let mut additional_cost = self.calculate_tx_l1_cost(enveloped_tx, spec);
274
275 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
277 let operator_fee_charge = self.operator_fee_charge(enveloped_tx, gas_limit, spec);
278 additional_cost = additional_cost.saturating_add(operator_fee_charge);
279 }
280
281 additional_cost
282 }
283
284 pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 {
286 if let Some(tx_l1_cost) = self.tx_l1_cost {
287 return tx_l1_cost;
288 }
289 let tx_l1_cost = if input.is_empty() || input.first() == Some(&0x7E) {
291 return U256::ZERO;
292 } else if spec_id.is_enabled_in(OpSpecId::FJORD) {
293 self.calculate_tx_l1_cost_fjord(input)
294 } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) {
295 self.calculate_tx_l1_cost_ecotone(input, spec_id)
296 } else {
297 self.calculate_tx_l1_cost_bedrock(input, spec_id)
298 };
299
300 self.tx_l1_cost = Some(tx_l1_cost);
301 tx_l1_cost
302 }
303
304 fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
306 let rollup_data_gas_cost = self.data_gas(input, spec_id);
307 rollup_data_gas_cost
308 .saturating_add(self.l1_fee_overhead.unwrap_or_default())
309 .saturating_mul(self.l1_base_fee)
310 .saturating_mul(self.l1_base_fee_scalar)
311 .wrapping_div(U256::from(1_000_000))
312 }
313
314 fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
325 if self.empty_ecotone_scalars {
329 return self.calculate_tx_l1_cost_bedrock(input, spec_id);
330 }
331
332 let rollup_data_gas_cost = self.data_gas(input, spec_id);
333 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
334
335 l1_fee_scaled
336 .saturating_mul(rollup_data_gas_cost)
337 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
338 }
339
340 fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 {
345 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
346 if l1_fee_scaled.is_zero() {
347 return U256::ZERO;
348 }
349
350 let estimated_size = self.tx_estimated_size_fjord(input);
351
352 estimated_size
353 .saturating_mul(l1_fee_scaled)
354 .wrapping_div(U256::from(1_000_000_000_000u64))
355 }
356
357 fn calculate_l1_fee_scaled_ecotone(&self) -> U256 {
359 let calldata_cost_per_byte = self
360 .l1_base_fee
361 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
362 .saturating_mul(self.l1_base_fee_scalar);
363 let blob_cost_per_byte = self
364 .l1_blob_base_fee
365 .unwrap_or_default()
366 .saturating_mul(self.l1_blob_base_fee_scalar.unwrap_or_default());
367
368 calldata_cost_per_byte.saturating_add(blob_cost_per_byte)
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use revm::primitives::{bytes, hex};
376
377 #[test]
378 fn test_data_gas_non_zero_bytes() {
379 let l1_block_info = L1BlockInfo {
380 l1_base_fee: U256::from(1_000_000),
381 l1_fee_overhead: Some(U256::from(1_000_000)),
382 l1_base_fee_scalar: U256::from(1_000_000),
383 ..Default::default()
384 };
385
386 let input = bytes!("FACADE");
393 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
394 assert_eq!(bedrock_data_gas, U256::from(1136));
395
396 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
399 assert_eq!(regolith_data_gas, U256::from(48));
400
401 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
404 assert_eq!(fjord_data_gas, U256::from(1600));
405 }
406
407 #[test]
408 fn test_data_gas_zero_bytes() {
409 let l1_block_info = L1BlockInfo {
410 l1_base_fee: U256::from(1_000_000),
411 l1_fee_overhead: Some(U256::from(1_000_000)),
412 l1_base_fee_scalar: U256::from(1_000_000),
413 ..Default::default()
414 };
415
416 let input = bytes!("FA00CA00DE");
423 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
424 assert_eq!(bedrock_data_gas, U256::from(1144));
425
426 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
429 assert_eq!(regolith_data_gas, U256::from(56));
430
431 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
434 assert_eq!(fjord_data_gas, U256::from(1600));
435 }
436
437 #[test]
438 fn test_calculate_tx_l1_cost() {
439 let mut l1_block_info = L1BlockInfo {
440 l1_base_fee: U256::from(1_000),
441 l1_fee_overhead: Some(U256::from(1_000)),
442 l1_base_fee_scalar: U256::from(1_000),
443 ..Default::default()
444 };
445
446 let input = bytes!("FACADE");
447 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
448 assert_eq!(gas_cost, U256::from(1048));
449 l1_block_info.clear_tx_l1_cost();
450
451 let input = bytes!("");
453 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
454 assert_eq!(gas_cost, U256::ZERO);
455 l1_block_info.clear_tx_l1_cost();
456
457 let input = bytes!("7EFACADE");
459 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
460 assert_eq!(gas_cost, U256::ZERO);
461 }
462
463 #[test]
464 fn test_calculate_tx_l1_cost_ecotone() {
465 let mut l1_block_info = L1BlockInfo {
466 l1_base_fee: U256::from(1_000),
467 l1_base_fee_scalar: U256::from(1_000),
468 l1_blob_base_fee: Some(U256::from(1_000)),
469 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
470 l1_fee_overhead: Some(U256::from(1_000)),
471 ..Default::default()
472 };
473
474 let input = bytes!("FACADE");
478 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
479 assert_eq!(gas_cost, U256::from(51));
480 l1_block_info.clear_tx_l1_cost();
481
482 let input = bytes!("");
484 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
485 assert_eq!(gas_cost, U256::ZERO);
486 l1_block_info.clear_tx_l1_cost();
487
488 let input = bytes!("7EFACADE");
490 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
491 assert_eq!(gas_cost, U256::ZERO);
492 l1_block_info.clear_tx_l1_cost();
493
494 l1_block_info.empty_ecotone_scalars = true;
496 let input = bytes!("FACADE");
497 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
498 assert_eq!(gas_cost, U256::from(1048));
499 }
500
501 #[test]
502 fn calculate_tx_l1_cost_ecotone() {
503 let l1_block_info = L1BlockInfo {
512 l1_base_fee: U256::from_be_bytes(hex!(
513 "0000000000000000000000000000000000000000000000000000000af39ac327"
514 )), l1_base_fee_scalar: U256::from(1368),
516 l1_blob_base_fee: Some(U256::from_be_bytes(hex!(
517 "0000000000000000000000000000000000000000000000000000000d5ea528d2"
518 ))), l1_blob_base_fee_scalar: Some(U256::from(810949)),
520 ..Default::default()
521 };
522
523 const TX: &[u8] = &hex!("02f8b30a832253fc8402d11f39842c8a46398301388094dc6ff44d5d932cbd77b52e5612ba0529dc6226f180b844a9059cbb000000000000000000000000d43e02db81f4d46cdf8521f623d21ea0ec7562a50000000000000000000000000000000000000000000000008ac7230489e80000c001a02947e24750723b48f886931562c55d9e07f856d8e06468e719755e18bbc3a570a0784da9ce59fd7754ea5be6e17a86b348e441348cd48ace59d174772465eadbd1");
526
527 let expected_l1_gas_used = U256::from(2456);
530 let expected_l1_fee = U256::from_be_bytes(hex!(
531 "000000000000000000000000000000000000000000000000000006a510bd7431" ));
533
534 let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE);
537
538 assert_eq!(gas_used, expected_l1_gas_used);
539
540 let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE);
541
542 assert_eq!(l1_fee, expected_l1_fee)
543 }
544
545 #[test]
546 fn test_calculate_tx_l1_cost_fjord() {
547 let mut l1_block_info = L1BlockInfo {
551 l1_base_fee: U256::from(1_000),
552 l1_base_fee_scalar: U256::from(1_000),
553 l1_blob_base_fee: Some(U256::from(1_000)),
554 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
555 ..Default::default()
556 };
557
558 let input = bytes!("FACADE");
563 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
567 assert_eq!(gas_cost, U256::from(1700));
568 l1_block_info.clear_tx_l1_cost();
569
570 let input = bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e");
575 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
579 assert_eq!(gas_cost, U256::from(2148));
580 l1_block_info.clear_tx_l1_cost();
581
582 let input = bytes!("");
584 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
585 assert_eq!(gas_cost, U256::ZERO);
586 l1_block_info.clear_tx_l1_cost();
587
588 let input = bytes!("7EFACADE");
590 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
591 assert_eq!(gas_cost, U256::ZERO);
592 }
593
594 #[test]
595 fn calculate_tx_l1_cost_fjord() {
596 let l1_block_info = L1BlockInfo {
601 l1_base_fee: U256::from(1055991687),
602 l1_base_fee_scalar: U256::from(5227),
603 l1_blob_base_fee_scalar: Some(U256::from(1014213)),
604 l1_blob_base_fee: Some(U256::from(1)),
605 ..Default::default() };
607
608 const TX: &[u8] = &hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
611
612 let expected_data_gas = U256::from(4471);
615 let expected_l1_fee = U256::from_be_bytes(hex!(
616 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
617 ));
618
619 let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD);
622
623 assert_eq!(data_gas, expected_data_gas);
624
625 let l1_fee = l1_block_info.calculate_tx_l1_cost_fjord(TX);
626
627 assert_eq!(l1_fee, expected_l1_fee)
628 }
629
630 #[test]
631 fn test_operator_fee_charge_formulas() {
632 let l1_block_info = L1BlockInfo {
633 operator_fee_scalar: Some(U256::from(1_000u64)),
634 operator_fee_constant: Some(U256::from(10u64)),
635 ..Default::default()
636 };
637
638 let input = [0x01u8];
639
640 let isthmus_fee =
641 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::ISTHMUS);
642 assert_eq!(isthmus_fee, U256::from(11u64));
643
644 let jovian_fee =
645 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::JOVIAN);
646 assert_eq!(jovian_fee, U256::from(100_000_010u64));
647 }
648
649 #[test]
650 fn test_operator_fee_refund() {
651 let gas = Gas::new(50000);
652
653 let l1_block_info = L1BlockInfo {
654 l1_base_fee: U256::from(1055991687),
655 l1_base_fee_scalar: U256::from(5227),
656 operator_fee_scalar: Some(U256::from(2000)),
657 operator_fee_constant: Some(U256::from(5)),
658 ..Default::default()
659 };
660
661 let refunded = l1_block_info.operator_fee_refund(&gas, OpSpecId::ISTHMUS);
662
663 assert_eq!(refunded, U256::from(100))
664 }
665}