alloy_hardforks/hardfork/
ethereum.rs

1use crate::{
2    arbitrum::{mainnet::*, sepolia::*},
3    ethereum::{holesky::*, hoodi::*, mainnet::*, sepolia::*},
4    hardfork, ForkCondition,
5};
6use alloc::vec::Vec;
7use alloy_chains::Chain;
8use alloy_primitives::U256;
9
10hardfork!(
11    /// The name of an Ethereum hardfork.
12    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13    EthereumHardfork {
14        /// Frontier: <https://blog.ethereum.org/2015/03/03/ethereum-launch-process>.
15        Frontier,
16        /// Homestead: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md>.
17        Homestead,
18        /// The DAO fork: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md>.
19        Dao,
20        /// Tangerine: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md>.
21        Tangerine,
22        /// Spurious Dragon: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md>.
23        SpuriousDragon,
24        /// Byzantium: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md>.
25        Byzantium,
26        /// Constantinople: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md>.
27        Constantinople,
28        /// Petersburg: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md>.
29        Petersburg,
30        /// Istanbul: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md>.
31        Istanbul,
32        /// Muir Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md>.
33        MuirGlacier,
34        /// Berlin: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md>.
35        Berlin,
36        /// London: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md>.
37        London,
38        /// Arrow Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md>.
39        ArrowGlacier,
40        /// Gray Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md>.
41        GrayGlacier,
42        /// Paris: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md>.
43        Paris,
44        /// Shanghai: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md>.
45        Shanghai,
46        /// Cancun: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md>
47        Cancun,
48        /// Prague.
49        Prague,
50        /// Osaka: <https://eips.ethereum.org/EIPS/eip-7607>
51        Osaka,
52    }
53);
54
55impl EthereumHardfork {
56    /// Retrieves the activation block for the specified hardfork on the given chain.
57    pub fn activation_block(&self, chain: Chain) -> Option<u64> {
58        if chain == Chain::mainnet() {
59            return self.mainnet_activation_block();
60        }
61        if chain == Chain::sepolia() {
62            return self.sepolia_activation_block();
63        }
64        if chain == Chain::holesky() {
65            return self.holesky_activation_block();
66        }
67        if chain == Chain::hoodi() {
68            return self.hoodi_activation_block();
69        }
70
71        None
72    }
73
74    /// Retrieves the activation block for the specified hardfork on the Ethereum mainnet.
75    pub const fn mainnet_activation_block(&self) -> Option<u64> {
76        match self {
77            Self::Frontier => Some(MAINNET_FRONTIER_BLOCK),
78            Self::Homestead => Some(MAINNET_HOMESTEAD_BLOCK),
79            Self::Dao => Some(MAINNET_DAO_BLOCK),
80            Self::Tangerine => Some(MAINNET_TANGERINE_BLOCK),
81            Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_BLOCK),
82            Self::Byzantium => Some(MAINNET_BYZANTIUM_BLOCK),
83            Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_BLOCK),
84            Self::Petersburg => Some(MAINNET_PETERSBURG_BLOCK),
85            Self::Istanbul => Some(MAINNET_ISTANBUL_BLOCK),
86            Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_BLOCK),
87            Self::Berlin => Some(MAINNET_BERLIN_BLOCK),
88            Self::London => Some(MAINNET_LONDON_BLOCK),
89            Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_BLOCK),
90            Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_BLOCK),
91            Self::Paris => Some(MAINNET_PARIS_BLOCK),
92            Self::Shanghai => Some(MAINNET_SHANGHAI_BLOCK),
93            Self::Cancun => Some(MAINNET_CANCUN_BLOCK),
94            _ => None,
95        }
96    }
97
98    /// Retrieves the activation block for the specified hardfork on the Sepolia testnet.
99    pub const fn sepolia_activation_block(&self) -> Option<u64> {
100        match self {
101            Self::Paris => Some(SEPOLIA_PARIS_BLOCK),
102            Self::Shanghai => Some(SEPOLIA_SHANGHAI_BLOCK),
103            Self::Cancun => Some(SEPOLIA_CANCUN_BLOCK),
104            Self::Frontier
105            | Self::Homestead
106            | Self::Dao
107            | Self::Tangerine
108            | Self::SpuriousDragon
109            | Self::Byzantium
110            | Self::Constantinople
111            | Self::Petersburg
112            | Self::Istanbul
113            | Self::MuirGlacier
114            | Self::Berlin
115            | Self::London
116            | Self::ArrowGlacier
117            | Self::GrayGlacier => Some(0),
118            _ => None,
119        }
120    }
121
122    /// Retrieves the activation block for the specified hardfork on the holesky testnet.
123    const fn holesky_activation_block(&self) -> Option<u64> {
124        match self {
125            Self::Frontier
126            | Self::Dao
127            | Self::Tangerine
128            | Self::SpuriousDragon
129            | Self::Byzantium
130            | Self::Constantinople
131            | Self::Petersburg
132            | Self::Istanbul
133            | Self::MuirGlacier
134            | Self::Berlin
135            | Self::London
136            | Self::ArrowGlacier
137            | Self::GrayGlacier
138            | Self::Paris => Some(0),
139            Self::Shanghai => Some(HOLESKY_SHANGHAI_BLOCK),
140            Self::Cancun => Some(HOLESKY_CANCUN_BLOCK),
141            _ => None,
142        }
143    }
144
145    /// Retrieves the activation block for the specified hardfork on the hoodi testnet.
146    const fn hoodi_activation_block(&self) -> Option<u64> {
147        match self {
148            Self::Frontier
149            | Self::Dao
150            | Self::Tangerine
151            | Self::SpuriousDragon
152            | Self::Byzantium
153            | Self::Constantinople
154            | Self::Petersburg
155            | Self::Istanbul
156            | Self::MuirGlacier
157            | Self::Berlin
158            | Self::London
159            | Self::ArrowGlacier
160            | Self::GrayGlacier
161            | Self::Paris
162            | Self::Shanghai
163            | Self::Cancun => Some(0),
164            _ => None,
165        }
166    }
167
168    /// Retrieves the activation block for the specified hardfork on the Arbitrum Sepolia testnet.
169    pub const fn arbitrum_sepolia_activation_block(&self) -> Option<u64> {
170        match self {
171            Self::Frontier
172            | Self::Homestead
173            | Self::Dao
174            | Self::Tangerine
175            | Self::SpuriousDragon
176            | Self::Byzantium
177            | Self::Constantinople
178            | Self::Petersburg
179            | Self::Istanbul
180            | Self::MuirGlacier
181            | Self::Berlin
182            | Self::London
183            | Self::ArrowGlacier
184            | Self::GrayGlacier
185            | Self::Paris => Some(0),
186            Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_BLOCK),
187            // Hardfork::ArbOS11 => Some(10653737),
188            Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_BLOCK),
189            // Hardfork::ArbOS20Atlas => Some(18683405),
190            _ => None,
191        }
192    }
193
194    /// Retrieves the activation block for the specified hardfork on the Arbitrum One mainnet.
195    pub const fn arbitrum_activation_block(&self) -> Option<u64> {
196        match self {
197            Self::Frontier
198            | Self::Homestead
199            | Self::Dao
200            | Self::Tangerine
201            | Self::SpuriousDragon
202            | Self::Byzantium
203            | Self::Constantinople
204            | Self::Petersburg
205            | Self::Istanbul
206            | Self::MuirGlacier
207            | Self::Berlin
208            | Self::London
209            | Self::ArrowGlacier
210            | Self::GrayGlacier
211            | Self::Paris => Some(0),
212            Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_BLOCK),
213            // Hardfork::ArbOS11 => Some(184097479),
214            Self::Cancun => Some(ARBITRUM_ONE_CANCUN_BLOCK),
215            // Hardfork::ArbOS20Atlas => Some(190301729),
216            _ => None,
217        }
218    }
219
220    /// Retrieves the activation timestamp for the specified hardfork on the given chain.
221    pub fn activation_timestamp(&self, chain: Chain) -> Option<u64> {
222        if chain == Chain::mainnet() {
223            return self.mainnet_activation_timestamp();
224        }
225        if chain == Chain::sepolia() {
226            return self.sepolia_activation_timestamp();
227        }
228        if chain == Chain::holesky() {
229            return self.holesky_activation_timestamp();
230        }
231        if chain == Chain::hoodi() {
232            return self.hoodi_activation_timestamp();
233        }
234
235        None
236    }
237
238    /// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet.
239    pub const fn mainnet_activation_timestamp(&self) -> Option<u64> {
240        match self {
241            Self::Frontier => Some(MAINNET_FRONTIER_TIMESTAMP),
242            Self::Homestead => Some(MAINNET_HOMESTEAD_TIMESTAMP),
243            Self::Dao => Some(MAINNET_DAO_TIMESTAMP),
244            Self::Tangerine => Some(MAINNET_TANGERINE_TIMESTAMP),
245            Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_TIMESTAMP),
246            Self::Byzantium => Some(MAINNET_BYZANTIUM_TIMESTAMP),
247            Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_TIMESTAMP),
248            Self::Petersburg => Some(MAINNET_PETERSBURG_TIMESTAMP),
249            Self::Istanbul => Some(MAINNET_ISTANBUL_TIMESTAMP),
250            Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_TIMESTAMP),
251            Self::Berlin => Some(MAINNET_BERLIN_TIMESTAMP),
252            Self::London => Some(MAINNET_LONDON_TIMESTAMP),
253            Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_TIMESTAMP),
254            Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_TIMESTAMP),
255            Self::Paris => Some(MAINNET_PARIS_TIMESTAMP),
256            Self::Shanghai => Some(MAINNET_SHANGHAI_TIMESTAMP),
257            Self::Cancun => Some(MAINNET_CANCUN_TIMESTAMP),
258            Self::Prague => Some(MAINNET_PRAGUE_TIMESTAMP),
259            // upcoming hardforks
260            _ => None,
261        }
262    }
263
264    /// Retrieves the activation timestamp for the specified hardfork on the Sepolia testnet.
265    pub const fn sepolia_activation_timestamp(&self) -> Option<u64> {
266        match self {
267            Self::Frontier
268            | Self::Homestead
269            | Self::Dao
270            | Self::Tangerine
271            | Self::SpuriousDragon
272            | Self::Byzantium
273            | Self::Constantinople
274            | Self::Petersburg
275            | Self::Istanbul
276            | Self::MuirGlacier
277            | Self::Berlin
278            | Self::London
279            | Self::ArrowGlacier
280            | Self::GrayGlacier
281            | Self::Paris => Some(SEPOLIA_PARIS_TIMESTAMP),
282            Self::Shanghai => Some(SEPOLIA_SHANGHAI_TIMESTAMP),
283            Self::Cancun => Some(SEPOLIA_CANCUN_TIMESTAMP),
284            _ => None,
285        }
286    }
287
288    /// Retrieves the activation timestamp for the specified hardfork on the Holesky testnet.
289    pub const fn holesky_activation_timestamp(&self) -> Option<u64> {
290        match self {
291            Self::Frontier
292            | Self::Homestead
293            | Self::Dao
294            | Self::Tangerine
295            | Self::SpuriousDragon
296            | Self::Byzantium
297            | Self::Constantinople
298            | Self::Petersburg
299            | Self::Istanbul
300            | Self::MuirGlacier
301            | Self::Berlin
302            | Self::London
303            | Self::ArrowGlacier
304            | Self::GrayGlacier
305            | Self::Paris => Some(HOLESKY_PARIS_TIMESTAMP),
306            Self::Shanghai => Some(HOLESKY_SHANGHAI_TIMESTAMP),
307            Self::Cancun => Some(HOLESKY_CANCUN_TIMESTAMP),
308            _ => None,
309        }
310    }
311
312    /// Retrieves the activation timestamp for the specified hardfork on the Hoodi testnet.
313    pub const fn hoodi_activation_timestamp(&self) -> Option<u64> {
314        match self {
315            Self::Prague => Some(HOODI_PRAGUE_TIMESTAMP),
316            Self::Frontier
317            | Self::Homestead
318            | Self::Dao
319            | Self::Tangerine
320            | Self::SpuriousDragon
321            | Self::Byzantium
322            | Self::Constantinople
323            | Self::Petersburg
324            | Self::Istanbul
325            | Self::MuirGlacier
326            | Self::Berlin
327            | Self::London
328            | Self::ArrowGlacier
329            | Self::GrayGlacier
330            | Self::Paris
331            | Self::Shanghai
332            | Self::Cancun => Some(0),
333            _ => None,
334        }
335    }
336
337    /// Retrieves the activation timestamp for the specified hardfork on the Arbitrum Sepolia
338    /// testnet.
339    pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option<u64> {
340        match self {
341            Self::Frontier
342            | Self::Homestead
343            | Self::Dao
344            | Self::Tangerine
345            | Self::SpuriousDragon
346            | Self::Byzantium
347            | Self::Constantinople
348            | Self::Petersburg
349            | Self::Istanbul
350            | Self::MuirGlacier
351            | Self::Berlin
352            | Self::London
353            | Self::ArrowGlacier
354            | Self::GrayGlacier
355            | Self::Paris => Some(ARBITRUM_SEPOLIA_PARIS_TIMESTAMP),
356            Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP),
357            // Hardfork::ArbOS11 => Some(1706634000),
358            Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP),
359            // Hardfork::ArbOS20Atlas => Some(1709229600),
360            _ => None,
361        }
362    }
363
364    /// Retrieves the activation timestamp for the specified hardfork on the Arbitrum One mainnet.
365    pub const fn arbitrum_activation_timestamp(&self) -> Option<u64> {
366        match self {
367            Self::Frontier
368            | Self::Homestead
369            | Self::Dao
370            | Self::Tangerine
371            | Self::SpuriousDragon
372            | Self::Byzantium
373            | Self::Constantinople
374            | Self::Petersburg
375            | Self::Istanbul
376            | Self::MuirGlacier
377            | Self::Berlin
378            | Self::London
379            | Self::ArrowGlacier
380            | Self::GrayGlacier
381            | Self::Paris => Some(ARBITRUM_ONE_PARIS_TIMESTAMP),
382            Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_TIMESTAMP),
383            // Hardfork::ArbOS11 => Some(1708804873),
384            Self::Cancun => Some(ARBITRUM_ONE_CANCUN_TIMESTAMP),
385            // Hardfork::ArbOS20Atlas => Some(1710424089),
386            _ => None,
387        }
388    }
389
390    /// Ethereum mainnet list of hardforks.
391    pub const fn mainnet() -> [(Self, ForkCondition); 18] {
392        [
393            (Self::Frontier, ForkCondition::Block(MAINNET_FRONTIER_BLOCK)),
394            (Self::Homestead, ForkCondition::Block(MAINNET_HOMESTEAD_BLOCK)),
395            (Self::Dao, ForkCondition::Block(MAINNET_DAO_BLOCK)),
396            (Self::Tangerine, ForkCondition::Block(MAINNET_TANGERINE_BLOCK)),
397            (Self::SpuriousDragon, ForkCondition::Block(MAINNET_SPURIOUS_DRAGON_BLOCK)),
398            (Self::Byzantium, ForkCondition::Block(MAINNET_BYZANTIUM_BLOCK)),
399            (Self::Constantinople, ForkCondition::Block(MAINNET_CONSTANTINOPLE_BLOCK)),
400            (Self::Petersburg, ForkCondition::Block(MAINNET_PETERSBURG_BLOCK)),
401            (Self::Istanbul, ForkCondition::Block(MAINNET_ISTANBUL_BLOCK)),
402            (Self::MuirGlacier, ForkCondition::Block(MAINNET_MUIR_GLACIER_BLOCK)),
403            (Self::Berlin, ForkCondition::Block(MAINNET_BERLIN_BLOCK)),
404            (Self::London, ForkCondition::Block(MAINNET_LONDON_BLOCK)),
405            (Self::ArrowGlacier, ForkCondition::Block(MAINNET_ARROW_GLACIER_BLOCK)),
406            (Self::GrayGlacier, ForkCondition::Block(MAINNET_GRAY_GLACIER_BLOCK)),
407            (
408                Self::Paris,
409                ForkCondition::TTD {
410                    activation_block_number: MAINNET_PARIS_BLOCK,
411                    fork_block: None,
412                    total_difficulty: MAINNET_PARIS_TTD,
413                },
414            ),
415            (Self::Shanghai, ForkCondition::Timestamp(MAINNET_SHANGHAI_TIMESTAMP)),
416            (Self::Cancun, ForkCondition::Timestamp(MAINNET_CANCUN_TIMESTAMP)),
417            (Self::Prague, ForkCondition::Timestamp(MAINNET_PRAGUE_TIMESTAMP)),
418        ]
419    }
420
421    /// Ethereum sepolia list of hardforks.
422    pub const fn sepolia() -> [(Self, ForkCondition); 16] {
423        [
424            (Self::Frontier, ForkCondition::Block(0)),
425            (Self::Homestead, ForkCondition::Block(0)),
426            (Self::Dao, ForkCondition::Block(0)),
427            (Self::Tangerine, ForkCondition::Block(0)),
428            (Self::SpuriousDragon, ForkCondition::Block(0)),
429            (Self::Byzantium, ForkCondition::Block(0)),
430            (Self::Constantinople, ForkCondition::Block(0)),
431            (Self::Petersburg, ForkCondition::Block(0)),
432            (Self::Istanbul, ForkCondition::Block(0)),
433            (Self::MuirGlacier, ForkCondition::Block(0)),
434            (Self::Berlin, ForkCondition::Block(0)),
435            (Self::London, ForkCondition::Block(0)),
436            (
437                Self::Paris,
438                ForkCondition::TTD {
439                    activation_block_number: SEPOLIA_PARIS_BLOCK,
440                    fork_block: Some(SEPOLIA_PARIS_FORK_BLOCK),
441                    total_difficulty: SEPOLIA_PARIS_TTD,
442                },
443            ),
444            (Self::Shanghai, ForkCondition::Timestamp(SEPOLIA_SHANGHAI_TIMESTAMP)),
445            (Self::Cancun, ForkCondition::Timestamp(SEPOLIA_CANCUN_TIMESTAMP)),
446            (Self::Prague, ForkCondition::Timestamp(SEPOLIA_PRAGUE_TIMESTAMP)),
447        ]
448    }
449
450    /// Ethereum holesky list of hardforks.
451    pub const fn holesky() -> [(Self, ForkCondition); 16] {
452        [
453            (Self::Frontier, ForkCondition::Block(0)),
454            (Self::Homestead, ForkCondition::Block(0)),
455            (Self::Dao, ForkCondition::Block(0)),
456            (Self::Tangerine, ForkCondition::Block(0)),
457            (Self::SpuriousDragon, ForkCondition::Block(0)),
458            (Self::Byzantium, ForkCondition::Block(0)),
459            (Self::Constantinople, ForkCondition::Block(0)),
460            (Self::Petersburg, ForkCondition::Block(0)),
461            (Self::Istanbul, ForkCondition::Block(0)),
462            (Self::MuirGlacier, ForkCondition::Block(0)),
463            (Self::Berlin, ForkCondition::Block(0)),
464            (Self::London, ForkCondition::Block(0)),
465            (
466                Self::Paris,
467                ForkCondition::TTD {
468                    activation_block_number: 0,
469                    fork_block: Some(0),
470                    total_difficulty: U256::ZERO,
471                },
472            ),
473            (Self::Shanghai, ForkCondition::Timestamp(HOLESKY_SHANGHAI_TIMESTAMP)),
474            (Self::Cancun, ForkCondition::Timestamp(HOLESKY_CANCUN_TIMESTAMP)),
475            (Self::Prague, ForkCondition::Timestamp(HOLESKY_PRAGUE_TIMESTAMP)),
476        ]
477    }
478
479    /// Ethereum Hoodi list of hardforks.
480    pub const fn hoodi() -> [(Self, ForkCondition); 16] {
481        [
482            (Self::Frontier, ForkCondition::Block(0)),
483            (Self::Homestead, ForkCondition::Block(0)),
484            (Self::Dao, ForkCondition::Block(0)),
485            (Self::Tangerine, ForkCondition::Block(0)),
486            (Self::SpuriousDragon, ForkCondition::Block(0)),
487            (Self::Byzantium, ForkCondition::Block(0)),
488            (Self::Constantinople, ForkCondition::Block(0)),
489            (Self::Petersburg, ForkCondition::Block(0)),
490            (Self::Istanbul, ForkCondition::Block(0)),
491            (Self::MuirGlacier, ForkCondition::Block(0)),
492            (Self::Berlin, ForkCondition::Block(0)),
493            (Self::London, ForkCondition::Block(0)),
494            (
495                Self::Paris,
496                ForkCondition::TTD {
497                    activation_block_number: 0,
498                    fork_block: Some(0),
499                    total_difficulty: U256::ZERO,
500                },
501            ),
502            (Self::Shanghai, ForkCondition::Timestamp(0)),
503            (Self::Cancun, ForkCondition::Timestamp(0)),
504            (Self::Prague, ForkCondition::Timestamp(HOODI_PRAGUE_TIMESTAMP)),
505        ]
506    }
507}
508
509/// Helper methods for Ethereum forks.
510#[auto_impl::auto_impl(&, Arc)]
511pub trait EthereumHardforks {
512    /// Retrieves [`ForkCondition`] by an [`EthereumHardfork`]. If `fork` is not present, returns
513    /// [`ForkCondition::Never`].
514    fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition;
515
516    /// Convenience method to check if an [`EthereumHardfork`] is active at a given timestamp.
517    fn is_ethereum_fork_active_at_timestamp(&self, fork: EthereumHardfork, timestamp: u64) -> bool {
518        self.ethereum_fork_activation(fork).active_at_timestamp(timestamp)
519    }
520
521    /// Convenience method to check if an [`EthereumHardfork`] is active at a given block number.
522    fn is_ethereum_fork_active_at_block(&self, fork: EthereumHardfork, block_number: u64) -> bool {
523        self.ethereum_fork_activation(fork).active_at_block(block_number)
524    }
525
526    /// Convenience method to check if [`EthereumHardfork::Shanghai`] is active at a given
527    /// timestamp.
528    fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
529        self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
530    }
531
532    /// Convenience method to check if [`EthereumHardfork::Cancun`] is active at a given timestamp.
533    fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
534        self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
535    }
536
537    /// Convenience method to check if [`EthereumHardfork::Prague`] is active at a given timestamp.
538    fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
539        self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
540    }
541
542    /// Convenience method to check if [`EthereumHardfork::Osaka`] is active at a given timestamp.
543    fn is_osaka_active_at_timestamp(&self, timestamp: u64) -> bool {
544        self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp)
545    }
546
547    /// Convenience method to check if [`EthereumHardfork::Byzantium`] is active at a given block
548    /// number.
549    fn is_byzantium_active_at_block(&self, block_number: u64) -> bool {
550        self.is_ethereum_fork_active_at_block(EthereumHardfork::Byzantium, block_number)
551    }
552
553    /// Convenience method to check if [`EthereumHardfork::SpuriousDragon`] is active at a given
554    /// block number.
555    fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool {
556        self.is_ethereum_fork_active_at_block(EthereumHardfork::SpuriousDragon, block_number)
557    }
558
559    /// Convenience method to check if [`EthereumHardfork::Homestead`] is active at a given block
560    /// number.
561    fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
562        self.is_ethereum_fork_active_at_block(EthereumHardfork::Homestead, block_number)
563    }
564
565    /// Convenience method to check if [`EthereumHardfork::London`] is active at a given block
566    /// number.
567    fn is_london_active_at_block(&self, block_number: u64) -> bool {
568        self.is_ethereum_fork_active_at_block(EthereumHardfork::London, block_number)
569    }
570
571    /// Convenience method to check if [`EthereumHardfork::Constantinople`] is active at a given
572    /// block number.
573    fn is_constantinople_active_at_block(&self, block_number: u64) -> bool {
574        self.is_ethereum_fork_active_at_block(EthereumHardfork::Constantinople, block_number)
575    }
576
577    /// Convenience method to check if [`EthereumHardfork::Paris`] is active at a given block
578    /// number.
579    fn is_paris_active_at_block(&self, block_number: u64) -> bool {
580        self.is_ethereum_fork_active_at_block(EthereumHardfork::Paris, block_number)
581    }
582}
583
584/// A type allowing to configure activation [`ForkCondition`]s for a given list of
585/// [`EthereumHardfork`]s.
586#[derive(Debug, Clone)]
587pub struct EthereumChainHardforks {
588    forks: Vec<(EthereumHardfork, ForkCondition)>,
589}
590
591impl EthereumChainHardforks {
592    /// Creates a new [`EthereumChainHardforks`] with the given list of forks.
593    pub fn new(forks: impl IntoIterator<Item = (EthereumHardfork, ForkCondition)>) -> Self {
594        let mut forks = forks.into_iter().collect::<Vec<_>>();
595        forks.sort();
596        Self { forks }
597    }
598
599    /// Creates a new [`EthereumChainHardforks`] with Mainnet configuration.
600    pub fn mainnet() -> Self {
601        Self::new(EthereumHardfork::mainnet())
602    }
603
604    /// Creates a new [`EthereumChainHardforks`] with Sepolia configuration.
605    pub fn sepolia() -> Self {
606        Self::new(EthereumHardfork::sepolia())
607    }
608
609    /// Creates a new [`EthereumChainHardforks`] with Holesky configuration.
610    pub fn holesky() -> Self {
611        Self::new(EthereumHardfork::holesky())
612    }
613
614    /// Creates a new [`EthereumChainHardforks`] with Hoodi configuration.
615    pub fn hoodi() -> Self {
616        Self::new(EthereumHardfork::hoodi())
617    }
618}
619
620impl EthereumHardforks for EthereumChainHardforks {
621    fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
622        let Ok(idx) = self.forks.binary_search_by(|(f, _)| f.cmp(&fork)) else {
623            return ForkCondition::Never;
624        };
625
626        self.forks[idx].1
627    }
628}
629
630#[cfg(test)]
631mod tests {
632    use super::*;
633    use alloc::vec::Vec;
634    use core::str::FromStr;
635
636    #[test]
637    fn check_hardfork_from_str() {
638        let hardfork_str = [
639            "frOntier",
640            "homEstead",
641            "dao",
642            "tAngerIne",
643            "spurIousdrAgon",
644            "byzAntium",
645            "constantinople",
646            "petersburg",
647            "istanbul",
648            "muirglacier",
649            "bErlin",
650            "lonDon",
651            "arrowglacier",
652            "grayglacier",
653            "PARIS",
654            "ShAnGhAI",
655            "CaNcUn",
656            "PrAguE",
657        ];
658        let expected_hardforks = [
659            EthereumHardfork::Frontier,
660            EthereumHardfork::Homestead,
661            EthereumHardfork::Dao,
662            EthereumHardfork::Tangerine,
663            EthereumHardfork::SpuriousDragon,
664            EthereumHardfork::Byzantium,
665            EthereumHardfork::Constantinople,
666            EthereumHardfork::Petersburg,
667            EthereumHardfork::Istanbul,
668            EthereumHardfork::MuirGlacier,
669            EthereumHardfork::Berlin,
670            EthereumHardfork::London,
671            EthereumHardfork::ArrowGlacier,
672            EthereumHardfork::GrayGlacier,
673            EthereumHardfork::Paris,
674            EthereumHardfork::Shanghai,
675            EthereumHardfork::Cancun,
676            EthereumHardfork::Prague,
677        ];
678
679        let hardforks: Vec<EthereumHardfork> =
680            hardfork_str.iter().map(|h| EthereumHardfork::from_str(h).unwrap()).collect();
681
682        assert_eq!(hardforks, expected_hardforks);
683    }
684
685    #[test]
686    fn check_nonexistent_hardfork_from_str() {
687        assert!(EthereumHardfork::from_str("not a hardfork").is_err());
688    }
689}