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::{hardfork::SpecId, 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 if spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN) {
144 let _ = db.basic(L1_BLOCK_CONTRACT)?;
145 }
146
147 let mut out = L1BlockInfo {
148 l2_block: Some(l2_block),
149 l1_base_fee: db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?,
150 ..Default::default()
151 };
152
153 if !spec_id.is_enabled_in(OpSpecId::ECOTONE) {
155 out.l1_base_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?;
156 out.l1_fee_overhead = Some(db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?);
157
158 return Ok(out);
159 }
160
161 out.try_fetch_ecotone(db)?;
162
163 if spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
165 out.try_fetch_isthmus(db)?;
166 }
167
168 if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
170 out.try_fetch_jovian(db)?;
171 }
172
173 Ok(out)
174 }
175
176 pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256, spec_id: OpSpecId) -> U256 {
180 if input.is_empty() || input.first() == Some(&0x7E) {
182 return U256::ZERO;
183 }
184
185 self.operator_fee_charge_inner(gas_limit, spec_id)
186 }
187
188 fn operator_fee_charge_inner(&self, gas: U256, spec_id: OpSpecId) -> U256 {
190 let operator_fee_scalar = self
191 .operator_fee_scalar
192 .expect("Missing operator fee scalar for isthmus L1 Block");
193 let operator_fee_constant = self
194 .operator_fee_constant
195 .expect("Missing operator fee constant for isthmus L1 Block");
196
197 let product = if spec_id.is_enabled_in(OpSpecId::JOVIAN) {
198 gas.saturating_mul(operator_fee_scalar)
199 .saturating_mul(U256::from(OPERATOR_FEE_JOVIAN_MULTIPLIER))
200 } else {
201 gas.saturating_mul(operator_fee_scalar) / U256::from(OPERATOR_FEE_SCALAR_DECIMAL)
202 };
203
204 product.saturating_add(operator_fee_constant)
205 }
206
207 pub fn operator_fee_refund(&self, gas: &Gas, spec_id: OpSpecId) -> U256 {
211 if !spec_id.is_enabled_in(OpSpecId::ISTHMUS) {
212 return U256::ZERO;
213 }
214
215 let operator_cost_gas_limit =
216 self.operator_fee_charge_inner(U256::from(gas.limit()), spec_id);
217 let operator_cost_gas_used = self.operator_fee_charge_inner(
218 U256::from(gas.limit() - (gas.remaining() + gas.refunded() as u64)),
219 spec_id,
220 );
221
222 operator_cost_gas_limit.saturating_sub(operator_cost_gas_used)
223 }
224
225 pub fn data_gas(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
233 if spec_id.is_enabled_in(OpSpecId::FJORD) {
234 let estimated_size = self.tx_estimated_size_fjord(input);
235
236 return estimated_size
237 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
238 .wrapping_div(U256::from(1_000_000));
239 };
240
241 let mut tokens_in_transaction_data = get_tokens_in_calldata(input, true);
243
244 if !spec_id.is_enabled_in(OpSpecId::REGOLITH) {
246 tokens_in_transaction_data += 68 * NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
247 }
248
249 U256::from(tokens_in_transaction_data.saturating_mul(STANDARD_TOKEN_COST))
250 }
251
252 fn tx_estimated_size_fjord(&self, input: &[u8]) -> U256 {
256 U256::from(estimate_tx_compressed_size(input))
257 }
258
259 pub fn clear_tx_l1_cost(&mut self) {
261 self.tx_l1_cost = None;
262 }
263
264 #[track_caller]
268 pub fn tx_cost_with_tx(&mut self, tx: impl OpTxTr, spec: OpSpecId) -> U256 {
269 let enveloped_tx = tx
271 .enveloped_tx()
272 .expect("all not deposit tx have enveloped tx")
273 .clone();
274 let gas_limit = U256::from(tx.gas_limit());
275 self.tx_cost(&enveloped_tx, gas_limit, spec)
276 }
277
278 #[inline]
280 pub fn tx_cost(&mut self, enveloped_tx: &[u8], gas_limit: U256, spec: OpSpecId) -> U256 {
281 let mut additional_cost = self.calculate_tx_l1_cost(enveloped_tx, spec);
283
284 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
286 let operator_fee_charge = self.operator_fee_charge(enveloped_tx, gas_limit, spec);
287 additional_cost = additional_cost.saturating_add(operator_fee_charge);
288 }
289
290 additional_cost
291 }
292
293 pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 {
295 if let Some(tx_l1_cost) = self.tx_l1_cost {
296 return tx_l1_cost;
297 }
298 let tx_l1_cost = if input.is_empty() || input.first() == Some(&0x7E) {
300 return U256::ZERO;
301 } else if spec_id.is_enabled_in(OpSpecId::FJORD) {
302 self.calculate_tx_l1_cost_fjord(input)
303 } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) {
304 self.calculate_tx_l1_cost_ecotone(input, spec_id)
305 } else {
306 self.calculate_tx_l1_cost_bedrock(input, spec_id)
307 };
308
309 self.tx_l1_cost = Some(tx_l1_cost);
310 tx_l1_cost
311 }
312
313 fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
315 let rollup_data_gas_cost = self.data_gas(input, spec_id);
316 rollup_data_gas_cost
317 .saturating_add(self.l1_fee_overhead.unwrap_or_default())
318 .saturating_mul(self.l1_base_fee)
319 .saturating_mul(self.l1_base_fee_scalar)
320 .wrapping_div(U256::from(1_000_000))
321 }
322
323 fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpecId) -> U256 {
334 if self.empty_ecotone_scalars {
338 return self.calculate_tx_l1_cost_bedrock(input, spec_id);
339 }
340
341 let rollup_data_gas_cost = self.data_gas(input, spec_id);
342 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
343
344 l1_fee_scaled
345 .saturating_mul(rollup_data_gas_cost)
346 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
347 }
348
349 fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 {
354 let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone();
355 let estimated_size = self.tx_estimated_size_fjord(input);
356
357 estimated_size
358 .saturating_mul(l1_fee_scaled)
359 .wrapping_div(U256::from(1_000_000_000_000u64))
360 }
361
362 fn calculate_l1_fee_scaled_ecotone(&self) -> U256 {
364 let calldata_cost_per_byte = self
365 .l1_base_fee
366 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
367 .saturating_mul(self.l1_base_fee_scalar);
368 let blob_cost_per_byte = self
369 .l1_blob_base_fee
370 .unwrap_or_default()
371 .saturating_mul(self.l1_blob_base_fee_scalar.unwrap_or_default());
372
373 calldata_cost_per_byte.saturating_add(blob_cost_per_byte)
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380 use revm::primitives::{bytes, hex};
381
382 #[test]
383 fn test_data_gas_non_zero_bytes() {
384 let l1_block_info = L1BlockInfo {
385 l1_base_fee: U256::from(1_000_000),
386 l1_fee_overhead: Some(U256::from(1_000_000)),
387 l1_base_fee_scalar: U256::from(1_000_000),
388 ..Default::default()
389 };
390
391 let input = bytes!("FACADE");
398 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
399 assert_eq!(bedrock_data_gas, U256::from(1136));
400
401 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
404 assert_eq!(regolith_data_gas, U256::from(48));
405
406 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
409 assert_eq!(fjord_data_gas, U256::from(1600));
410 }
411
412 #[test]
413 fn test_data_gas_zero_bytes() {
414 let l1_block_info = L1BlockInfo {
415 l1_base_fee: U256::from(1_000_000),
416 l1_fee_overhead: Some(U256::from(1_000_000)),
417 l1_base_fee_scalar: U256::from(1_000_000),
418 ..Default::default()
419 };
420
421 let input = bytes!("FA00CA00DE");
428 let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK);
429 assert_eq!(bedrock_data_gas, U256::from(1144));
430
431 let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH);
434 assert_eq!(regolith_data_gas, U256::from(56));
435
436 let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD);
439 assert_eq!(fjord_data_gas, U256::from(1600));
440 }
441
442 #[test]
443 fn test_calculate_tx_l1_cost() {
444 let mut l1_block_info = L1BlockInfo {
445 l1_base_fee: U256::from(1_000),
446 l1_fee_overhead: Some(U256::from(1_000)),
447 l1_base_fee_scalar: U256::from(1_000),
448 ..Default::default()
449 };
450
451 let input = bytes!("FACADE");
452 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
453 assert_eq!(gas_cost, U256::from(1048));
454 l1_block_info.clear_tx_l1_cost();
455
456 let input = bytes!("");
458 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
459 assert_eq!(gas_cost, U256::ZERO);
460 l1_block_info.clear_tx_l1_cost();
461
462 let input = bytes!("7EFACADE");
464 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH);
465 assert_eq!(gas_cost, U256::ZERO);
466 }
467
468 #[test]
469 fn test_calculate_tx_l1_cost_ecotone() {
470 let mut l1_block_info = L1BlockInfo {
471 l1_base_fee: U256::from(1_000),
472 l1_base_fee_scalar: U256::from(1_000),
473 l1_blob_base_fee: Some(U256::from(1_000)),
474 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
475 l1_fee_overhead: Some(U256::from(1_000)),
476 ..Default::default()
477 };
478
479 let input = bytes!("FACADE");
483 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
484 assert_eq!(gas_cost, U256::from(51));
485 l1_block_info.clear_tx_l1_cost();
486
487 let input = bytes!("");
489 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
490 assert_eq!(gas_cost, U256::ZERO);
491 l1_block_info.clear_tx_l1_cost();
492
493 let input = bytes!("7EFACADE");
495 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
496 assert_eq!(gas_cost, U256::ZERO);
497 l1_block_info.clear_tx_l1_cost();
498
499 l1_block_info.empty_ecotone_scalars = true;
501 let input = bytes!("FACADE");
502 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE);
503 assert_eq!(gas_cost, U256::from(1048));
504 }
505
506 #[test]
507 fn calculate_tx_l1_cost_ecotone() {
508 let l1_block_info = L1BlockInfo {
517 l1_base_fee: U256::from_be_bytes(hex!(
518 "0000000000000000000000000000000000000000000000000000000af39ac327"
519 )), l1_base_fee_scalar: U256::from(1368),
521 l1_blob_base_fee: Some(U256::from_be_bytes(hex!(
522 "0000000000000000000000000000000000000000000000000000000d5ea528d2"
523 ))), l1_blob_base_fee_scalar: Some(U256::from(810949)),
525 ..Default::default()
526 };
527
528 const TX: &[u8] = &hex!("02f8b30a832253fc8402d11f39842c8a46398301388094dc6ff44d5d932cbd77b52e5612ba0529dc6226f180b844a9059cbb000000000000000000000000d43e02db81f4d46cdf8521f623d21ea0ec7562a50000000000000000000000000000000000000000000000008ac7230489e80000c001a02947e24750723b48f886931562c55d9e07f856d8e06468e719755e18bbc3a570a0784da9ce59fd7754ea5be6e17a86b348e441348cd48ace59d174772465eadbd1");
531
532 let expected_l1_gas_used = U256::from(2456);
535 let expected_l1_fee = U256::from_be_bytes(hex!(
536 "000000000000000000000000000000000000000000000000000006a510bd7431" ));
538
539 let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE);
542
543 assert_eq!(gas_used, expected_l1_gas_used);
544
545 let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE);
546
547 assert_eq!(l1_fee, expected_l1_fee)
548 }
549
550 #[test]
551 fn test_calculate_tx_l1_cost_fjord() {
552 let mut l1_block_info = L1BlockInfo {
556 l1_base_fee: U256::from(1_000),
557 l1_base_fee_scalar: U256::from(1_000),
558 l1_blob_base_fee: Some(U256::from(1_000)),
559 l1_blob_base_fee_scalar: Some(U256::from(1_000)),
560 ..Default::default()
561 };
562
563 let input = bytes!("FACADE");
568 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
572 assert_eq!(gas_cost, U256::from(1700));
573 l1_block_info.clear_tx_l1_cost();
574
575 let input = bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e");
580 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
584 assert_eq!(gas_cost, U256::from(2148));
585 l1_block_info.clear_tx_l1_cost();
586
587 let input = bytes!("");
589 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
590 assert_eq!(gas_cost, U256::ZERO);
591 l1_block_info.clear_tx_l1_cost();
592
593 let input = bytes!("7EFACADE");
595 let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD);
596 assert_eq!(gas_cost, U256::ZERO);
597 }
598
599 #[test]
600 fn calculate_tx_l1_cost_fjord() {
601 let l1_block_info = L1BlockInfo {
606 l1_base_fee: U256::from(1055991687),
607 l1_base_fee_scalar: U256::from(5227),
608 l1_blob_base_fee_scalar: Some(U256::from(1014213)),
609 l1_blob_base_fee: Some(U256::from(1)),
610 ..Default::default() };
612
613 const TX: &[u8] = &hex!("02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e");
616
617 let expected_data_gas = U256::from(4471);
620 let expected_l1_fee = U256::from_be_bytes(hex!(
621 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
622 ));
623
624 let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD);
627
628 assert_eq!(data_gas, expected_data_gas);
629
630 let l1_fee = l1_block_info.calculate_tx_l1_cost_fjord(TX);
631
632 assert_eq!(l1_fee, expected_l1_fee)
633 }
634
635 #[test]
636 fn test_operator_fee_charge_formulas() {
637 let l1_block_info = L1BlockInfo {
638 operator_fee_scalar: Some(U256::from(1_000u64)),
639 operator_fee_constant: Some(U256::from(10u64)),
640 ..Default::default()
641 };
642
643 let input = [0x01u8];
644
645 let isthmus_fee =
646 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::ISTHMUS);
647 assert_eq!(isthmus_fee, U256::from(11u64));
648
649 let jovian_fee =
650 l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::JOVIAN);
651 assert_eq!(jovian_fee, U256::from(100_000_010u64));
652 }
653
654 #[test]
655 fn test_operator_fee_refund() {
656 let gas = Gas::new(50000);
657
658 let l1_block_info = L1BlockInfo {
659 l1_base_fee: U256::from(1055991687),
660 l1_base_fee_scalar: U256::from(5227),
661 operator_fee_scalar: Some(U256::from(2000)),
662 operator_fee_constant: Some(U256::from(5)),
663 ..Default::default()
664 };
665
666 let refunded = l1_block_info.operator_fee_refund(&gas, OpSpecId::ISTHMUS);
667
668 assert_eq!(refunded, U256::from(100))
669 }
670}