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    }
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::ISTHMUS,
78    }
79}
80
81/// Convert a `BlockNumberOrTag` into an `EthereumHardfork`.
82pub fn ethereum_hardfork_from_block_tag(block: impl Into<BlockNumberOrTag>) -> EthereumHardfork {
83    let num = match block.into() {
84        BlockNumberOrTag::Earliest => 0,
85        BlockNumberOrTag::Number(num) => num,
86        _ => u64::MAX,
87    };
88
89    EthereumHardfork::from_mainnet_block_number(num)
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use alloy_hardforks::ethereum::mainnet::*;
96    #[allow(unused_imports)]
97    use alloy_rpc_types::BlockNumberOrTag;
98
99    #[test]
100    fn test_ethereum_spec_id_mapping() {
101        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Frontier), SpecId::FRONTIER);
102        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Homestead), SpecId::HOMESTEAD);
103
104        // Test latest hardforks
105        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN);
106        assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE);
107    }
108
109    #[test]
110    fn test_optimism_spec_id_mapping() {
111        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK);
112        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH);
113
114        // Test latest hardforks
115        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE);
116        assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP);
117    }
118
119    #[test]
120    fn test_hardfork_from_block_tag_numbers() {
121        assert_eq!(
122            ethereum_hardfork_from_block_tag(MAINNET_HOMESTEAD_BLOCK - 1),
123            EthereumHardfork::Frontier
124        );
125        assert_eq!(
126            ethereum_hardfork_from_block_tag(MAINNET_LONDON_BLOCK + 1),
127            EthereumHardfork::London
128        );
129    }
130}