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
35pub 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
66pub 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
83pub 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 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 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}