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 | EthereumHardfork::Bpo2 => SpecId::OSAKA,
58        EthereumHardfork::Bpo3 | EthereumHardfork::Bpo4 | EthereumHardfork::Bpo5 => {
59            unimplemented!()
60        }
61        f => unreachable!("unimplemented {}", f),
62    }
63}
64
65/// Map an OptimismHardfork enum into its corresponding OpSpecId.
66pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId {
67    match hardfork {
68        OpHardfork::Bedrock => OpSpecId::BEDROCK,
69        OpHardfork::Regolith => OpSpecId::REGOLITH,
70        OpHardfork::Canyon => OpSpecId::CANYON,
71        OpHardfork::Ecotone => OpSpecId::ECOTONE,
72        OpHardfork::Fjord => OpSpecId::FJORD,
73        OpHardfork::Granite => OpSpecId::GRANITE,
74        OpHardfork::Holocene => OpSpecId::HOLOCENE,
75        OpHardfork::Isthmus => OpSpecId::ISTHMUS,
76        OpHardfork::Interop => OpSpecId::INTEROP,
77        OpHardfork::Jovian => OpSpecId::JOVIAN,
78        f => unreachable!("unimplemented {}", f),
79    }
80}
81
82/// Convert a `BlockNumberOrTag` into an `EthereumHardfork`.
83pub fn ethereum_hardfork_from_block_tag(block: impl Into<BlockNumberOrTag>) -> EthereumHardfork {
84    let num = match block.into() {
85        BlockNumberOrTag::Earliest => 0,
86        BlockNumberOrTag::Number(num) => num,
87        _ => u64::MAX,
88    };
89
90    EthereumHardfork::from_mainnet_block_number(num)
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use alloy_hardforks::ethereum::mainnet::*;
97
98    #[test]
99    fn test_ethereum_spec_id_mapping() {
100        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Frontier), SpecId::FRONTIER);
101        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Homestead), SpecId::HOMESTEAD);
102
103        // Test latest hardforks
104        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN);
105        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE);
106    }
107
108    #[test]
109    fn test_optimism_spec_id_mapping() {
110        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK);
111        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH);
112
113        // Test latest hardforks
114        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE);
115        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP);
116    }
117
118    #[test]
119    fn test_hardfork_from_block_tag_numbers() {
120        assert_eq!(
121            ethereum_hardfork_from_block_tag(MAINNET_HOMESTEAD_BLOCK - 1),
122            EthereumHardfork::Frontier
123        );
124        assert_eq!(
125            ethereum_hardfork_from_block_tag(MAINNET_LONDON_BLOCK + 1),
126            EthereumHardfork::London
127        );
128    }
129}