1use 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#[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 #[cfg_attr(feature = "serde", serde(rename = "Name", alias = "name"))]
33 pub name: String,
34 #[cfg_attr(feature = "serde", serde(skip))]
36 pub l1_chain_id: u64,
37 #[cfg_attr(feature = "serde", serde(rename = "PublicRPC", alias = "public_rpc"))]
39 pub public_rpc: String,
40 #[cfg_attr(feature = "serde", serde(rename = "SequencerRPC", alias = "sequencer_rpc"))]
42 pub sequencer_rpc: String,
43 #[cfg_attr(feature = "serde", serde(rename = "Explorer", alias = "explorer"))]
45 pub explorer: String,
46 #[cfg_attr(feature = "serde", serde(rename = "SuperchainLevel", alias = "superchain_level"))]
48 pub superchain_level: SuperchainLevel,
49 #[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 #[cfg_attr(feature = "serde", serde(rename = "SuperchainTime", alias = "superchain_time"))]
60 pub superchain_time: Option<u64>,
61 #[cfg_attr(
63 feature = "serde",
64 serde(rename = "DataAvailabilityType", alias = "data_availability_type")
65 )]
66 pub data_availability_type: String,
67 #[cfg_attr(feature = "serde", serde(rename = "l2_chain_id", alias = "chain_id"))]
69 pub chain_id: u64,
70 #[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 #[cfg_attr(feature = "serde", serde(rename = "block_time"))]
79 pub block_time: u64,
80 #[cfg_attr(feature = "serde", serde(rename = "seq_window_size"))]
82 pub seq_window_size: u64,
83 #[cfg_attr(feature = "serde", serde(rename = "max_sequencer_drift"))]
85 pub max_sequencer_drift: u64,
86 #[cfg_attr(feature = "serde", serde(rename = "GasPayingToken", alias = "gas_paying_token"))]
88 pub gas_paying_token: Option<Address>,
89 #[cfg_attr(feature = "serde", serde(rename = "hardfork_configuration", alias = "hardforks"))]
91 pub hardfork_config: HardForkConfig,
92 #[cfg_attr(feature = "serde", serde(rename = "optimism"))]
94 pub optimism: Option<BaseFeeConfig>,
95 #[cfg_attr(feature = "serde", serde(rename = "alt_da"))]
97 pub alt_da: Option<AltDAConfig>,
98 pub genesis: ChainGenesis,
100 #[cfg_attr(feature = "serde", serde(rename = "Roles", alias = "roles"))]
102 pub roles: Option<Roles>,
103 #[cfg_attr(feature = "serde", serde(rename = "Addresses", alias = "addresses"))]
105 pub addresses: Option<AddressList>,
106}
107
108impl ChainConfig {
109 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 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 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 #[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 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 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}