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 }
63}
64
65pub 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
81pub 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 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 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}