kona_genesis/chain/
config.rs

1//! Contains the chain config type.
2
3use alloc::string::String;
4use alloy_eips::eip1559::BaseFeeParams;
5use alloy_primitives::Address;
6
7use crate::{
8    AddressList, AltDAConfig, BaseFeeConfig, ChainGenesis, GRANITE_CHANNEL_TIMEOUT, HardForkConfig,
9    Roles, RollupConfig, SuperchainLevel, base_fee_params, base_fee_params_canyon,
10    params::base_fee_config, rollup::DEFAULT_INTEROP_MESSAGE_EXPIRY_WINDOW,
11};
12
13/// Defines core blockchain settings per block.
14///
15/// Tailors unique settings for each network based on
16/// its genesis block and superchain configuration.
17///
18/// This struct bridges the interface between the [`ChainConfig`][ccr]
19/// defined in the [`superchain-registry`][scr] and the [`ChainConfig`][ccg]
20/// defined in [`op-geth`][opg].
21///
22/// [opg]: https://github.com/ethereum-optimism/op-geth
23/// [scr]: https://github.com/ethereum-optimism/superchain-registry
24/// [ccg]: https://github.com/ethereum-optimism/op-geth/blob/optimism/params/config.go#L342
25/// [ccr]: https://github.com/ethereum-optimism/superchain-registry/blob/main/superchain/superchain.go#L80
26#[derive(Debug, Clone, Default, Eq, PartialEq)]
27#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
30pub struct ChainConfig {
31    /// Chain name (e.g. "Base")
32    #[cfg_attr(feature = "serde", serde(rename = "Name", alias = "name"))]
33    pub name: String,
34    /// L1 chain ID
35    #[cfg_attr(feature = "serde", serde(skip))]
36    pub l1_chain_id: u64,
37    /// Chain public RPC endpoint
38    #[cfg_attr(feature = "serde", serde(rename = "PublicRPC", alias = "public_rpc"))]
39    pub public_rpc: String,
40    /// Chain sequencer RPC endpoint
41    #[cfg_attr(feature = "serde", serde(rename = "SequencerRPC", alias = "sequencer_rpc"))]
42    pub sequencer_rpc: String,
43    /// Chain explorer HTTP endpoint
44    #[cfg_attr(feature = "serde", serde(rename = "Explorer", alias = "explorer"))]
45    pub explorer: String,
46    /// Level of integration with the superchain.
47    #[cfg_attr(feature = "serde", serde(rename = "SuperchainLevel", alias = "superchain_level"))]
48    pub superchain_level: SuperchainLevel,
49    /// Whether the chain is governed by optimism.
50    #[cfg_attr(
51        feature = "serde",
52        serde(rename = "GovernedByOptimism", alias = "governed_by_optimism")
53    )]
54    #[cfg_attr(feature = "serde", serde(default))]
55    pub governed_by_optimism: bool,
56    /// Time of when a given chain is opted in to the Superchain.
57    /// If set, hardforks times after the superchain time
58    /// will be inherited from the superchain-wide config.
59    #[cfg_attr(feature = "serde", serde(rename = "SuperchainTime", alias = "superchain_time"))]
60    pub superchain_time: Option<u64>,
61    /// Data availability type.
62    #[cfg_attr(
63        feature = "serde",
64        serde(rename = "DataAvailabilityType", alias = "data_availability_type")
65    )]
66    pub data_availability_type: String,
67    /// Chain ID
68    #[cfg_attr(feature = "serde", serde(rename = "l2_chain_id", alias = "chain_id"))]
69    pub chain_id: u64,
70    /// Chain-specific batch inbox address
71    #[cfg_attr(
72        feature = "serde",
73        serde(rename = "batch_inbox_address", alias = "batch_inbox_addr")
74    )]
75    #[cfg_attr(feature = "serde", serde(default))]
76    pub batch_inbox_addr: Address,
77    /// The block time in seconds.
78    #[cfg_attr(feature = "serde", serde(rename = "block_time"))]
79    pub block_time: u64,
80    /// The sequencer window size in seconds.
81    #[cfg_attr(feature = "serde", serde(rename = "seq_window_size"))]
82    pub seq_window_size: u64,
83    /// The maximum sequencer drift in seconds.
84    #[cfg_attr(feature = "serde", serde(rename = "max_sequencer_drift"))]
85    pub max_sequencer_drift: u64,
86    /// Gas paying token metadata. Not consumed by downstream OPStack components.
87    #[cfg_attr(feature = "serde", serde(rename = "GasPayingToken", alias = "gas_paying_token"))]
88    pub gas_paying_token: Option<Address>,
89    /// Hardfork Config. These values may override the superchain-wide defaults.
90    #[cfg_attr(feature = "serde", serde(rename = "hardfork_configuration", alias = "hardforks"))]
91    pub hardfork_config: HardForkConfig,
92    /// Optimism configuration
93    #[cfg_attr(feature = "serde", serde(rename = "optimism"))]
94    pub optimism: Option<BaseFeeConfig>,
95    /// Alternative DA configuration
96    #[cfg_attr(feature = "serde", serde(rename = "alt_da"))]
97    pub alt_da: Option<AltDAConfig>,
98    /// Chain-specific genesis information
99    pub genesis: ChainGenesis,
100    /// Roles
101    #[cfg_attr(feature = "serde", serde(rename = "Roles", alias = "roles"))]
102    pub roles: Option<Roles>,
103    /// Addresses
104    #[cfg_attr(feature = "serde", serde(rename = "Addresses", alias = "addresses"))]
105    pub addresses: Option<AddressList>,
106}
107
108impl ChainConfig {
109    /// Returns the base fee params for the chain.
110    pub fn base_fee_params(&self) -> BaseFeeParams {
111        self.optimism
112            .as_ref()
113            .map(|op| op.as_base_fee_params())
114            .unwrap_or_else(|| base_fee_params(self.chain_id))
115    }
116
117    /// Returns the canyon base fee params for the chain.
118    pub fn canyon_base_fee_params(&self) -> BaseFeeParams {
119        self.optimism
120            .as_ref()
121            .map(|op| op.as_canyon_base_fee_params())
122            .unwrap_or_else(|| base_fee_params_canyon(self.chain_id))
123    }
124
125    /// Returns the base fee config for the chain.
126    pub fn base_fee_config(&self) -> BaseFeeConfig {
127        self.optimism.as_ref().map(|op| *op).unwrap_or_else(|| base_fee_config(self.chain_id))
128    }
129
130    /// Loads the rollup config for the OP-Stack chain given the chain config and address list.
131    #[deprecated(since = "0.2.1", note = "please use `as_rollup_config` instead")]
132    pub fn load_op_stack_rollup_config(&self) -> RollupConfig {
133        self.as_rollup_config()
134    }
135
136    /// Loads the rollup config for the OP-Stack chain given the chain config and address list.
137    pub fn as_rollup_config(&self) -> RollupConfig {
138        RollupConfig {
139            genesis: self.genesis,
140            l1_chain_id: self.l1_chain_id,
141            l2_chain_id: self.chain_id,
142            block_time: self.block_time,
143            seq_window_size: self.seq_window_size,
144            max_sequencer_drift: self.max_sequencer_drift,
145            hardforks: self.hardfork_config,
146            batch_inbox_address: self.batch_inbox_addr,
147            deposit_contract_address: self
148                .addresses
149                .as_ref()
150                .map(|a| a.optimism_portal_proxy)
151                .unwrap_or_default(),
152            l1_system_config_address: self
153                .addresses
154                .as_ref()
155                .map(|a| a.system_config_proxy)
156                .unwrap_or_default(),
157            protocol_versions_address: self
158                .addresses
159                .as_ref()
160                .map(|a| a.address_manager)
161                .unwrap_or_default(),
162            superchain_config_address: None,
163            blobs_enabled_l1_timestamp: None,
164            da_challenge_address: self
165                .alt_da
166                .as_ref()
167                .and_then(|alt_da| alt_da.da_challenge_address),
168
169            // The below chain parameters can be different per OP-Stack chain,
170            // but since none of the superchain chains differ, it's not represented in the
171            // superchain-registry yet. This restriction on superchain-chains may change in the
172            // future. Test/Alt configurations can still load custom rollup-configs when
173            // necessary.
174            channel_timeout: 300,
175            granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT,
176            interop_message_expiry_window: DEFAULT_INTEROP_MESSAGE_EXPIRY_WINDOW,
177            chain_op_config: self.base_fee_config(),
178            alt_da_config: self.alt_da.clone(),
179        }
180    }
181}
182
183#[cfg(test)]
184#[cfg(feature = "serde")]
185mod tests {
186    use super::*;
187
188    #[test]
189    fn test_chain_config_json() {
190        let raw: &str = r#"
191        {
192            "Name": "Base",
193            "PublicRPC": "https://mainnet.base.org",
194            "SequencerRPC": "https://mainnet-sequencer.base.org",
195            "Explorer": "https://explorer.base.org",
196            "SuperchainLevel": 1,
197            "GovernedByOptimism": false,
198            "SuperchainTime": 0,
199            "DataAvailabilityType": "eth-da",
200            "l2_chain_id": 8453,
201            "batch_inbox_address": "0xff00000000000000000000000000000000008453",
202            "block_time": 2,
203            "seq_window_size": 3600,
204            "max_sequencer_drift": 600,
205            "GasPayingToken": null,
206            "hardfork_configuration": {
207                "canyon_time": 1704992401,
208                "delta_time": 1708560000,
209                "ecotone_time": 1710374401,
210                "fjord_time": 1720627201,
211                "granite_time": 1726070401,
212                "holocene_time": 1736445601
213            },
214            "optimism": {
215            "eip1559Elasticity": "0x6",
216            "eip1559Denominator": "0x32",
217            "eip1559DenominatorCanyon": "0xfa"
218            },
219            "alt_da": null,
220            "genesis": {
221                "l1": {
222                  "number": 17481768,
223                  "hash": "0x5c13d307623a926cd31415036c8b7fa14572f9dac64528e857a470511fc30771"
224                },
225                "l2": {
226                  "number": 0,
227                  "hash": "0xf712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"
228                },
229                "l2_time": 1686789347,
230                "system_config": {
231                  "batcherAddress": "0x5050f69a9786f081509234f1a7f4684b5e5b76c9",
232                  "overhead": "0xbc",
233                  "scalar": "0xa6fe0",
234                  "gasLimit": 30000000
235                }
236            },
237            "Roles": {
238                "SystemConfigOwner": "0x14536667cd30e52c0b458baaccb9fada7046e056",
239                "ProxyAdminOwner": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c",
240                "Guardian": "0x09f7150d8c019bef34450d6920f6b3608cefdaf2",
241                "Challenger": "0x6f8c5ba3f59ea3e76300e3becdc231d656017824",
242                "Proposer": "0x642229f238fb9de03374be34b0ed8d9de80752c5",
243                "UnsafeBlockSigner": "0xaf6e19be0f9ce7f8afd49a1824851023a8249e8a",
244                "BatchSubmitter": "0x5050f69a9786f081509234f1a7f4684b5e5b76c9"
245            },
246            "Addresses": {
247                "AddressManager": "0x8efb6b5c4767b09dc9aa6af4eaa89f749522bae2",
248                "L1CrossDomainMessengerProxy": "0x866e82a600a1414e583f7f13623f1ac5d58b0afa",
249                "L1Erc721BridgeProxy": "0x608d94945a64503e642e6370ec598e519a2c1e53",
250                "L1StandardBridgeProxy": "0x3154cf16ccdb4c6d922629664174b904d80f2c35",
251                "L2OutputOracleProxy": "0x56315b90c40730925ec5485cf004d835058518a0",
252                "OptimismMintableErc20FactoryProxy": "0x05cc379ebd9b30bba19c6fa282ab29218ec61d84",
253                "OptimismPortalProxy": "0x49048044d57e1c92a77f79988d21fa8faf74e97e",
254                "SystemConfigProxy": "0x73a79fab69143498ed3712e519a88a918e1f4072",
255                "ProxyAdmin": "0x0475cbcaebd9ce8afa5025828d5b98dfb67e059e",
256                "AnchorStateRegistryProxy": "0xdb9091e48b1c42992a1213e6916184f9ebdbfedf",
257                "DelayedWethProxy": "0xa2f2ac6f5af72e494a227d79db20473cf7a1ffe8",
258                "DisputeGameFactoryProxy": "0x43edb88c4b80fdd2adff2412a7bebf9df42cb40e",
259                "FaultDisputeGame": "0xcd3c0194db74c23807d4b90a5181e1b28cf7007c",
260                "Mips": "0x16e83ce5ce29bf90ad9da06d2fe6a15d5f344ce4",
261                "PermissionedDisputeGame": "0x19009debf8954b610f207d5925eede827805986e",
262                "PreimageOracle": "0x9c065e11870b891d214bc2da7ef1f9ddfa1be277"
263            }
264        }
265        "#;
266
267        let deserialized: ChainConfig = serde_json::from_str(raw).unwrap();
268        assert_eq!(deserialized.name, "Base");
269    }
270
271    #[test]
272    fn test_chain_config_unknown_field_json() {
273        let raw: &str = r#"
274        {
275            "Name": "Base",
276            "PublicRPC": "https://mainnet.base.org",
277            "SequencerRPC": "https://mainnet-sequencer.base.org",
278            "Explorer": "https://explorer.base.org",
279            "SuperchainLevel": 1,
280            "GovernedByOptimism": false,
281            "SuperchainTime": 0,
282            "DataAvailabilityType": "eth-da",
283            "l2_chain_id": 8453,
284            "batch_inbox_address": "0xff00000000000000000000000000000000008453",
285            "block_time": 2,
286            "seq_window_size": 3600,
287            "max_sequencer_drift": 600,
288            "GasPayingToken": null,
289            "hardfork_configuration": {
290                "canyon_time": 1704992401,
291                "delta_time": 1708560000,
292                "ecotone_time": 1710374401,
293                "fjord_time": 1720627201,
294                "granite_time": 1726070401,
295                "holocene_time": 1736445601
296            },
297            "optimism": {
298            "eip1559Elasticity": "0x6",
299            "eip1559Denominator": "0x32",
300            "eip1559DenominatorCanyon": "0xfa"
301            },
302            "alt_da": null,
303            "genesis": {
304                "l1": {
305                  "number": 17481768,
306                  "hash": "0x5c13d307623a926cd31415036c8b7fa14572f9dac64528e857a470511fc30771"
307                },
308                "l2": {
309                  "number": 0,
310                  "hash": "0xf712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"
311                },
312                "l2_time": 1686789347,
313                "system_config": {
314                  "batcherAddress": "0x5050f69a9786f081509234f1a7f4684b5e5b76c9",
315                  "overhead": "0xbc",
316                  "scalar": "0xa6fe0",
317                  "gasLimit": 30000000
318                }
319            },
320            "Roles": {
321                "SystemConfigOwner": "0x14536667cd30e52c0b458baaccb9fada7046e056",
322                "ProxyAdminOwner": "0x7bb41c3008b3f03fe483b28b8db90e19cf07595c",
323                "Guardian": "0x09f7150d8c019bef34450d6920f6b3608cefdaf2",
324                "Challenger": "0x6f8c5ba3f59ea3e76300e3becdc231d656017824",
325                "Proposer": "0x642229f238fb9de03374be34b0ed8d9de80752c5",
326                "UnsafeBlockSigner": "0xaf6e19be0f9ce7f8afd49a1824851023a8249e8a",
327                "BatchSubmitter": "0x5050f69a9786f081509234f1a7f4684b5e5b76c9"
328            },
329            "Addresses": {
330                "AddressManager": "0x8efb6b5c4767b09dc9aa6af4eaa89f749522bae2",
331                "L1CrossDomainMessengerProxy": "0x866e82a600a1414e583f7f13623f1ac5d58b0afa",
332                "L1Erc721BridgeProxy": "0x608d94945a64503e642e6370ec598e519a2c1e53",
333                "L1StandardBridgeProxy": "0x3154cf16ccdb4c6d922629664174b904d80f2c35",
334                "L2OutputOracleProxy": "0x56315b90c40730925ec5485cf004d835058518a0",
335                "OptimismMintableErc20FactoryProxy": "0x05cc379ebd9b30bba19c6fa282ab29218ec61d84",
336                "OptimismPortalProxy": "0x49048044d57e1c92a77f79988d21fa8faf74e97e",
337                "SystemConfigProxy": "0x73a79fab69143498ed3712e519a88a918e1f4072",
338                "ProxyAdmin": "0x0475cbcaebd9ce8afa5025828d5b98dfb67e059e",
339                "AnchorStateRegistryProxy": "0xdb9091e48b1c42992a1213e6916184f9ebdbfedf",
340                "DelayedWethProxy": "0xa2f2ac6f5af72e494a227d79db20473cf7a1ffe8",
341                "DisputeGameFactoryProxy": "0x43edb88c4b80fdd2adff2412a7bebf9df42cb40e",
342                "FaultDisputeGame": "0xcd3c0194db74c23807d4b90a5181e1b28cf7007c",
343                "Mips": "0x16e83ce5ce29bf90ad9da06d2fe6a15d5f344ce4",
344                "PermissionedDisputeGame": "0x19009debf8954b610f207d5925eede827805986e",
345                "PreimageOracle": "0x9c065e11870b891d214bc2da7ef1f9ddfa1be277"
346            },
347            "unknown_field": "unknown"
348        }
349        "#;
350
351        let err = serde_json::from_str::<ChainConfig>(raw).unwrap_err();
352        assert_eq!(err.classify(), serde_json::error::Category::Data);
353    }
354}