foundry_evm_core/
hardfork.rs

1use alloy_rpc_types::BlockNumberOrTag;
2use op_revm::OpSpecId;
3use revm::primitives::hardfork::SpecId;
4
5pub use alloy_hardforks::EthereumHardfork;
6pub use alloy_op_hardforks::OpHardfork;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum FoundryHardfork {
10    Ethereum(EthereumHardfork),
11    Optimism(OpHardfork),
12}
13
14impl FoundryHardfork {
15    pub fn ethereum(h: EthereumHardfork) -> Self {
16        Self::Ethereum(h)
17    }
18
19    pub fn optimism(h: OpHardfork) -> Self {
20        Self::Optimism(h)
21    }
22}
23
24impl From<EthereumHardfork> for FoundryHardfork {
25    fn from(value: EthereumHardfork) -> Self {
26        Self::Ethereum(value)
27    }
28}
29
30impl From<OpHardfork> for FoundryHardfork {
31    fn from(value: OpHardfork) -> Self {
32        Self::Optimism(value)
33    }
34}
35
36impl From<FoundryHardfork> for SpecId {
37    fn from(fork: FoundryHardfork) -> Self {
38        match fork {
39            FoundryHardfork::Ethereum(hardfork) => spec_id_from_ethereum_hardfork(hardfork),
40            FoundryHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork).into(),
41        }
42    }
43}
44
45/// Map an EthereumHardfork enum into its corresponding SpecId.
46pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId {
47    match hardfork {
48        EthereumHardfork::Frontier => SpecId::FRONTIER,
49        EthereumHardfork::Homestead => SpecId::HOMESTEAD,
50        EthereumHardfork::Dao => SpecId::DAO_FORK,
51        EthereumHardfork::Tangerine => SpecId::TANGERINE,
52        EthereumHardfork::SpuriousDragon => SpecId::SPURIOUS_DRAGON,
53        EthereumHardfork::Byzantium => SpecId::BYZANTIUM,
54        EthereumHardfork::Constantinople => SpecId::CONSTANTINOPLE,
55        EthereumHardfork::Petersburg => SpecId::PETERSBURG,
56        EthereumHardfork::Istanbul => SpecId::ISTANBUL,
57        EthereumHardfork::MuirGlacier => SpecId::MUIR_GLACIER,
58        EthereumHardfork::Berlin => SpecId::BERLIN,
59        EthereumHardfork::London => SpecId::LONDON,
60        EthereumHardfork::ArrowGlacier => SpecId::ARROW_GLACIER,
61        EthereumHardfork::GrayGlacier => SpecId::GRAY_GLACIER,
62        EthereumHardfork::Paris => SpecId::MERGE,
63        EthereumHardfork::Shanghai => SpecId::SHANGHAI,
64        EthereumHardfork::Cancun => SpecId::CANCUN,
65        EthereumHardfork::Prague => SpecId::PRAGUE,
66        EthereumHardfork::Osaka => SpecId::OSAKA,
67        EthereumHardfork::Bpo1 | EthereumHardfork::Bpo2 => SpecId::OSAKA,
68        EthereumHardfork::Bpo3 | EthereumHardfork::Bpo4 | EthereumHardfork::Bpo5 => {
69            unimplemented!()
70        }
71        f => unreachable!("unimplemented {}", f),
72    }
73}
74
75/// Map an OptimismHardfork enum into its corresponding OpSpecId.
76pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId {
77    match hardfork {
78        OpHardfork::Bedrock => OpSpecId::BEDROCK,
79        OpHardfork::Regolith => OpSpecId::REGOLITH,
80        OpHardfork::Canyon => OpSpecId::CANYON,
81        OpHardfork::Ecotone => OpSpecId::ECOTONE,
82        OpHardfork::Fjord => OpSpecId::FJORD,
83        OpHardfork::Granite => OpSpecId::GRANITE,
84        OpHardfork::Holocene => OpSpecId::HOLOCENE,
85        OpHardfork::Isthmus => OpSpecId::ISTHMUS,
86        OpHardfork::Interop => OpSpecId::INTEROP,
87        OpHardfork::Jovian => OpSpecId::JOVIAN,
88        f => unreachable!("unimplemented {}", f),
89    }
90}
91
92/// Convert a `BlockNumberOrTag` into an `EthereumHardfork`.
93pub fn ethereum_hardfork_from_block_tag(block: impl Into<BlockNumberOrTag>) -> EthereumHardfork {
94    let num = match block.into() {
95        BlockNumberOrTag::Earliest => 0,
96        BlockNumberOrTag::Number(num) => num,
97        _ => u64::MAX,
98    };
99
100    EthereumHardfork::from_mainnet_block_number(num)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use alloy_hardforks::ethereum::mainnet::*;
107
108    #[test]
109    fn test_ethereum_spec_id_mapping() {
110        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Frontier), SpecId::FRONTIER);
111        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Homestead), SpecId::HOMESTEAD);
112
113        // Test latest hardforks
114        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN);
115        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE);
116    }
117
118    #[test]
119    fn test_optimism_spec_id_mapping() {
120        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK);
121        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH);
122
123        // Test latest hardforks
124        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE);
125        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP);
126    }
127
128    #[test]
129    fn test_hardfork_from_block_tag_numbers() {
130        assert_eq!(
131            ethereum_hardfork_from_block_tag(MAINNET_HOMESTEAD_BLOCK - 1),
132            EthereumHardfork::Frontier
133        );
134        assert_eq!(
135            ethereum_hardfork_from_block_tag(MAINNET_LONDON_BLOCK + 1),
136            EthereumHardfork::London
137        );
138    }
139}