reth_optimism_evm/
l1.rs

1//! Optimism-specific implementation and utilities for the executor
2
3use crate::{error::L1BlockInfoError, revm_spec_by_timestamp_after_bedrock, OpBlockExecutionError};
4use alloy_consensus::Transaction;
5use alloy_primitives::{hex, U16, U256};
6use op_revm::L1BlockInfo;
7use reth_execution_errors::BlockExecutionError;
8use reth_optimism_forks::OpHardforks;
9use reth_primitives_traits::BlockBody;
10
11/// The function selector of the "setL1BlockValuesEcotone" function in the `L1Block` contract.
12const L1_BLOCK_ECOTONE_SELECTOR: [u8; 4] = hex!("440a5e20");
13
14/// The function selector of the "setL1BlockValuesIsthmus" function in the `L1Block` contract.
15const L1_BLOCK_ISTHMUS_SELECTOR: [u8; 4] = hex!("098999be");
16
17/// The function selector of the "setL1BlockValuesJovian" function in the `L1Block` contract.
18/// This is the first 4 bytes of `keccak256("setL1BlockValuesJovian()")`.
19const L1_BLOCK_JOVIAN_SELECTOR: [u8; 4] = hex!("3db6be2b");
20
21/// Extracts the [`L1BlockInfo`] from the L2 block. The L1 info transaction is always the first
22/// transaction in the L2 block.
23///
24/// Returns an error if the L1 info transaction is not found, if the block is empty.
25pub fn extract_l1_info<B: BlockBody>(body: &B) -> Result<L1BlockInfo, OpBlockExecutionError> {
26    let l1_info_tx = body
27        .transactions()
28        .first()
29        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::MissingTransaction))?;
30    extract_l1_info_from_tx(l1_info_tx)
31}
32
33/// Extracts the [`L1BlockInfo`] from the L1 info transaction (first transaction) in the L2
34/// block.
35///
36/// Returns an error if the calldata is shorter than 4 bytes.
37pub fn extract_l1_info_from_tx<T: Transaction>(
38    tx: &T,
39) -> Result<L1BlockInfo, OpBlockExecutionError> {
40    let l1_info_tx_data = tx.input();
41    if l1_info_tx_data.len() < 4 {
42        return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::InvalidCalldata));
43    }
44
45    parse_l1_info(l1_info_tx_data)
46}
47
48/// Parses the input of the first transaction in the L2 block, into [`L1BlockInfo`].
49///
50/// Returns an error if data is incorrect length.
51///
52/// Caution this expects that the input is the calldata of the [`L1BlockInfo`] transaction (first
53/// transaction) in the L2 block.
54///
55/// # Panics
56/// If the input is shorter than 4 bytes.
57pub fn parse_l1_info(input: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
58    // Parse the L1 info transaction into an L1BlockInfo struct, depending on the function selector.
59    // There are currently 4 variants:
60    // - Jovian
61    // - Isthmus
62    // - Ecotone
63    // - Bedrock
64    if input[0..4] == L1_BLOCK_JOVIAN_SELECTOR {
65        parse_l1_info_tx_jovian(input[4..].as_ref())
66    } else if input[0..4] == L1_BLOCK_ISTHMUS_SELECTOR {
67        parse_l1_info_tx_isthmus(input[4..].as_ref())
68    } else if input[0..4] == L1_BLOCK_ECOTONE_SELECTOR {
69        parse_l1_info_tx_ecotone(input[4..].as_ref())
70    } else {
71        parse_l1_info_tx_bedrock(input[4..].as_ref())
72    }
73}
74
75/// Parses the calldata of the [`L1BlockInfo`] transaction pre-Ecotone hardfork.
76pub fn parse_l1_info_tx_bedrock(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
77    // The setL1BlockValues tx calldata must be exactly 260 bytes long, considering that
78    // we already removed the first 4 bytes (the function selector). Detailed breakdown:
79    //   32 bytes for the block number
80    // + 32 bytes for the block timestamp
81    // + 32 bytes for the base fee
82    // + 32 bytes for the block hash
83    // + 32 bytes for the block sequence number
84    // + 32 bytes for the batcher hash
85    // + 32 bytes for the fee overhead
86    // + 32 bytes for the fee scalar
87    if data.len() != 256 {
88        return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::UnexpectedCalldataLength));
89    }
90
91    let l1_base_fee = U256::try_from_be_slice(&data[64..96])
92        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeConversion))?;
93    let l1_fee_overhead = U256::try_from_be_slice(&data[192..224])
94        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::FeeOverheadConversion))?;
95    let l1_fee_scalar = U256::try_from_be_slice(&data[224..256])
96        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::FeeScalarConversion))?;
97
98    Ok(L1BlockInfo {
99        l1_base_fee,
100        l1_fee_overhead: Some(l1_fee_overhead),
101        l1_base_fee_scalar: l1_fee_scalar,
102        ..Default::default()
103    })
104}
105
106/// Updates the L1 block values for an Ecotone upgraded chain.
107/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
108/// Params are expected to be in the following order:
109///   1. _baseFeeScalar      L1 base fee scalar
110///   2. _blobBaseFeeScalar  L1 blob base fee scalar
111///   3. _sequenceNumber     Number of L2 blocks since epoch start.
112///   4. _timestamp          L1 timestamp.
113///   5. _number             L1 blocknumber.
114///   6. _basefee            L1 base fee.
115///   7. _blobBaseFee        L1 blob base fee.
116///   8. _hash               L1 blockhash.
117///   9. _batcherHash        Versioned hash to authenticate batcher by.
118///
119/// <https://github.com/ethereum-optimism/optimism/blob/957e13dd504fb336a4be40fb5dd0d8ba0276be34/packages/contracts-bedrock/src/L2/L1Block.sol#L136>
120pub fn parse_l1_info_tx_ecotone(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
121    if data.len() != 160 {
122        return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::UnexpectedCalldataLength));
123    }
124
125    // https://github.com/ethereum-optimism/op-geth/blob/60038121c7571a59875ff9ed7679c48c9f73405d/core/types/rollup_cost.go#L317-L328
126    //
127    // data layout assumed for Ecotone:
128    // offset type varname
129    // 0     <selector>
130    // 4     uint32 _basefeeScalar (start offset in this scope)
131    // 8     uint32 _blobBaseFeeScalar
132    // 12    uint64 _sequenceNumber,
133    // 20    uint64 _timestamp,
134    // 28    uint64 _l1BlockNumber
135    // 36    uint256 _basefee,
136    // 68    uint256 _blobBaseFee,
137    // 100   bytes32 _hash,
138    // 132   bytes32 _batcherHash,
139
140    let l1_base_fee_scalar = U256::try_from_be_slice(&data[..4])
141        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeScalarConversion))?;
142    let l1_blob_base_fee_scalar = U256::try_from_be_slice(&data[4..8]).ok_or({
143        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeScalarConversion)
144    })?;
145    let l1_base_fee = U256::try_from_be_slice(&data[32..64])
146        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeConversion))?;
147    let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96])
148        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeConversion))?;
149
150    Ok(L1BlockInfo {
151        l1_base_fee,
152        l1_base_fee_scalar,
153        l1_blob_base_fee: Some(l1_blob_base_fee),
154        l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
155        ..Default::default()
156    })
157}
158
159/// Updates the L1 block values for an Isthmus upgraded chain.
160/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
161/// Params are expected to be in the following order:
162///   1. _baseFeeScalar       L1 base fee scalar
163///   2. _blobBaseFeeScalar   L1 blob base fee scalar
164///   3. _sequenceNumber      Number of L2 blocks since epoch start.
165///   4. _timestamp           L1 timestamp.
166///   5. _number              L1 blocknumber.
167///   6. _basefee             L1 base fee.
168///   7. _blobBaseFee         L1 blob base fee.
169///   8. _hash                L1 blockhash.
170///   9. _batcherHash         Versioned hash to authenticate batcher by.
171///  10. _operatorFeeScalar   Operator fee scalar
172///  11. _operatorFeeConstant Operator fee constant
173pub fn parse_l1_info_tx_isthmus(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
174    if data.len() != 172 {
175        return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::UnexpectedCalldataLength));
176    }
177
178    // https://github.com/ethereum-optimism/op-geth/blob/60038121c7571a59875ff9ed7679c48c9f73405d/core/types/rollup_cost.go#L317-L328
179    //
180    // data layout assumed for Ecotone:
181    // offset type varname
182    // 0     <selector>
183    // 4     uint32 _basefeeScalar (start offset in this scope)
184    // 8     uint32 _blobBaseFeeScalar
185    // 12    uint64 _sequenceNumber,
186    // 20    uint64 _timestamp,
187    // 28    uint64 _l1BlockNumber
188    // 36    uint256 _basefee,
189    // 68    uint256 _blobBaseFee,
190    // 100   bytes32 _hash,
191    // 132   bytes32 _batcherHash,
192    // 164   uint32 _operatorFeeScalar
193    // 168   uint64 _operatorFeeConstant
194
195    let l1_base_fee_scalar = U256::try_from_be_slice(&data[..4])
196        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeScalarConversion))?;
197    let l1_blob_base_fee_scalar = U256::try_from_be_slice(&data[4..8]).ok_or({
198        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeScalarConversion)
199    })?;
200    let l1_base_fee = U256::try_from_be_slice(&data[32..64])
201        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeConversion))?;
202    let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96])
203        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeConversion))?;
204    let operator_fee_scalar = U256::try_from_be_slice(&data[160..164]).ok_or({
205        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeScalarConversion)
206    })?;
207    let operator_fee_constant = U256::try_from_be_slice(&data[164..172]).ok_or({
208        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeConstantConversion)
209    })?;
210
211    Ok(L1BlockInfo {
212        l1_base_fee,
213        l1_base_fee_scalar,
214        l1_blob_base_fee: Some(l1_blob_base_fee),
215        l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
216        operator_fee_scalar: Some(operator_fee_scalar),
217        operator_fee_constant: Some(operator_fee_constant),
218        ..Default::default()
219    })
220}
221
222/// Updates the L1 block values for an Jovian upgraded chain.
223/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
224/// Params are expected to be in the following order:
225///   1. _baseFeeScalar       L1 base fee scalar
226///   2. _blobBaseFeeScalar   L1 blob base fee scalar
227///   3. _sequenceNumber      Number of L2 blocks since epoch start.
228///   4. _timestamp           L1 timestamp.
229///   5. _number              L1 blocknumber.
230///   6. _basefee             L1 base fee.
231///   7. _blobBaseFee         L1 blob base fee.
232///   8. _hash                L1 blockhash.
233///   9. _batcherHash         Versioned hash to authenticate batcher by.
234///  10. _operatorFeeScalar   Operator fee scalar
235///  11. _operatorFeeConstant Operator fee constant
236///  12. _daFootprintGasScalar DA footprint gas scalar
237pub fn parse_l1_info_tx_jovian(data: &[u8]) -> Result<L1BlockInfo, OpBlockExecutionError> {
238    if data.len() != 174 {
239        return Err(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::UnexpectedCalldataLength));
240    }
241
242    // https://github.com/ethereum-optimism/op-geth/blob/60038121c7571a59875ff9ed7679c48c9f73405d/core/types/rollup_cost.go#L317-L328
243    //
244    // data layout assumed for Ecotone:
245    // offset type varname
246    // 0     <selector>
247    // 4     uint32 _basefeeScalar (start offset in this scope)
248    // 8     uint32 _blobBaseFeeScalar
249    // 12    uint64 _sequenceNumber,
250    // 20    uint64 _timestamp,
251    // 28    uint64 _l1BlockNumber
252    // 36    uint256 _basefee,
253    // 68    uint256 _blobBaseFee,
254    // 100   bytes32 _hash,
255    // 132   bytes32 _batcherHash,
256    // 164   uint32 _operatorFeeScalar
257    // 168   uint64 _operatorFeeConstant
258    // 176   uint16 _daFootprintGasScalar
259
260    let l1_base_fee_scalar = U256::try_from_be_slice(&data[..4])
261        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeScalarConversion))?;
262    let l1_blob_base_fee_scalar = U256::try_from_be_slice(&data[4..8]).ok_or({
263        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeScalarConversion)
264    })?;
265    let l1_base_fee = U256::try_from_be_slice(&data[32..64])
266        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BaseFeeConversion))?;
267    let l1_blob_base_fee = U256::try_from_be_slice(&data[64..96])
268        .ok_or(OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::BlobBaseFeeConversion))?;
269    let operator_fee_scalar = U256::try_from_be_slice(&data[160..164]).ok_or({
270        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeScalarConversion)
271    })?;
272    let operator_fee_constant = U256::try_from_be_slice(&data[164..172]).ok_or({
273        OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::OperatorFeeConstantConversion)
274    })?;
275    let da_footprint_gas_scalar: u16 = U16::try_from_be_slice(&data[172..174])
276        .ok_or({
277            OpBlockExecutionError::L1BlockInfo(L1BlockInfoError::DaFootprintGasScalarConversion)
278        })?
279        .to();
280
281    Ok(L1BlockInfo {
282        l1_base_fee,
283        l1_base_fee_scalar,
284        l1_blob_base_fee: Some(l1_blob_base_fee),
285        l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar),
286        operator_fee_scalar: Some(operator_fee_scalar),
287        operator_fee_constant: Some(operator_fee_constant),
288        da_footprint_gas_scalar: Some(da_footprint_gas_scalar),
289        ..Default::default()
290    })
291}
292
293/// An extension trait for [`L1BlockInfo`] that allows us to calculate the L1 cost of a transaction
294/// based off of the chain spec's activated hardfork.
295pub trait RethL1BlockInfo {
296    /// Forwards an L1 transaction calculation to revm and returns the gas cost.
297    ///
298    /// ### Takes
299    /// - `chain_spec`: The chain spec for the node.
300    /// - `timestamp`: The timestamp of the current block.
301    /// - `input`: The calldata of the transaction.
302    /// - `is_deposit`: Whether or not the transaction is a deposit.
303    fn l1_tx_data_fee(
304        &mut self,
305        chain_spec: impl OpHardforks,
306        timestamp: u64,
307        input: &[u8],
308        is_deposit: bool,
309    ) -> Result<U256, BlockExecutionError>;
310
311    /// Computes the data gas cost for an L2 transaction.
312    ///
313    /// ### Takes
314    /// - `chain_spec`: The chain spec for the node.
315    /// - `timestamp`: The timestamp of the current block.
316    /// - `input`: The calldata of the transaction.
317    fn l1_data_gas(
318        &self,
319        chain_spec: impl OpHardforks,
320        timestamp: u64,
321        input: &[u8],
322    ) -> Result<U256, BlockExecutionError>;
323}
324
325impl RethL1BlockInfo for L1BlockInfo {
326    fn l1_tx_data_fee(
327        &mut self,
328        chain_spec: impl OpHardforks,
329        timestamp: u64,
330        input: &[u8],
331        is_deposit: bool,
332    ) -> Result<U256, BlockExecutionError> {
333        if is_deposit {
334            return Ok(U256::ZERO);
335        }
336
337        let spec_id = revm_spec_by_timestamp_after_bedrock(&chain_spec, timestamp);
338        Ok(self.calculate_tx_l1_cost(input, spec_id))
339    }
340
341    fn l1_data_gas(
342        &self,
343        chain_spec: impl OpHardforks,
344        timestamp: u64,
345        input: &[u8],
346    ) -> Result<U256, BlockExecutionError> {
347        let spec_id = revm_spec_by_timestamp_after_bedrock(&chain_spec, timestamp);
348        Ok(self.data_gas(input, spec_id))
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355    use alloy_consensus::{Block, BlockBody};
356    use alloy_eips::eip2718::Decodable2718;
357    use alloy_primitives::keccak256;
358    use reth_optimism_chainspec::OP_MAINNET;
359    use reth_optimism_forks::OpHardforks;
360    use reth_optimism_primitives::OpTransactionSigned;
361
362    #[test]
363    fn sanity_l1_block() {
364        use alloy_consensus::Header;
365        use alloy_primitives::{hex_literal::hex, Bytes};
366
367        let bytes = Bytes::from_static(&hex!(
368            "7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"
369        ));
370        let l1_info_tx = OpTransactionSigned::decode_2718(&mut bytes.as_ref()).unwrap();
371        let mock_block = Block {
372            header: Header::default(),
373            body: BlockBody { transactions: vec![l1_info_tx], ..Default::default() },
374        };
375
376        let l1_info: L1BlockInfo = extract_l1_info(&mock_block.body).unwrap();
377        assert_eq!(l1_info.l1_base_fee, U256::from(652_114));
378        assert_eq!(l1_info.l1_fee_overhead, Some(U256::from(2100)));
379        assert_eq!(l1_info.l1_base_fee_scalar, U256::from(1_000_000));
380        assert_eq!(l1_info.l1_blob_base_fee, None);
381        assert_eq!(l1_info.l1_blob_base_fee_scalar, None);
382    }
383
384    #[test]
385    fn test_verify_set_jovian() {
386        let hash = &keccak256("setL1BlockValuesJovian()")[..4];
387        assert_eq!(hash, L1_BLOCK_JOVIAN_SELECTOR)
388    }
389
390    #[test]
391    fn sanity_l1_block_ecotone() {
392        // rig
393
394        // OP mainnet ecotone block 118024092
395        // <https://optimistic.etherscan.io/block/118024092>
396        const TIMESTAMP: u64 = 1711603765;
397        assert!(OP_MAINNET.is_ecotone_active_at_timestamp(TIMESTAMP));
398
399        // First transaction in OP mainnet block 118024092
400        //
401        // https://optimistic.etherscan.io/getRawTx?tx=0x88501da5d5ca990347c2193be90a07037af1e3820bb40774c8154871c7669150
402        const TX: [u8; 251] = hex!(
403            "7ef8f8a0a539eb753df3b13b7e386e147d45822b67cb908c9ddc5618e3dbaa22ed00850b94deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e2000000558000c5fc50000000000000000000000006605a89f00000000012a10d90000000000000000000000000000000000000000000000000000000af39ac3270000000000000000000000000000000000000000000000000000000d5ea528d24e582fa68786f080069bdbfe06a43f8e67bfd31b8e4d8a8837ba41da9a82a54a0000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"
404        );
405
406        let tx = OpTransactionSigned::decode_2718(&mut TX.as_slice()).unwrap();
407        let block: Block<OpTransactionSigned> = Block {
408            body: BlockBody { transactions: vec![tx], ..Default::default() },
409            ..Default::default()
410        };
411
412        // expected l1 block info
413        let expected_l1_base_fee = U256::from_be_bytes(hex!(
414            "0000000000000000000000000000000000000000000000000000000af39ac327" // 47036678951
415        ));
416        let expected_l1_base_fee_scalar = U256::from(1368);
417        let expected_l1_blob_base_fee = U256::from_be_bytes(hex!(
418            "0000000000000000000000000000000000000000000000000000000d5ea528d2" // 57422457042
419        ));
420        let expected_l1_blob_base_fee_scalar = U256::from(810949);
421
422        // test
423
424        let l1_block_info: L1BlockInfo = extract_l1_info(&block.body).unwrap();
425
426        assert_eq!(l1_block_info.l1_base_fee, expected_l1_base_fee);
427        assert_eq!(l1_block_info.l1_base_fee_scalar, expected_l1_base_fee_scalar);
428        assert_eq!(l1_block_info.l1_blob_base_fee, Some(expected_l1_blob_base_fee));
429        assert_eq!(l1_block_info.l1_blob_base_fee_scalar, Some(expected_l1_blob_base_fee_scalar));
430    }
431
432    #[test]
433    fn parse_l1_info_fjord() {
434        // rig
435
436        // L1 block info for OP mainnet block 124665056 (stored in input of tx at index 0)
437        //
438        // https://optimistic.etherscan.io/tx/0x312e290cf36df704a2217b015d6455396830b0ce678b860ebfcc30f41403d7b1
439        const DATA: &[u8] = &hex!(
440            "440a5e200000146b000f79c500000000000000040000000066d052e700000000013ad8a3000000000000000000000000000000000000000000000000000000003ef1278700000000000000000000000000000000000000000000000000000000000000012fdf87b89884a61e74b322bbcf60386f543bfae7827725efaaf0ab1de2294a590000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"
441        );
442
443        // expected l1 block info verified against expected l1 fee for tx. l1 tx fee listed on OP
444        // mainnet block scanner
445        //
446        // https://github.com/bluealloy/revm/blob/fa5650ee8a4d802f4f3557014dd157adfb074460/crates/revm/src/optimism/l1block.rs#L414-L443
447        let l1_base_fee = U256::from(1055991687);
448        let l1_base_fee_scalar = U256::from(5227);
449        let l1_blob_base_fee = Some(U256::from(1));
450        let l1_blob_base_fee_scalar = Some(U256::from(1014213));
451
452        // test
453
454        let l1_block_info = parse_l1_info(DATA).unwrap();
455
456        assert_eq!(l1_block_info.l1_base_fee, l1_base_fee);
457        assert_eq!(l1_block_info.l1_base_fee_scalar, l1_base_fee_scalar);
458        assert_eq!(l1_block_info.l1_blob_base_fee, l1_blob_base_fee);
459        assert_eq!(l1_block_info.l1_blob_base_fee_scalar, l1_blob_base_fee_scalar);
460    }
461
462    #[test]
463    fn parse_l1_info_isthmus() {
464        // rig
465
466        // L1 block info from a devnet with Isthmus activated
467        const DATA: &[u8] = &hex!(
468            "098999be00000558000c5fc500000000000000030000000067a9f765000000000000002900000000000000000000000000000000000000000000000000000000006a6d09000000000000000000000000000000000000000000000000000000000000000172fcc8e8886636bdbe96ba0e4baab67ea7e7811633f52b52e8cf7a5123213b6f000000000000000000000000d3f2c5afb2d76f5579f326b0cd7da5f5a4126c3500004e2000000000000001f4"
469        );
470
471        // expected l1 block info verified against expected l1 fee and operator fee for tx.
472        let l1_base_fee = U256::from(6974729);
473        let l1_base_fee_scalar = U256::from(1368);
474        let l1_blob_base_fee = Some(U256::from(1));
475        let l1_blob_base_fee_scalar = Some(U256::from(810949));
476        let operator_fee_scalar = Some(U256::from(20000));
477        let operator_fee_constant = Some(U256::from(500));
478
479        // test
480
481        let l1_block_info = parse_l1_info(DATA).unwrap();
482
483        assert_eq!(l1_block_info.l1_base_fee, l1_base_fee);
484        assert_eq!(l1_block_info.l1_base_fee_scalar, l1_base_fee_scalar);
485        assert_eq!(l1_block_info.l1_blob_base_fee, l1_blob_base_fee);
486        assert_eq!(l1_block_info.l1_blob_base_fee_scalar, l1_blob_base_fee_scalar);
487        assert_eq!(l1_block_info.operator_fee_scalar, operator_fee_scalar);
488        assert_eq!(l1_block_info.operator_fee_constant, operator_fee_constant);
489    }
490
491    #[test]
492    fn parse_l1_info_jovian() {
493        // L1 block info from a devnet with Isthmus activated
494        const DATA: &[u8] = &hex!(
495            "3db6be2b00000558000c5fc500000000000000030000000067a9f765000000000000002900000000000000000000000000000000000000000000000000000000006a6d09000000000000000000000000000000000000000000000000000000000000000172fcc8e8886636bdbe96ba0e4baab67ea7e7811633f52b52e8cf7a5123213b6f000000000000000000000000d3f2c5afb2d76f5579f326b0cd7da5f5a4126c3500004e2000000000000001f4dead"
496        );
497
498        // expected l1 block info verified against expected l1 fee and operator fee for tx.
499        let l1_base_fee = U256::from(6974729);
500        let l1_base_fee_scalar = U256::from(1368);
501        let l1_blob_base_fee = Some(U256::from(1));
502        let l1_blob_base_fee_scalar = Some(U256::from(810949));
503        let operator_fee_scalar = Some(U256::from(20000));
504        let operator_fee_constant = Some(U256::from(500));
505        let da_footprint_gas_scalar: Option<u16> = Some(U16::from(0xdead).to());
506
507        // test
508
509        let l1_block_info = parse_l1_info(DATA).unwrap();
510
511        assert_eq!(l1_block_info.l1_base_fee, l1_base_fee);
512        assert_eq!(l1_block_info.l1_base_fee_scalar, l1_base_fee_scalar);
513        assert_eq!(l1_block_info.l1_blob_base_fee, l1_blob_base_fee);
514        assert_eq!(l1_block_info.l1_blob_base_fee_scalar, l1_blob_base_fee_scalar);
515        assert_eq!(l1_block_info.operator_fee_scalar, operator_fee_scalar);
516        assert_eq!(l1_block_info.operator_fee_constant, operator_fee_constant);
517        assert_eq!(l1_block_info.da_footprint_gas_scalar, da_footprint_gas_scalar);
518    }
519}