Skip to main content

foundry_evm_hardforks/
lib.rs

1//! EVM hardfork definitions for Foundry.
2//!
3//! Provides [`FoundryHardfork`], a unified enum over Ethereum, Optimism, and Tempo hardforks
4//! with `FromStr`/`Serialize`/`Deserialize` support for CLI and config usage.
5
6use std::{
7    str::FromStr,
8    time::{SystemTime, UNIX_EPOCH},
9};
10
11use alloy_chains::Chain;
12use alloy_rpc_types::BlockNumberOrTag;
13use foundry_compilers::artifacts::EvmVersion;
14#[cfg(feature = "optimism")]
15use op_revm::OpSpecId;
16use revm::primitives::hardfork::SpecId;
17use serde::{Deserialize, Serialize};
18
19pub use alloy_hardforks::EthereumHardfork;
20#[cfg(feature = "optimism")]
21pub use alloy_op_hardforks::OpHardfork;
22pub use tempo_chainspec::hardfork::TempoHardfork;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
25#[serde(into = "String")]
26pub enum FoundryHardfork {
27    Ethereum(EthereumHardfork),
28    #[cfg(feature = "optimism")]
29    Optimism(OpHardfork),
30    Tempo(TempoHardfork),
31}
32
33impl From<FoundryHardfork> for String {
34    fn from(fork: FoundryHardfork) -> Self {
35        match fork {
36            FoundryHardfork::Ethereum(h) => format!("{h}"),
37            #[cfg(feature = "optimism")]
38            FoundryHardfork::Optimism(h) => format!("optimism:{h}"),
39            FoundryHardfork::Tempo(h) => format!("tempo:{h}"),
40        }
41    }
42}
43
44impl<'de> Deserialize<'de> for FoundryHardfork {
45    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46    where
47        D: serde::Deserializer<'de>,
48    {
49        let s = String::deserialize(deserializer)?;
50        Self::from_str(&s).map_err(serde::de::Error::custom)
51    }
52}
53
54impl FromStr for FoundryHardfork {
55    type Err = String;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        let raw = s.trim();
59
60        let Some((ns, fork_raw)) = raw.split_once(':') else {
61            return EthereumHardfork::from_str(raw)
62                .map(Self::Ethereum)
63                .map_err(|_| format!("unknown ethereum hardfork '{raw}'"));
64        };
65
66        let ns = ns.trim().to_ascii_lowercase();
67        let fork = fork_raw.trim().to_ascii_lowercase().replace(['-', ' '], "_");
68
69        match ns.as_str() {
70            "eth" | "ethereum" => EthereumHardfork::from_str(&fork)
71                .map(Self::Ethereum)
72                .map_err(|_| format!("unknown ethereum hardfork '{fork_raw}'")),
73
74            #[cfg(feature = "optimism")]
75            "op" | "optimism" => OpHardfork::from_str(&fork)
76                .map(Self::Optimism)
77                .map_err(|_| format!("unknown optimism hardfork '{fork_raw}'")),
78
79            "t" | "tempo" => TempoHardfork::from_str(&fork)
80                .map(Self::Tempo)
81                .map_err(|_| format!("unknown tempo hardfork '{fork_raw}'")),
82            _ => EthereumHardfork::from_str(&fork)
83                .map(Self::Ethereum)
84                .map_err(|_| format!("unknown hardfork '{raw}'")),
85        }
86    }
87}
88
89impl FoundryHardfork {
90    pub const fn ethereum(h: EthereumHardfork) -> Self {
91        Self::Ethereum(h)
92    }
93
94    #[cfg(feature = "optimism")]
95    pub const fn optimism(h: OpHardfork) -> Self {
96        Self::Optimism(h)
97    }
98
99    pub const fn tempo(h: TempoHardfork) -> Self {
100        Self::Tempo(h)
101    }
102
103    /// Returns the hardfork name without a network namespace prefix.
104    pub fn name(&self) -> String {
105        match self {
106            Self::Ethereum(h) => format!("{h}"),
107            #[cfg(feature = "optimism")]
108            Self::Optimism(h) => format!("{h}"),
109            Self::Tempo(h) => format!("{h}"),
110        }
111    }
112
113    /// Returns the network namespace for this hardfork, or `None` for plain Ethereum.
114    ///
115    /// Mirrors the namespace prefix used in the `"network:hardfork"` serialization format.
116    pub const fn namespace(&self) -> Option<&'static str> {
117        match self {
118            Self::Ethereum(_) => None,
119            #[cfg(feature = "optimism")]
120            Self::Optimism(_) => Some("optimism"),
121            Self::Tempo(_) => Some("tempo"),
122        }
123    }
124
125    /// Auto-detect the active hardfork for a given chain at a specific timestamp.
126    ///
127    /// Tries Ethereum, then Optimism. Returns `None` for unknown chains.
128    pub fn from_chain_and_timestamp(chain_id: u64, timestamp: u64) -> Option<Self> {
129        let chain = Chain::from_id(chain_id);
130        if let Some(fork) = EthereumHardfork::from_chain_and_timestamp(chain, timestamp) {
131            return Some(Self::Ethereum(fork));
132        }
133        #[cfg(feature = "optimism")]
134        if let Some(fork) = OpHardfork::from_chain_and_timestamp(chain, timestamp) {
135            return Some(Self::Optimism(fork));
136        }
137        TempoHardfork::from_chain_and_timestamp(chain_id, timestamp).map(Self::Tempo)
138    }
139}
140
141impl From<EthereumHardfork> for FoundryHardfork {
142    fn from(value: EthereumHardfork) -> Self {
143        Self::Ethereum(value)
144    }
145}
146
147impl From<FoundryHardfork> for EthereumHardfork {
148    fn from(fork: FoundryHardfork) -> Self {
149        match fork {
150            FoundryHardfork::Ethereum(hardfork) => hardfork,
151            _ => Self::default(),
152        }
153    }
154}
155
156#[cfg(feature = "optimism")]
157impl From<OpHardfork> for FoundryHardfork {
158    fn from(value: OpHardfork) -> Self {
159        Self::Optimism(value)
160    }
161}
162
163#[cfg(feature = "optimism")]
164impl From<FoundryHardfork> for OpHardfork {
165    fn from(fork: FoundryHardfork) -> Self {
166        match fork {
167            FoundryHardfork::Optimism(hardfork) => hardfork,
168            _ => Self::default(),
169        }
170    }
171}
172
173impl From<TempoHardfork> for FoundryHardfork {
174    fn from(value: TempoHardfork) -> Self {
175        Self::Tempo(value)
176    }
177}
178
179impl From<FoundryHardfork> for TempoHardfork {
180    fn from(fork: FoundryHardfork) -> Self {
181        match fork {
182            FoundryHardfork::Tempo(hardfork) => hardfork,
183            _ => Self::default(),
184        }
185    }
186}
187
188impl From<FoundryHardfork> for SpecId {
189    fn from(fork: FoundryHardfork) -> Self {
190        match fork {
191            FoundryHardfork::Ethereum(hardfork) => spec_id_from_ethereum_hardfork(hardfork),
192            #[cfg(feature = "optimism")]
193            FoundryHardfork::Optimism(hardfork) => eth_spec_id_from_optimism_hardfork(hardfork),
194            FoundryHardfork::Tempo(hardfork) => spec_id_from_tempo_hardfork(hardfork),
195        }
196    }
197}
198
199#[cfg(feature = "optimism")]
200impl From<FoundryHardfork> for OpSpecId {
201    fn from(fork: FoundryHardfork) -> Self {
202        match fork {
203            FoundryHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork),
204            _ => Self::default(),
205        }
206    }
207}
208
209/// Map an `EthereumHardfork` enum into its corresponding `SpecId`.
210pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId {
211    match hardfork {
212        EthereumHardfork::Frontier => SpecId::FRONTIER,
213        EthereumHardfork::Homestead => SpecId::HOMESTEAD,
214        EthereumHardfork::Dao => SpecId::HOMESTEAD,
215        EthereumHardfork::Tangerine => SpecId::TANGERINE,
216        EthereumHardfork::SpuriousDragon => SpecId::SPURIOUS_DRAGON,
217        EthereumHardfork::Byzantium => SpecId::BYZANTIUM,
218        EthereumHardfork::Constantinople => SpecId::PETERSBURG,
219        EthereumHardfork::Petersburg => SpecId::PETERSBURG,
220        EthereumHardfork::Istanbul => SpecId::ISTANBUL,
221        EthereumHardfork::MuirGlacier => SpecId::ISTANBUL,
222        EthereumHardfork::Berlin => SpecId::BERLIN,
223        EthereumHardfork::London => SpecId::LONDON,
224        EthereumHardfork::ArrowGlacier => SpecId::LONDON,
225        EthereumHardfork::GrayGlacier => SpecId::LONDON,
226        EthereumHardfork::Paris => SpecId::MERGE,
227        EthereumHardfork::Shanghai => SpecId::SHANGHAI,
228        EthereumHardfork::Cancun => SpecId::CANCUN,
229        EthereumHardfork::Prague => SpecId::PRAGUE,
230        EthereumHardfork::Osaka => SpecId::OSAKA,
231        EthereumHardfork::Bpo1 | EthereumHardfork::Bpo2 => SpecId::OSAKA,
232        EthereumHardfork::Bpo3 | EthereumHardfork::Bpo4 | EthereumHardfork::Bpo5 => {
233            unimplemented!()
234        }
235        EthereumHardfork::Amsterdam => SpecId::AMSTERDAM,
236        f => unreachable!("unimplemented {}", f),
237    }
238}
239
240/// Map an `OptimismHardfork` enum into its corresponding `OpSpecId`.
241#[cfg(feature = "optimism")]
242pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId {
243    match hardfork {
244        OpHardfork::Bedrock => OpSpecId::BEDROCK,
245        OpHardfork::Regolith => OpSpecId::REGOLITH,
246        OpHardfork::Canyon => OpSpecId::CANYON,
247        OpHardfork::Ecotone => OpSpecId::ECOTONE,
248        OpHardfork::Fjord => OpSpecId::FJORD,
249        OpHardfork::Granite => OpSpecId::GRANITE,
250        OpHardfork::Holocene => OpSpecId::HOLOCENE,
251        OpHardfork::Isthmus => OpSpecId::ISTHMUS,
252        OpHardfork::Jovian => OpSpecId::JOVIAN,
253        OpHardfork::Karst => OpSpecId::KARST,
254        OpHardfork::Interop => OpSpecId::INTEROP,
255        f => unreachable!("unimplemented {}", f),
256    }
257}
258
259/// Map an `OptimismHardfork` enum into its corresponding Ethereum `SpecId`.
260#[cfg(feature = "optimism")]
261pub fn eth_spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> SpecId {
262    match hardfork {
263        OpHardfork::Bedrock | OpHardfork::Regolith => SpecId::MERGE,
264        OpHardfork::Canyon => SpecId::SHANGHAI,
265        OpHardfork::Ecotone | OpHardfork::Fjord | OpHardfork::Granite | OpHardfork::Holocene => {
266            SpecId::CANCUN
267        }
268        OpHardfork::Isthmus | OpHardfork::Jovian | OpHardfork::Interop => SpecId::PRAGUE,
269        OpHardfork::Karst => SpecId::OSAKA,
270        f => unreachable!("unimplemented {}", f),
271    }
272}
273
274/// Map a `TempoHardfork` enum into its corresponding Ethereum `SpecId`.
275pub const fn spec_id_from_tempo_hardfork(_: TempoHardfork) -> SpecId {
276    SpecId::OSAKA
277}
278
279/// Trait for converting an [`EvmVersion`] into a network-specific spec type.
280pub trait FromEvmVersion: From<FoundryHardfork> {
281    fn from_evm_version(version: EvmVersion) -> Self;
282}
283
284/// Trait for parsing and displaying a network-specific execution spec.
285pub trait ExecutionSpec: FromEvmVersion {
286    // Returns the user-facing name for the active execution spec.
287    fn evm_version_name(&self) -> String;
288
289    // Parses an unnamespaced hardfork name for the active network.
290    fn from_network_hardfork(_: &str) -> Option<Self> {
291        None
292    }
293
294    // Converts a namespaced Foundry hardfork if it belongs to this spec family.
295    fn from_foundry_hardfork(hardfork: FoundryHardfork) -> Option<Self>;
296}
297
298impl FromEvmVersion for SpecId {
299    fn from_evm_version(version: EvmVersion) -> Self {
300        match version {
301            EvmVersion::Homestead => Self::HOMESTEAD,
302            EvmVersion::TangerineWhistle => Self::TANGERINE,
303            EvmVersion::SpuriousDragon => Self::SPURIOUS_DRAGON,
304            EvmVersion::Byzantium => Self::BYZANTIUM,
305            EvmVersion::Constantinople => Self::PETERSBURG,
306            EvmVersion::Petersburg => Self::PETERSBURG,
307            EvmVersion::Istanbul => Self::ISTANBUL,
308            EvmVersion::Berlin => Self::BERLIN,
309            EvmVersion::London => Self::LONDON,
310            EvmVersion::Paris => Self::MERGE,
311            EvmVersion::Shanghai => Self::SHANGHAI,
312            EvmVersion::Cancun => Self::CANCUN,
313            EvmVersion::Prague => Self::PRAGUE,
314            EvmVersion::Osaka => Self::OSAKA,
315            EvmVersion::Amsterdam => Self::AMSTERDAM,
316        }
317    }
318}
319
320impl ExecutionSpec for SpecId {
321    // Returns the user-facing name for the active execution spec.
322    fn evm_version_name(&self) -> String {
323        self.to_string()
324    }
325
326    // Parses an unnamespaced Ethereum hardfork name.
327    fn from_network_hardfork(hardfork: &str) -> Option<Self> {
328        EthereumHardfork::from_str(hardfork).ok().map(spec_id_from_ethereum_hardfork)
329    }
330
331    // Converts only Ethereum namespaced hardforks to an Ethereum spec.
332    fn from_foundry_hardfork(hardfork: FoundryHardfork) -> Option<Self> {
333        match hardfork {
334            FoundryHardfork::Ethereum(hardfork) => Some(spec_id_from_ethereum_hardfork(hardfork)),
335            _ => None,
336        }
337    }
338}
339
340#[cfg(feature = "optimism")]
341impl FromEvmVersion for OpSpecId {
342    fn from_evm_version(version: EvmVersion) -> Self {
343        match version {
344            EvmVersion::Homestead
345            | EvmVersion::TangerineWhistle
346            | EvmVersion::SpuriousDragon
347            | EvmVersion::Byzantium
348            | EvmVersion::Constantinople
349            | EvmVersion::Petersburg
350            | EvmVersion::Istanbul
351            | EvmVersion::Berlin
352            | EvmVersion::London
353            | EvmVersion::Paris => Self::BEDROCK,
354            EvmVersion::Shanghai => Self::CANYON,
355            EvmVersion::Cancun => Self::ECOTONE,
356            EvmVersion::Prague => Self::ISTHMUS,
357            EvmVersion::Osaka | EvmVersion::Amsterdam => Self::KARST,
358        }
359    }
360}
361
362#[cfg(feature = "optimism")]
363impl ExecutionSpec for OpSpecId {
364    // Returns the user-facing name for the active execution spec.
365    fn evm_version_name(&self) -> String {
366        let name: &'static str = (*self).into();
367        name.to_string()
368    }
369
370    // Parses an unnamespaced Optimism hardfork name.
371    fn from_network_hardfork(hardfork: &str) -> Option<Self> {
372        OpHardfork::from_str(hardfork).ok().map(spec_id_from_optimism_hardfork)
373    }
374
375    // Converts only Optimism namespaced hardforks to an Optimism spec.
376    fn from_foundry_hardfork(hardfork: FoundryHardfork) -> Option<Self> {
377        match hardfork {
378            FoundryHardfork::Optimism(hardfork) => Some(spec_id_from_optimism_hardfork(hardfork)),
379            _ => None,
380        }
381    }
382}
383
384impl FromEvmVersion for TempoHardfork {
385    fn from_evm_version(_: EvmVersion) -> Self {
386        latest_active_tempo_hardfork()
387    }
388}
389
390impl ExecutionSpec for TempoHardfork {
391    // Returns the user-facing name for the active execution spec.
392    fn evm_version_name(&self) -> String {
393        self.to_string()
394    }
395
396    // Parses an unnamespaced Tempo hardfork name.
397    fn from_network_hardfork(hardfork: &str) -> Option<Self> {
398        Self::from_str(hardfork).ok()
399    }
400
401    // Converts only Tempo namespaced hardforks to a Tempo spec.
402    fn from_foundry_hardfork(hardfork: FoundryHardfork) -> Option<Self> {
403        match hardfork {
404            FoundryHardfork::Tempo(hardfork) => Some(hardfork),
405            _ => None,
406        }
407    }
408}
409
410/// Returns the spec id derived from [`EvmVersion`] for a given spec type.
411pub fn evm_spec_id<SPEC: FromEvmVersion>(evm_version: EvmVersion) -> SPEC {
412    SPEC::from_evm_version(evm_version)
413}
414
415/// Returns the latest Tempo hardfork that has an activation on a known Tempo network.
416pub fn latest_active_tempo_hardfork() -> TempoHardfork {
417    // Tempo currently publishes activation timestamps through chain-aware hardfork resolution.
418    let now = SystemTime::now()
419        .duration_since(UNIX_EPOCH)
420        .map(|duration| duration.as_secs())
421        .unwrap_or(u64::MAX);
422    TempoHardfork::from_chain_and_timestamp(4217, now)
423        .or_else(|| TempoHardfork::from_chain_and_timestamp(42431, now))
424        .unwrap_or_default()
425}
426
427// Parses an EVM version or network-specific hardfork into the given spec type.
428pub fn evm_spec_id_from_str<SPEC: ExecutionSpec>(evm_version: &str) -> Option<SPEC> {
429    let evm_version = evm_version.trim();
430
431    if let Ok(version) = EvmVersion::from_str(evm_version) {
432        return Some(evm_spec_id(version));
433    }
434
435    if let Some(spec) = SPEC::from_network_hardfork(evm_version) {
436        return Some(spec);
437    }
438
439    FoundryHardfork::from_str(evm_version).ok().and_then(SPEC::from_foundry_hardfork)
440}
441
442/// Convert a `BlockNumberOrTag` into an `EthereumHardfork`.
443pub fn ethereum_hardfork_from_block_tag(block: impl Into<BlockNumberOrTag>) -> EthereumHardfork {
444    let num = match block.into() {
445        BlockNumberOrTag::Earliest => 0,
446        BlockNumberOrTag::Number(num) => num,
447        _ => u64::MAX,
448    };
449
450    EthereumHardfork::from_mainnet_block_number(num)
451}
452
453#[cfg(test)]
454mod tests {
455    use super::*;
456    use alloy_hardforks::ethereum::mainnet::*;
457
458    #[test]
459    fn test_ethereum_spec_id_mapping() {
460        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Frontier), SpecId::FRONTIER);
461        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Homestead), SpecId::HOMESTEAD);
462
463        // Test latest hardforks
464        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN);
465        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE);
466        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Osaka), SpecId::OSAKA);
467        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Amsterdam), SpecId::AMSTERDAM);
468    }
469
470    #[test]
471    fn test_tempo_spec_id_mapping() {
472        assert_eq!(spec_id_from_tempo_hardfork(TempoHardfork::Genesis), SpecId::OSAKA);
473        assert_eq!(spec_id_from_tempo_hardfork(TempoHardfork::T8), SpecId::OSAKA);
474    }
475
476    #[test]
477    fn test_tempo_evm_version_defaults_to_latest_active_hardfork() {
478        let latest = latest_active_tempo_hardfork();
479        assert_eq!(evm_spec_id::<TempoHardfork>(EvmVersion::Osaka), latest);
480    }
481
482    #[test]
483    fn test_tempo_hardfork_from_chain_and_timestamp() {
484        assert_eq!(
485            FoundryHardfork::from_chain_and_timestamp(4217, u64::MAX),
486            Some(FoundryHardfork::Tempo(TempoHardfork::T6))
487        );
488        assert_eq!(
489            FoundryHardfork::from_chain_and_timestamp(42431, u64::MAX),
490            Some(FoundryHardfork::Tempo(TempoHardfork::T6))
491        );
492    }
493
494    #[test]
495    fn test_evm_spec_id_from_str_parses_network_hardforks() {
496        assert_eq!(evm_spec_id_from_str::<TempoHardfork>("T3"), Some(TempoHardfork::T3));
497        assert_eq!(evm_spec_id_from_str::<TempoHardfork>("tempo:T2"), Some(TempoHardfork::T2));
498        assert_eq!(evm_spec_id_from_str::<TempoHardfork>("tempo:T7"), Some(TempoHardfork::T7));
499        assert_eq!(evm_spec_id_from_str::<TempoHardfork>("tempo:T8"), Some(TempoHardfork::T8));
500        assert_eq!(evm_spec_id_from_str::<TempoHardfork>("ethereum:prague"), None);
501    }
502
503    #[test]
504    fn test_hardfork_from_block_tag_numbers() {
505        assert_eq!(
506            ethereum_hardfork_from_block_tag(MAINNET_HOMESTEAD_BLOCK - 1),
507            EthereumHardfork::Frontier
508        );
509        assert_eq!(
510            ethereum_hardfork_from_block_tag(MAINNET_LONDON_BLOCK + 1),
511            EthereumHardfork::London
512        );
513    }
514
515    #[test]
516    fn test_from_chain_and_timestamp_ethereum_mainnet() {
517        assert_eq!(
518            FoundryHardfork::from_chain_and_timestamp(1, 0),
519            Some(FoundryHardfork::Ethereum(EthereumHardfork::Frontier))
520        );
521        // Shanghai activated at timestamp 1681338455 on mainnet
522        assert_eq!(
523            FoundryHardfork::from_chain_and_timestamp(1, 1_681_338_455),
524            Some(FoundryHardfork::Ethereum(EthereumHardfork::Shanghai))
525        );
526    }
527
528    #[test]
529    fn test_from_chain_and_timestamp_sepolia() {
530        let sepolia_chain_id = 11155111;
531        assert!(FoundryHardfork::from_chain_and_timestamp(sepolia_chain_id, u64::MAX).is_some());
532    }
533
534    #[test]
535    fn test_from_chain_and_timestamp_unknown_chain() {
536        assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None);
537    }
538
539    #[cfg(feature = "optimism")]
540    mod optimism {
541        use super::*;
542
543        #[test]
544        fn test_optimism_spec_id_mapping() {
545            assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK);
546            assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH);
547
548            // Test latest hardforks
549            assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE);
550            assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Karst), OpSpecId::KARST);
551            assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP);
552            assert_eq!(evm_spec_id::<OpSpecId>(EvmVersion::Osaka), OpSpecId::KARST);
553        }
554
555        #[test]
556        fn test_from_chain_and_timestamp_op_mainnet() {
557            let op_chain_id = 10;
558            assert!(matches!(
559                FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX),
560                Some(FoundryHardfork::Optimism(_))
561            ));
562        }
563
564        #[test]
565        fn test_from_chain_and_timestamp_base() {
566            let base_chain_id = 8453;
567            assert!(matches!(
568                FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX),
569                Some(FoundryHardfork::Optimism(_))
570            ));
571        }
572    }
573}