superchain_primitives/
rollup_config.rs

1//! Rollup Config Types
2
3use alloy_eips::eip1559::BaseFeeParams;
4use alloy_primitives::{address, b256, uint, Address};
5use anyhow::{anyhow, Result};
6
7use crate::block::BlockID;
8use crate::chain_config::ChainConfig;
9use crate::fee_params::{
10    base_fee_params, canyon_base_fee_params, BASE_SEPOLIA_BASE_FEE_PARAMS,
11    BASE_SEPOLIA_CANYON_BASE_FEE_PARAMS, OP_BASE_FEE_PARAMS, OP_CANYON_BASE_FEE_PARAMS,
12    OP_SEPOLIA_BASE_FEE_PARAMS, OP_SEPOLIA_CANYON_BASE_FEE_PARAMS,
13};
14use crate::genesis::ChainGenesis;
15use crate::system_config::SystemConfig;
16
17/// The max rlp bytes per channel for the Bedrock hardfork.
18pub const MAX_RLP_BYTES_PER_CHANNEL_BEDROCK: u64 = 10_000_000;
19
20/// The max rlp bytes per channel for the Fjord hardfork.
21pub const MAX_RLP_BYTES_PER_CHANNEL_FJORD: u64 = 100_000_000;
22
23/// The max sequencer drift when the Fjord hardfork is active.
24pub const FJORD_MAX_SEQUENCER_DRIFT: u64 = 1800;
25
26/// The channel timeout once the Granite hardfork is active.
27pub const GRANITE_CHANNEL_TIMEOUT: u64 = 50;
28
29#[cfg(feature = "serde")]
30fn default_granite_channel_timeout() -> u64 {
31    GRANITE_CHANNEL_TIMEOUT
32}
33
34/// Returns the rollup config for the given chain ID.
35pub fn rollup_config_from_chain_id(chain_id: u64) -> Result<RollupConfig> {
36    chain_id.try_into()
37}
38
39impl TryFrom<u64> for RollupConfig {
40    type Error = anyhow::Error;
41
42    fn try_from(chain_id: u64) -> Result<RollupConfig> {
43        match chain_id {
44            10 => Ok(OP_MAINNET_CONFIG),
45            11155420 => Ok(OP_SEPOLIA_CONFIG),
46            8453 => Ok(BASE_MAINNET_CONFIG),
47            84532 => Ok(BASE_SEPOLIA_CONFIG),
48            _ => Err(anyhow!("Unknown chain ID")),
49        }
50    }
51}
52
53/// The Rollup configuration.
54#[derive(Debug, Clone, Eq, PartialEq)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct RollupConfig {
57    /// The genesis state of the rollup.
58    pub genesis: ChainGenesis,
59    /// The block time of the L2, in seconds.
60    pub block_time: u64,
61    /// Sequencer batches may not be more than MaxSequencerDrift seconds after
62    /// the L1 timestamp of the sequencing window end.
63    ///
64    /// Note: When L1 has many 1 second consecutive blocks, and L2 grows at fixed 2 seconds,
65    /// the L2 time may still grow beyond this difference.
66    ///
67    /// Note: After the Fjord hardfork, this value becomes a constant of `1800`.
68    pub max_sequencer_drift: u64,
69    /// The sequencer window size.
70    pub seq_window_size: u64,
71    /// Number of L1 blocks between when a channel can be opened and when it can be closed.
72    pub channel_timeout: u64,
73    /// The channel timeout after the Granite hardfork.
74    #[cfg_attr(feature = "serde", serde(default = "default_granite_channel_timeout"))]
75    pub granite_channel_timeout: u64,
76    /// The L1 chain ID
77    pub l1_chain_id: u64,
78    /// The L2 chain ID
79    pub l2_chain_id: u64,
80    /// Base Fee Params
81    #[cfg_attr(feature = "serde", serde(default = "BaseFeeParams::optimism"))]
82    pub base_fee_params: BaseFeeParams,
83    /// Base fee params post-canyon hardfork
84    #[cfg_attr(feature = "serde", serde(default = "BaseFeeParams::optimism_canyon"))]
85    pub canyon_base_fee_params: BaseFeeParams,
86    /// `regolith_time` sets the activation time of the Regolith network-upgrade:
87    /// a pre-mainnet Bedrock change that addresses findings of the Sherlock contest related to
88    /// deposit attributes. "Regolith" is the loose deposited rock that sits on top of Bedrock.
89    /// Active if regolith_time != None && L2 block timestamp >= Some(regolith_time), inactive
90    /// otherwise.
91    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
92    pub regolith_time: Option<u64>,
93    /// `canyon_time` sets the activation time of the Canyon network upgrade.
94    /// Active if `canyon_time` != None && L2 block timestamp >= Some(canyon_time), inactive
95    /// otherwise.
96    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
97    pub canyon_time: Option<u64>,
98    /// `delta_time` sets the activation time of the Delta network upgrade.
99    /// Active if `delta_time` != None && L2 block timestamp >= Some(delta_time), inactive
100    /// otherwise.
101    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
102    pub delta_time: Option<u64>,
103    /// `ecotone_time` sets the activation time of the Ecotone network upgrade.
104    /// Active if `ecotone_time` != None && L2 block timestamp >= Some(ecotone_time), inactive
105    /// otherwise.
106    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
107    pub ecotone_time: Option<u64>,
108    /// `fjord_time` sets the activation time of the Fjord network upgrade.
109    /// Active if `fjord_time` != None && L2 block timestamp >= Some(fjord_time), inactive
110    /// otherwise.
111    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
112    pub fjord_time: Option<u64>,
113    /// `granite_time` sets the activation time for the Granite network upgrade.
114    /// Active if `granite_time` != None && L2 block timestamp >= Some(granite_time), inactive
115    /// otherwise.
116    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
117    pub granite_time: Option<u64>,
118    /// `holocene_time` sets the activation time for the Holocene network upgrade.
119    /// Active if `holocene_time` != None && L2 block timestamp >= Some(holocene_time), inactive
120    /// otherwise.
121    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122    pub holocene_time: Option<u64>,
123    /// `batch_inbox_address` is the L1 address that batches are sent to.
124    pub batch_inbox_address: Address,
125    /// `deposit_contract_address` is the L1 address that deposits are sent to.
126    pub deposit_contract_address: Address,
127    /// `l1_system_config_address` is the L1 address that the system config is stored at.
128    pub l1_system_config_address: Address,
129    /// `protocol_versions_address` is the L1 address that the protocol versions are stored at.
130    pub protocol_versions_address: Address,
131    /// The superchain config address.
132    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
133    pub superchain_config_address: Option<Address>,
134    /// `blobs_enabled_l1_timestamp` is the timestamp to start reading blobs as a batch data
135    /// source. Optional.
136    #[cfg_attr(
137        feature = "serde",
138        serde(rename = "blobs_data", skip_serializing_if = "Option::is_none")
139    )]
140    pub blobs_enabled_l1_timestamp: Option<u64>,
141    /// `da_challenge_address` is the L1 address that the data availability challenge contract is
142    /// stored at.
143    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
144    pub da_challenge_address: Option<Address>,
145}
146
147// Need to manually implement Default because [`BaseFeeParams`] has no Default impl.
148impl Default for RollupConfig {
149    fn default() -> Self {
150        RollupConfig {
151            genesis: ChainGenesis::default(),
152            block_time: 0,
153            max_sequencer_drift: 0,
154            seq_window_size: 0,
155            channel_timeout: 0,
156            granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT,
157            l1_chain_id: 0,
158            l2_chain_id: 0,
159            base_fee_params: OP_BASE_FEE_PARAMS,
160            canyon_base_fee_params: OP_CANYON_BASE_FEE_PARAMS,
161            regolith_time: None,
162            canyon_time: None,
163            delta_time: None,
164            ecotone_time: None,
165            fjord_time: None,
166            granite_time: None,
167            holocene_time: None,
168            batch_inbox_address: Address::ZERO,
169            deposit_contract_address: Address::ZERO,
170            l1_system_config_address: Address::ZERO,
171            protocol_versions_address: Address::ZERO,
172            superchain_config_address: None,
173            blobs_enabled_l1_timestamp: None,
174            da_challenge_address: None,
175        }
176    }
177}
178
179/// Loads the rollup config for the OP-Stack chain given the chain config and address list.
180pub fn load_op_stack_rollup_config(chain_config: &ChainConfig) -> RollupConfig {
181    RollupConfig {
182        genesis: chain_config.genesis.clone(),
183        l1_chain_id: chain_config.l1_chain_id,
184        l2_chain_id: chain_config.chain_id,
185        base_fee_params: base_fee_params(chain_config.chain_id),
186        canyon_base_fee_params: canyon_base_fee_params(chain_config.chain_id),
187        regolith_time: Some(0),
188        canyon_time: chain_config.hardfork_configuration.canyon_time,
189        delta_time: chain_config.hardfork_configuration.delta_time,
190        ecotone_time: chain_config.hardfork_configuration.ecotone_time,
191        fjord_time: chain_config.hardfork_configuration.fjord_time,
192        granite_time: chain_config.hardfork_configuration.granite_time,
193        holocene_time: chain_config.hardfork_configuration.holocene_time,
194        batch_inbox_address: chain_config.batch_inbox_addr,
195        deposit_contract_address: chain_config
196            .addresses
197            .as_ref()
198            .map(|a| a.optimism_portal_proxy)
199            .unwrap_or_default(),
200        l1_system_config_address: chain_config
201            .addresses
202            .as_ref()
203            .map(|a| a.system_config_proxy)
204            .unwrap_or_default(),
205        protocol_versions_address: chain_config
206            .addresses
207            .as_ref()
208            .map(|a| a.address_manager)
209            .unwrap_or_default(),
210        superchain_config_address: None,
211        blobs_enabled_l1_timestamp: None,
212        da_challenge_address: chain_config
213            .alt_da
214            .as_ref()
215            .and_then(|alt_da| alt_da.da_challenge_address),
216
217        // The below chain parameters can be different per OP-Stack chain,
218        // but since none of the superchain chains differ, it's not represented in the superchain-registry yet.
219        // This restriction on superchain-chains may change in the future.
220        // Test/Alt configurations can still load custom rollup-configs when necessary.
221        block_time: 2,
222        channel_timeout: 300,
223        granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT,
224        max_sequencer_drift: 600,
225        seq_window_size: 3600,
226    }
227}
228
229impl RollupConfig {
230    /// Returns true if Regolith is active at the given timestamp.
231    pub fn is_regolith_active(&self, timestamp: u64) -> bool {
232        self.regolith_time.map_or(false, |t| timestamp >= t)
233    }
234
235    /// Returns true if Canyon is active at the given timestamp.
236    pub fn is_canyon_active(&self, timestamp: u64) -> bool {
237        self.canyon_time.map_or(false, |t| timestamp >= t)
238    }
239
240    /// Returns true if Delta is active at the given timestamp.
241    pub fn is_delta_active(&self, timestamp: u64) -> bool {
242        self.delta_time.map_or(false, |t| timestamp >= t)
243    }
244
245    /// Returns true if Ecotone is active at the given timestamp.
246    pub fn is_ecotone_active(&self, timestamp: u64) -> bool {
247        self.ecotone_time.map_or(false, |t| timestamp >= t)
248    }
249
250    /// Returns true if Fjord is active at the given timestamp.
251    pub fn is_fjord_active(&self, timestamp: u64) -> bool {
252        self.fjord_time.map_or(false, |t| timestamp >= t)
253    }
254
255    /// Returns true if Granite is active at the given timestamp.
256    pub fn is_granite_active(&self, timestamp: u64) -> bool {
257        self.granite_time.map_or(false, |t| timestamp >= t)
258    }
259
260    /// Returns true if Holocene is active at the given timestamp.
261    pub fn is_holocene_active(&self, timestamp: u64) -> bool {
262        self.holocene_time.map_or(false, |t| timestamp >= t)
263    }
264
265    /// Returns true if a DA Challenge proxy Address is provided in the rollup config and the
266    /// address is not zero.
267    pub fn is_alt_da_enabled(&self) -> bool {
268        self.da_challenge_address
269            .map_or(false, |addr| !addr.is_zero())
270    }
271
272    /// Returns the max sequencer drift for the given timestamp.
273    pub fn max_sequencer_drift(&self, timestamp: u64) -> u64 {
274        if self.is_fjord_active(timestamp) {
275            FJORD_MAX_SEQUENCER_DRIFT
276        } else {
277            self.max_sequencer_drift
278        }
279    }
280
281    /// Returns the max rlp bytes per channel for the given timestamp.
282    pub fn max_rlp_bytes_per_channel(&self, timestamp: u64) -> u64 {
283        if self.is_fjord_active(timestamp) {
284            MAX_RLP_BYTES_PER_CHANNEL_FJORD
285        } else {
286            MAX_RLP_BYTES_PER_CHANNEL_BEDROCK
287        }
288    }
289
290    /// Returns the channel timeout for the given timestamp.
291    pub fn channel_timeout(&self, timestamp: u64) -> u64 {
292        if self.is_granite_active(timestamp) {
293            self.granite_channel_timeout
294        } else {
295            self.channel_timeout
296        }
297    }
298
299    /// Checks the scalar value in Ecotone.
300    pub fn check_ecotone_l1_system_config_scalar(scalar: [u8; 32]) -> Result<(), &'static str> {
301        let version_byte = scalar[0];
302        match version_byte {
303            0 => {
304                if scalar[1..28] != [0; 27] {
305                    return Err("Bedrock scalar padding not empty");
306                }
307                Ok(())
308            }
309            1 => {
310                if scalar[1..24] != [0; 23] {
311                    return Err("Invalid version 1 scalar padding");
312                }
313                Ok(())
314            }
315            _ => {
316                // ignore the event if it's an unknown scalar format
317                Err("Unrecognized scalar version")
318            }
319        }
320    }
321
322    /// Returns the [RollupConfig] for the given L2 chain ID.
323    pub fn from_l2_chain_id(l2_chain_id: u64) -> Option<RollupConfig> {
324        match l2_chain_id {
325            10 => Some(OP_MAINNET_CONFIG),
326            11155420 => Some(OP_SEPOLIA_CONFIG),
327            8453 => Some(BASE_MAINNET_CONFIG),
328            84532 => Some(BASE_SEPOLIA_CONFIG),
329            _ => None,
330        }
331    }
332}
333
334/// The [RollupConfig] for OP Mainnet.
335pub const OP_MAINNET_CONFIG: RollupConfig = RollupConfig {
336    genesis: ChainGenesis {
337        l1: BlockID {
338            hash: b256!("438335a20d98863a4c0c97999eb2481921ccd28553eac6f913af7c12aec04108"),
339            number: 17_422_590_u64,
340        },
341        l2: BlockID {
342            hash: b256!("dbf6a80fef073de06add9b0d14026d6e5a86c85f6d102c36d3d8e9cf89c2afd3"),
343            number: 105_235_063_u64,
344        },
345        l2_time: 1_686_068_903_u64,
346        system_config: Some(SystemConfig {
347            batcher_address: address!("6887246668a3b87f54deb3b94ba47a6f63f32985"),
348            overhead: uint!(0xbc_U256),
349            scalar: uint!(0xa6fe0_U256),
350            gas_limit: 30_000_000_u64,
351            base_fee_scalar: None,
352            blob_base_fee_scalar: None,
353        }),
354    },
355    block_time: 2_u64,
356    max_sequencer_drift: 600_u64,
357    seq_window_size: 3600_u64,
358    channel_timeout: 300_u64,
359    granite_channel_timeout: 50,
360    l1_chain_id: 1_u64,
361    l2_chain_id: 10_u64,
362    base_fee_params: OP_BASE_FEE_PARAMS,
363    canyon_base_fee_params: OP_CANYON_BASE_FEE_PARAMS,
364    regolith_time: Some(0_u64),
365    canyon_time: Some(1_704_992_401_u64),
366    delta_time: Some(1_708_560_000_u64),
367    ecotone_time: Some(1_710_374_401_u64),
368    fjord_time: Some(1_720_627_201_u64),
369    granite_time: Some(1_726_070_401_u64),
370    holocene_time: None,
371    batch_inbox_address: address!("ff00000000000000000000000000000000000010"),
372    deposit_contract_address: address!("beb5fc579115071764c7423a4f12edde41f106ed"),
373    l1_system_config_address: address!("229047fed2591dbec1ef1118d64f7af3db9eb290"),
374    protocol_versions_address: address!("8062abc286f5e7d9428a0ccb9abd71e50d93b935"),
375    superchain_config_address: Some(address!("95703e0982140D16f8ebA6d158FccEde42f04a4C")),
376    da_challenge_address: None,
377    blobs_enabled_l1_timestamp: None,
378};
379
380/// The [RollupConfig] for OP Sepolia.
381pub const OP_SEPOLIA_CONFIG: RollupConfig = RollupConfig {
382    genesis: ChainGenesis {
383        l1: BlockID {
384            hash: b256!("48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b"),
385            number: 4071408,
386        },
387        l2: BlockID {
388            hash: b256!("102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d"),
389            number: 0,
390        },
391        l2_time: 1691802540,
392        system_config: Some(SystemConfig {
393            batcher_address: address!("8f23bb38f531600e5d8fddaaec41f13fab46e98c"),
394            overhead: uint!(0xbc_U256),
395            scalar: uint!(0xa6fe0_U256),
396            gas_limit: 30_000_000,
397            base_fee_scalar: None,
398            blob_base_fee_scalar: None,
399        }),
400    },
401    block_time: 2,
402    max_sequencer_drift: 600,
403    seq_window_size: 3600,
404    channel_timeout: 300,
405    granite_channel_timeout: 50,
406    l1_chain_id: 11155111,
407    l2_chain_id: 11155420,
408    base_fee_params: OP_SEPOLIA_BASE_FEE_PARAMS,
409    canyon_base_fee_params: OP_SEPOLIA_CANYON_BASE_FEE_PARAMS,
410    regolith_time: Some(0),
411    canyon_time: Some(1699981200),
412    delta_time: Some(1703203200),
413    ecotone_time: Some(1708534800),
414    fjord_time: Some(1716998400),
415    granite_time: Some(1723478400),
416    holocene_time: None,
417    batch_inbox_address: address!("ff00000000000000000000000000000011155420"),
418    deposit_contract_address: address!("16fc5058f25648194471939df75cf27a2fdc48bc"),
419    l1_system_config_address: address!("034edd2a225f7f429a63e0f1d2084b9e0a93b538"),
420    protocol_versions_address: address!("79add5713b383daa0a138d3c4780c7a1804a8090"),
421    superchain_config_address: Some(address!("C2Be75506d5724086DEB7245bd260Cc9753911Be")),
422    da_challenge_address: None,
423    blobs_enabled_l1_timestamp: None,
424};
425
426/// The [RollupConfig] for Base Mainnet.
427pub const BASE_MAINNET_CONFIG: RollupConfig = RollupConfig {
428    genesis: ChainGenesis {
429        l1: BlockID {
430            hash: b256!("5c13d307623a926cd31415036c8b7fa14572f9dac64528e857a470511fc30771"),
431            number: 17_481_768_u64,
432        },
433        l2: BlockID {
434            hash: b256!("f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"),
435            number: 0_u64,
436        },
437        l2_time: 1686789347_u64,
438        system_config: Some(SystemConfig {
439            batcher_address: address!("5050f69a9786f081509234f1a7f4684b5e5b76c9"),
440            overhead: uint!(0xbc_U256),
441            scalar: uint!(0xa6fe0_U256),
442            gas_limit: 30_000_000_u64,
443            base_fee_scalar: None,
444            blob_base_fee_scalar: None,
445        }),
446    },
447    block_time: 2,
448    max_sequencer_drift: 600,
449    seq_window_size: 3600,
450    channel_timeout: 300,
451    granite_channel_timeout: 50,
452    l1_chain_id: 1,
453    l2_chain_id: 8453,
454    base_fee_params: OP_BASE_FEE_PARAMS,
455    canyon_base_fee_params: OP_CANYON_BASE_FEE_PARAMS,
456    regolith_time: Some(0_u64),
457    canyon_time: Some(1704992401),
458    delta_time: Some(1708560000),
459    ecotone_time: Some(1710374401),
460    fjord_time: Some(1720627201),
461    granite_time: Some(1_726_070_401_u64),
462    holocene_time: None,
463    batch_inbox_address: address!("ff00000000000000000000000000000000008453"),
464    deposit_contract_address: address!("49048044d57e1c92a77f79988d21fa8faf74e97e"),
465    l1_system_config_address: address!("73a79fab69143498ed3712e519a88a918e1f4072"),
466    protocol_versions_address: address!("8062abc286f5e7d9428a0ccb9abd71e50d93b935"),
467    superchain_config_address: Some(address!("95703e0982140D16f8ebA6d158FccEde42f04a4C")),
468    da_challenge_address: None,
469    blobs_enabled_l1_timestamp: None,
470};
471
472/// The [RollupConfig] for Base Sepolia.
473pub const BASE_SEPOLIA_CONFIG: RollupConfig = RollupConfig {
474    genesis: ChainGenesis {
475        l1: BlockID {
476            hash: b256!("cac9a83291d4dec146d6f7f69ab2304f23f5be87b1789119a0c5b1e4482444ed"),
477            number: 4370868,
478        },
479        l2: BlockID {
480            hash: b256!("0dcc9e089e30b90ddfc55be9a37dd15bc551aeee999d2e2b51414c54eaf934e4"),
481            number: 0,
482        },
483        l2_time: 1695768288,
484        system_config: Some(SystemConfig {
485            batcher_address: address!("6cdebe940bc0f26850285caca097c11c33103e47"),
486            overhead: uint!(0x834_U256),
487            scalar: uint!(0xf4240_U256),
488            gas_limit: 25000000,
489            base_fee_scalar: None,
490            blob_base_fee_scalar: None,
491        }),
492    },
493    block_time: 2,
494    max_sequencer_drift: 600,
495    seq_window_size: 3600,
496    channel_timeout: 300,
497    granite_channel_timeout: 50,
498    l1_chain_id: 11155111,
499    l2_chain_id: 84532,
500    base_fee_params: BASE_SEPOLIA_BASE_FEE_PARAMS,
501    canyon_base_fee_params: BASE_SEPOLIA_CANYON_BASE_FEE_PARAMS,
502    regolith_time: Some(0),
503    canyon_time: Some(1699981200),
504    delta_time: Some(1703203200),
505    ecotone_time: Some(1708534800),
506    fjord_time: Some(1716998400),
507    granite_time: Some(1723478400),
508    holocene_time: None,
509    batch_inbox_address: address!("ff00000000000000000000000000000000084532"),
510    deposit_contract_address: address!("49f53e41452c74589e85ca1677426ba426459e85"),
511    l1_system_config_address: address!("f272670eb55e895584501d564afeb048bed26194"),
512    protocol_versions_address: address!("79add5713b383daa0a138d3c4780c7a1804a8090"),
513    superchain_config_address: Some(address!("C2Be75506d5724086DEB7245bd260Cc9753911Be")),
514    da_challenge_address: None,
515    blobs_enabled_l1_timestamp: None,
516};
517
518#[cfg(test)]
519mod tests {
520    use alloy_primitives::U256;
521
522    use super::*;
523
524    #[test]
525    fn test_regolith_active() {
526        let mut config = RollupConfig::default();
527        assert!(!config.is_regolith_active(0));
528        config.regolith_time = Some(10);
529        assert!(config.is_regolith_active(10));
530        assert!(!config.is_regolith_active(9));
531    }
532
533    #[test]
534    fn test_canyon_active() {
535        let mut config = RollupConfig::default();
536        assert!(!config.is_canyon_active(0));
537        config.canyon_time = Some(10);
538        assert!(config.is_canyon_active(10));
539        assert!(!config.is_canyon_active(9));
540    }
541
542    #[test]
543    fn test_delta_active() {
544        let mut config = RollupConfig::default();
545        assert!(!config.is_delta_active(0));
546        config.delta_time = Some(10);
547        assert!(config.is_delta_active(10));
548        assert!(!config.is_delta_active(9));
549    }
550
551    #[test]
552    fn test_ecotone_active() {
553        let mut config = RollupConfig::default();
554        assert!(!config.is_ecotone_active(0));
555        config.ecotone_time = Some(10);
556        assert!(config.is_ecotone_active(10));
557        assert!(!config.is_ecotone_active(9));
558    }
559
560    #[test]
561    fn test_fjord_active() {
562        let mut config = RollupConfig::default();
563        assert!(!config.is_fjord_active(0));
564        config.fjord_time = Some(10);
565        assert!(config.is_fjord_active(10));
566        assert!(!config.is_fjord_active(9));
567    }
568
569    #[test]
570    fn test_granite_active() {
571        let mut config = RollupConfig::default();
572        assert!(!config.is_granite_active(0));
573        config.granite_time = Some(10);
574        assert!(config.is_granite_active(10));
575        assert!(!config.is_granite_active(9));
576    }
577
578    #[test]
579    fn test_holocene_active() {
580        let mut config = RollupConfig::default();
581        assert!(!config.is_holocene_active(0));
582        config.holocene_time = Some(10);
583        assert!(config.is_holocene_active(10));
584        assert!(!config.is_holocene_active(9));
585    }
586
587    #[test]
588    fn test_alt_da_enabled() {
589        let mut config = RollupConfig::default();
590        assert!(!config.is_alt_da_enabled());
591        config.da_challenge_address = Some(Address::ZERO);
592        assert!(!config.is_alt_da_enabled());
593        config.da_challenge_address = Some(address!("0000000000000000000000000000000000000001"));
594        assert!(config.is_alt_da_enabled());
595    }
596
597    #[test]
598    fn test_granite_channel_timeout() {
599        let mut config = RollupConfig {
600            channel_timeout: 100,
601            granite_time: Some(10),
602            ..Default::default()
603        };
604        assert_eq!(config.channel_timeout(0), 100);
605        assert_eq!(config.channel_timeout(10), GRANITE_CHANNEL_TIMEOUT);
606        config.granite_time = None;
607        assert_eq!(config.channel_timeout(10), 100);
608    }
609
610    #[test]
611    fn test_max_sequencer_drift() {
612        let mut config = RollupConfig {
613            max_sequencer_drift: 100,
614            ..Default::default()
615        };
616        assert_eq!(config.max_sequencer_drift(0), 100);
617        config.fjord_time = Some(10);
618        assert_eq!(config.max_sequencer_drift(0), 100);
619        assert_eq!(config.max_sequencer_drift(10), FJORD_MAX_SEQUENCER_DRIFT);
620    }
621
622    #[test]
623    fn test_deserialize_reference_rollup_config() {
624        // Reference serialized rollup config from the `op-node`.
625        let ser_cfg = r#"
626{
627  "genesis": {
628    "l1": {
629      "hash": "0x481724ee99b1f4cb71d826e2ec5a37265f460e9b112315665c977f4050b0af54",
630      "number": 10
631    },
632    "l2": {
633      "hash": "0x88aedfbf7dea6bfa2c4ff315784ad1a7f145d8f650969359c003bbed68c87631",
634      "number": 0
635    },
636    "l2_time": 1725557164,
637    "system_config": {
638      "batcherAddr": "0xc81f87a644b41e49b3221f41251f15c6cb00ce03",
639      "overhead": "0x0000000000000000000000000000000000000000000000000000000000000000",
640      "scalar": "0x00000000000000000000000000000000000000000000000000000000000f4240",
641      "gasLimit": 30000000
642    }
643  },
644  "block_time": 2,
645  "max_sequencer_drift": 600,
646  "seq_window_size": 3600,
647  "channel_timeout": 300,
648  "l1_chain_id": 3151908,
649  "l2_chain_id": 1337,
650  "regolith_time": 0,
651  "canyon_time": 0,
652  "delta_time": 0,
653  "ecotone_time": 0,
654  "fjord_time": 0,
655  "batch_inbox_address": "0xff00000000000000000000000000000000042069",
656  "deposit_contract_address": "0x08073dc48dde578137b8af042bcbc1c2491f1eb2",
657  "l1_system_config_address": "0x94ee52a9d8edd72a85dea7fae3ba6d75e4bf1710",
658  "protocol_versions_address": "0x0000000000000000000000000000000000000000"
659}
660        "#;
661        let config: RollupConfig = serde_json::from_str(ser_cfg).unwrap();
662
663        // Validate standard fields.
664        assert_eq!(
665            config.genesis,
666            ChainGenesis {
667                l1: BlockID {
668                    hash: b256!("481724ee99b1f4cb71d826e2ec5a37265f460e9b112315665c977f4050b0af54"),
669                    number: 10
670                },
671                l2: BlockID {
672                    hash: b256!("88aedfbf7dea6bfa2c4ff315784ad1a7f145d8f650969359c003bbed68c87631"),
673                    number: 0
674                },
675                l2_time: 1725557164,
676                system_config: Some(SystemConfig {
677                    batcher_address: address!("c81f87a644b41e49b3221f41251f15c6cb00ce03"),
678                    overhead: U256::ZERO,
679                    scalar: U256::from(0xf4240),
680                    gas_limit: 30_000_000,
681                    base_fee_scalar: None,
682                    blob_base_fee_scalar: None
683                })
684            }
685        );
686        assert_eq!(config.block_time, 2);
687        assert_eq!(config.max_sequencer_drift, 600);
688        assert_eq!(config.seq_window_size, 3600);
689        assert_eq!(config.channel_timeout, 300);
690        assert_eq!(config.l1_chain_id, 3151908);
691        assert_eq!(config.l2_chain_id, 1337);
692        assert_eq!(config.regolith_time, Some(0));
693        assert_eq!(config.canyon_time, Some(0));
694        assert_eq!(config.delta_time, Some(0));
695        assert_eq!(config.ecotone_time, Some(0));
696        assert_eq!(config.fjord_time, Some(0));
697        assert_eq!(
698            config.batch_inbox_address,
699            address!("ff00000000000000000000000000000000042069")
700        );
701        assert_eq!(
702            config.deposit_contract_address,
703            address!("08073dc48dde578137b8af042bcbc1c2491f1eb2")
704        );
705        assert_eq!(
706            config.l1_system_config_address,
707            address!("94ee52a9d8edd72a85dea7fae3ba6d75e4bf1710")
708        );
709        assert_eq!(config.protocol_versions_address, Address::ZERO);
710
711        // Validate non-standard fields.
712        assert_eq!(config.granite_channel_timeout, GRANITE_CHANNEL_TIMEOUT);
713        assert_eq!(config.base_fee_params, OP_BASE_FEE_PARAMS);
714        assert_eq!(config.canyon_base_fee_params, OP_CANYON_BASE_FEE_PARAMS);
715    }
716}