anvil/
hardfork.rs

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