anvil/
hardfork.rs

1use alloy_rpc_types::BlockNumberOrTag;
2use eyre::bail;
3use foundry_evm::revm::primitives::SpecId;
4use std::str::FromStr;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub enum ChainHardfork {
8    Ethereum(EthereumHardfork),
9    Optimism(OptimismHardfork),
10}
11
12impl From<EthereumHardfork> for ChainHardfork {
13    fn from(value: EthereumHardfork) -> Self {
14        Self::Ethereum(value)
15    }
16}
17
18impl From<OptimismHardfork> for ChainHardfork {
19    fn from(value: OptimismHardfork) -> Self {
20        Self::Optimism(value)
21    }
22}
23
24impl From<ChainHardfork> for SpecId {
25    fn from(fork: ChainHardfork) -> Self {
26        match fork {
27            ChainHardfork::Ethereum(hardfork) => hardfork.into(),
28            ChainHardfork::Optimism(hardfork) => hardfork.into(),
29        }
30    }
31}
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub enum EthereumHardfork {
35    Frontier,
36    Homestead,
37    Dao,
38    Tangerine,
39    SpuriousDragon,
40    Byzantium,
41    Constantinople,
42    Petersburg,
43    Istanbul,
44    Muirglacier,
45    Berlin,
46    London,
47    ArrowGlacier,
48    GrayGlacier,
49    Paris,
50    Shanghai,
51    Cancun,
52    Prague,
53    PragueEOF,
54    #[default]
55    Latest,
56}
57
58impl EthereumHardfork {
59    /// Get the first block number of the hardfork.
60    pub fn fork_block(&self) -> u64 {
61        match *self {
62            Self::Frontier => 0,
63            Self::Homestead => 1150000,
64            Self::Dao => 1920000,
65            Self::Tangerine => 2463000,
66            Self::SpuriousDragon => 2675000,
67            Self::Byzantium => 4370000,
68            Self::Constantinople | Self::Petersburg => 7280000,
69            Self::Istanbul => 9069000,
70            Self::Muirglacier => 9200000,
71            Self::Berlin => 12244000,
72            Self::London => 12965000,
73            Self::ArrowGlacier => 13773000,
74            Self::GrayGlacier => 15050000,
75            Self::Paris => 15537394,
76            Self::Shanghai => 17034870,
77            Self::Cancun | Self::Latest => 19426587,
78            // TODO: add block after activation
79            Self::Prague | Self::PragueEOF => unreachable!(),
80        }
81    }
82}
83
84impl FromStr for EthereumHardfork {
85    type Err = eyre::Report;
86
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        let s = s.to_lowercase();
89        let hardfork = match s.as_str() {
90            "frontier" | "1" => Self::Frontier,
91            "homestead" | "2" => Self::Homestead,
92            "dao" | "3" => Self::Dao,
93            "tangerine" | "4" => Self::Tangerine,
94            "spuriousdragon" | "5" => Self::SpuriousDragon,
95            "byzantium" | "6" => Self::Byzantium,
96            "constantinople" | "7" => Self::Constantinople,
97            "petersburg" | "8" => Self::Petersburg,
98            "istanbul" | "9" => Self::Istanbul,
99            "muirglacier" | "10" => Self::Muirglacier,
100            "berlin" | "11" => Self::Berlin,
101            "london" | "12" => Self::London,
102            "arrowglacier" | "13" => Self::ArrowGlacier,
103            "grayglacier" | "14" => Self::GrayGlacier,
104            "paris" | "merge" | "15" => Self::Paris,
105            "shanghai" | "16" => Self::Shanghai,
106            "cancun" | "17" => Self::Cancun,
107            "prague" | "18" => Self::Prague,
108            "pragueeof" | "19" | "prague-eof" => Self::PragueEOF,
109            "latest" => Self::Latest,
110            _ => bail!("Unknown hardfork {s}"),
111        };
112        Ok(hardfork)
113    }
114}
115
116impl From<EthereumHardfork> for SpecId {
117    fn from(fork: EthereumHardfork) -> Self {
118        match fork {
119            EthereumHardfork::Frontier => Self::FRONTIER,
120            EthereumHardfork::Homestead => Self::HOMESTEAD,
121            EthereumHardfork::Dao => Self::HOMESTEAD,
122            EthereumHardfork::Tangerine => Self::TANGERINE,
123            EthereumHardfork::SpuriousDragon => Self::SPURIOUS_DRAGON,
124            EthereumHardfork::Byzantium => Self::BYZANTIUM,
125            EthereumHardfork::Constantinople => Self::CONSTANTINOPLE,
126            EthereumHardfork::Petersburg => Self::PETERSBURG,
127            EthereumHardfork::Istanbul => Self::ISTANBUL,
128            EthereumHardfork::Muirglacier => Self::MUIR_GLACIER,
129            EthereumHardfork::Berlin => Self::BERLIN,
130            EthereumHardfork::London => Self::LONDON,
131            EthereumHardfork::ArrowGlacier => Self::LONDON,
132            EthereumHardfork::GrayGlacier => Self::GRAY_GLACIER,
133            EthereumHardfork::Paris => Self::MERGE,
134            EthereumHardfork::Shanghai => Self::SHANGHAI,
135            EthereumHardfork::Cancun | EthereumHardfork::Latest => Self::CANCUN,
136            EthereumHardfork::Prague => Self::PRAGUE,
137            // TODO: switch to latest after activation
138            // EOF is included in OSAKA from Revm 16.0.0
139            EthereumHardfork::PragueEOF => Self::OSAKA,
140        }
141    }
142}
143
144impl<T: Into<BlockNumberOrTag>> From<T> for EthereumHardfork {
145    fn from(block: T) -> Self {
146        let num = match block.into() {
147            BlockNumberOrTag::Earliest => 0,
148            BlockNumberOrTag::Number(num) => num,
149            _ => u64::MAX,
150        };
151
152        match num {
153            _i if num < 1_150_000 => Self::Frontier,
154            _i if num < 1_920_000 => Self::Dao,
155            _i if num < 2_463_000 => Self::Homestead,
156            _i if num < 2_675_000 => Self::Tangerine,
157            _i if num < 4_370_000 => Self::SpuriousDragon,
158            _i if num < 7_280_000 => Self::Byzantium,
159            _i if num < 9_069_000 => Self::Constantinople,
160            _i if num < 9_200_000 => Self::Istanbul,
161            _i if num < 12_244_000 => Self::Muirglacier,
162            _i if num < 12_965_000 => Self::Berlin,
163            _i if num < 13_773_000 => Self::London,
164            _i if num < 15_050_000 => Self::ArrowGlacier,
165            _i if num < 17_034_870 => Self::Paris,
166            _i if num < 19_426_587 => Self::Shanghai,
167            _ => Self::Latest,
168        }
169    }
170}
171
172#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
173pub enum OptimismHardfork {
174    Bedrock,
175    Regolith,
176    Canyon,
177    Ecotone,
178    Fjord,
179    Granite,
180    Holocene,
181    Isthmus,
182    #[default]
183    Latest,
184}
185
186impl FromStr for OptimismHardfork {
187    type Err = eyre::Report;
188
189    fn from_str(s: &str) -> Result<Self, Self::Err> {
190        let s = s.to_lowercase();
191        let hardfork = match s.as_str() {
192            "bedrock" => Self::Bedrock,
193            "regolith" => Self::Regolith,
194            "canyon" => Self::Canyon,
195            "ecotone" => Self::Ecotone,
196            "fjord" => Self::Fjord,
197            "granite" => Self::Granite,
198            "holocene" => Self::Holocene,
199            "isthmus" => Self::Isthmus,
200            "latest" => Self::Latest,
201            _ => bail!("Unknown hardfork {s}"),
202        };
203        Ok(hardfork)
204    }
205}
206
207impl From<OptimismHardfork> for SpecId {
208    fn from(fork: OptimismHardfork) -> Self {
209        match fork {
210            OptimismHardfork::Bedrock => Self::BEDROCK,
211            OptimismHardfork::Regolith => Self::REGOLITH,
212            OptimismHardfork::Canyon => Self::CANYON,
213            OptimismHardfork::Ecotone => Self::ECOTONE,
214            OptimismHardfork::Fjord => Self::FJORD,
215            OptimismHardfork::Granite => Self::GRANITE,
216            OptimismHardfork::Holocene => Self::HOLOCENE,
217            OptimismHardfork::Isthmus => Self::ISTHMUS,
218            OptimismHardfork::Latest => Self::LATEST,
219        }
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use crate::EthereumHardfork;
226
227    #[test]
228    fn test_hardfork_blocks() {
229        let hf: EthereumHardfork = 12_965_000u64.into();
230        assert_eq!(hf, EthereumHardfork::London);
231
232        let hf: EthereumHardfork = 4370000u64.into();
233        assert_eq!(hf, EthereumHardfork::Byzantium);
234
235        let hf: EthereumHardfork = 12244000u64.into();
236        assert_eq!(hf, EthereumHardfork::Berlin);
237    }
238}