1use crate::{
2 ForkCondition,
3 arbitrum::{mainnet::*, sepolia::*},
4 ethereum::{holesky::*, hoodi::*, mainnet::*, sepolia::*},
5 hardfork,
6};
7use alloc::vec::Vec;
8use alloy_chains::{Chain, NamedChain};
9use alloy_primitives::U256;
10
11hardfork!(
12 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14 #[derive(Default)]
15 EthereumHardfork {
16 Frontier,
18 Homestead,
20 Dao,
22 Tangerine,
24 SpuriousDragon,
26 Byzantium,
28 Constantinople,
30 Petersburg,
32 Istanbul,
34 MuirGlacier,
36 Berlin,
38 London,
40 ArrowGlacier,
42 GrayGlacier,
44 Paris,
46 Shanghai,
48 Cancun,
50 #[default]
52 Prague,
53 Osaka,
55 }
56);
57
58impl EthereumHardfork {
59 pub fn activation_block(&self, chain: Chain) -> Option<u64> {
61 if chain == Chain::mainnet() {
62 return self.mainnet_activation_block();
63 }
64 if chain == Chain::sepolia() {
65 return self.sepolia_activation_block();
66 }
67 if chain == Chain::holesky() {
68 return self.holesky_activation_block();
69 }
70 if chain == Chain::hoodi() {
71 return self.hoodi_activation_block();
72 }
73
74 None
75 }
76
77 pub const fn mainnet_activation_block(&self) -> Option<u64> {
79 match self {
80 Self::Frontier => Some(MAINNET_FRONTIER_BLOCK),
81 Self::Homestead => Some(MAINNET_HOMESTEAD_BLOCK),
82 Self::Dao => Some(MAINNET_DAO_BLOCK),
83 Self::Tangerine => Some(MAINNET_TANGERINE_BLOCK),
84 Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_BLOCK),
85 Self::Byzantium => Some(MAINNET_BYZANTIUM_BLOCK),
86 Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_BLOCK),
87 Self::Petersburg => Some(MAINNET_PETERSBURG_BLOCK),
88 Self::Istanbul => Some(MAINNET_ISTANBUL_BLOCK),
89 Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_BLOCK),
90 Self::Berlin => Some(MAINNET_BERLIN_BLOCK),
91 Self::London => Some(MAINNET_LONDON_BLOCK),
92 Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_BLOCK),
93 Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_BLOCK),
94 Self::Paris => Some(MAINNET_PARIS_BLOCK),
95 Self::Shanghai => Some(MAINNET_SHANGHAI_BLOCK),
96 Self::Cancun => Some(MAINNET_CANCUN_BLOCK),
97 Self::Prague => Some(MAINNET_PRAGUE_BLOCK),
98 _ => None,
99 }
100 }
101
102 pub const fn sepolia_activation_block(&self) -> Option<u64> {
104 match self {
105 Self::Frontier
106 | Self::Homestead
107 | Self::Dao
108 | Self::Tangerine
109 | Self::SpuriousDragon
110 | Self::Byzantium
111 | Self::Constantinople
112 | Self::Petersburg
113 | Self::Istanbul
114 | Self::MuirGlacier
115 | Self::Berlin
116 | Self::London
117 | Self::ArrowGlacier
118 | Self::GrayGlacier => Some(0),
119 Self::Paris => Some(SEPOLIA_PARIS_BLOCK),
120 Self::Shanghai => Some(SEPOLIA_SHANGHAI_BLOCK),
121 Self::Cancun => Some(SEPOLIA_CANCUN_BLOCK),
122 Self::Prague => Some(SEPOLIA_PRAGUE_BLOCK),
123 _ => None,
124 }
125 }
126
127 const fn holesky_activation_block(&self) -> Option<u64> {
129 match self {
130 Self::Frontier
131 | Self::Dao
132 | Self::Tangerine
133 | Self::SpuriousDragon
134 | Self::Byzantium
135 | Self::Constantinople
136 | Self::Petersburg
137 | Self::Istanbul
138 | Self::MuirGlacier
139 | Self::Berlin
140 | Self::London
141 | Self::ArrowGlacier
142 | Self::GrayGlacier
143 | Self::Paris => Some(0),
144 Self::Shanghai => Some(HOLESKY_SHANGHAI_BLOCK),
145 Self::Cancun => Some(HOLESKY_CANCUN_BLOCK),
146 Self::Prague => Some(HOLESKY_PRAGUE_BLOCK),
147 _ => None,
148 }
149 }
150
151 const fn hoodi_activation_block(&self) -> Option<u64> {
153 match self {
154 Self::Frontier
155 | Self::Dao
156 | Self::Tangerine
157 | Self::SpuriousDragon
158 | Self::Byzantium
159 | Self::Constantinople
160 | Self::Petersburg
161 | Self::Istanbul
162 | Self::MuirGlacier
163 | Self::Berlin
164 | Self::London
165 | Self::ArrowGlacier
166 | Self::GrayGlacier
167 | Self::Paris
168 | Self::Shanghai
169 | Self::Cancun => Some(0),
170 Self::Prague => Some(HOODI_PRAGUE_BLOCK),
171 _ => None,
172 }
173 }
174
175 pub const fn arbitrum_sepolia_activation_block(&self) -> Option<u64> {
177 match self {
178 Self::Frontier
179 | Self::Homestead
180 | Self::Dao
181 | Self::Tangerine
182 | Self::SpuriousDragon
183 | Self::Byzantium
184 | Self::Constantinople
185 | Self::Petersburg
186 | Self::Istanbul
187 | Self::MuirGlacier
188 | Self::Berlin
189 | Self::London
190 | Self::ArrowGlacier
191 | Self::GrayGlacier
192 | Self::Paris => Some(0),
193 Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_BLOCK),
194 Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_BLOCK),
195 Self::Prague => Some(ARBITRUM_SEPOLIA_PRAGUE_BLOCK),
196 _ => None,
197 }
198 }
199
200 pub const fn arbitrum_activation_block(&self) -> Option<u64> {
202 match self {
203 Self::Frontier
204 | Self::Homestead
205 | Self::Dao
206 | Self::Tangerine
207 | Self::SpuriousDragon
208 | Self::Byzantium
209 | Self::Constantinople
210 | Self::Petersburg
211 | Self::Istanbul
212 | Self::MuirGlacier
213 | Self::Berlin
214 | Self::London
215 | Self::ArrowGlacier
216 | Self::GrayGlacier
217 | Self::Paris => Some(0),
218 Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_BLOCK),
219 Self::Cancun => Some(ARBITRUM_ONE_CANCUN_BLOCK),
220 Self::Prague => Some(ARBITRUM_ONE_PRAGUE_BLOCK),
221 _ => None,
222 }
223 }
224
225 pub fn activation_timestamp(&self, chain: Chain) -> Option<u64> {
227 if chain == Chain::mainnet() {
228 return self.mainnet_activation_timestamp();
229 }
230 if chain == Chain::sepolia() {
231 return self.sepolia_activation_timestamp();
232 }
233 if chain == Chain::holesky() {
234 return self.holesky_activation_timestamp();
235 }
236 if chain == Chain::hoodi() {
237 return self.hoodi_activation_timestamp();
238 }
239
240 None
241 }
242
243 pub const fn mainnet_activation_timestamp(&self) -> Option<u64> {
245 match self {
246 Self::Frontier => Some(MAINNET_FRONTIER_TIMESTAMP),
247 Self::Homestead => Some(MAINNET_HOMESTEAD_TIMESTAMP),
248 Self::Dao => Some(MAINNET_DAO_TIMESTAMP),
249 Self::Tangerine => Some(MAINNET_TANGERINE_TIMESTAMP),
250 Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_TIMESTAMP),
251 Self::Byzantium => Some(MAINNET_BYZANTIUM_TIMESTAMP),
252 Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_TIMESTAMP),
253 Self::Petersburg => Some(MAINNET_PETERSBURG_TIMESTAMP),
254 Self::Istanbul => Some(MAINNET_ISTANBUL_TIMESTAMP),
255 Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_TIMESTAMP),
256 Self::Berlin => Some(MAINNET_BERLIN_TIMESTAMP),
257 Self::London => Some(MAINNET_LONDON_TIMESTAMP),
258 Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_TIMESTAMP),
259 Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_TIMESTAMP),
260 Self::Paris => Some(MAINNET_PARIS_TIMESTAMP),
261 Self::Shanghai => Some(MAINNET_SHANGHAI_TIMESTAMP),
262 Self::Cancun => Some(MAINNET_CANCUN_TIMESTAMP),
263 Self::Prague => Some(MAINNET_PRAGUE_TIMESTAMP),
264 _ => None,
266 }
267 }
268
269 pub const fn sepolia_activation_timestamp(&self) -> Option<u64> {
271 match self {
272 Self::Frontier
273 | Self::Homestead
274 | Self::Dao
275 | Self::Tangerine
276 | Self::SpuriousDragon
277 | Self::Byzantium
278 | Self::Constantinople
279 | Self::Petersburg
280 | Self::Istanbul
281 | Self::MuirGlacier
282 | Self::Berlin
283 | Self::London
284 | Self::ArrowGlacier
285 | Self::GrayGlacier
286 | Self::Paris => Some(SEPOLIA_PARIS_TIMESTAMP),
287 Self::Shanghai => Some(SEPOLIA_SHANGHAI_TIMESTAMP),
288 Self::Cancun => Some(SEPOLIA_CANCUN_TIMESTAMP),
289 _ => None,
290 }
291 }
292
293 pub const fn holesky_activation_timestamp(&self) -> Option<u64> {
295 match self {
296 Self::Frontier
297 | Self::Homestead
298 | Self::Dao
299 | Self::Tangerine
300 | Self::SpuriousDragon
301 | Self::Byzantium
302 | Self::Constantinople
303 | Self::Petersburg
304 | Self::Istanbul
305 | Self::MuirGlacier
306 | Self::Berlin
307 | Self::London
308 | Self::ArrowGlacier
309 | Self::GrayGlacier
310 | Self::Paris => Some(HOLESKY_PARIS_TIMESTAMP),
311 Self::Shanghai => Some(HOLESKY_SHANGHAI_TIMESTAMP),
312 Self::Cancun => Some(HOLESKY_CANCUN_TIMESTAMP),
313 _ => None,
314 }
315 }
316
317 pub const fn hoodi_activation_timestamp(&self) -> Option<u64> {
319 match self {
320 Self::Prague => Some(HOODI_PRAGUE_TIMESTAMP),
321 Self::Frontier
322 | Self::Homestead
323 | Self::Dao
324 | Self::Tangerine
325 | Self::SpuriousDragon
326 | Self::Byzantium
327 | Self::Constantinople
328 | Self::Petersburg
329 | Self::Istanbul
330 | Self::MuirGlacier
331 | Self::Berlin
332 | Self::London
333 | Self::ArrowGlacier
334 | Self::GrayGlacier
335 | Self::Paris
336 | Self::Shanghai
337 | Self::Cancun => Some(0),
338 _ => None,
339 }
340 }
341
342 pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option<u64> {
345 match self {
346 Self::Frontier
347 | Self::Homestead
348 | Self::Dao
349 | Self::Tangerine
350 | Self::SpuriousDragon
351 | Self::Byzantium
352 | Self::Constantinople
353 | Self::Petersburg
354 | Self::Istanbul
355 | Self::MuirGlacier
356 | Self::Berlin
357 | Self::London
358 | Self::ArrowGlacier
359 | Self::GrayGlacier
360 | Self::Paris => Some(ARBITRUM_SEPOLIA_PARIS_TIMESTAMP),
361 Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP),
362 Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP),
363 Self::Prague => Some(ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP),
364 _ => None,
365 }
366 }
367
368 pub const fn arbitrum_activation_timestamp(&self) -> Option<u64> {
370 match self {
371 Self::Frontier
372 | Self::Homestead
373 | Self::Dao
374 | Self::Tangerine
375 | Self::SpuriousDragon
376 | Self::Byzantium
377 | Self::Constantinople
378 | Self::Petersburg
379 | Self::Istanbul
380 | Self::MuirGlacier
381 | Self::Berlin
382 | Self::London
383 | Self::ArrowGlacier
384 | Self::GrayGlacier
385 | Self::Paris => Some(ARBITRUM_ONE_PARIS_TIMESTAMP),
386 Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_TIMESTAMP),
387 Self::Cancun => Some(ARBITRUM_ONE_CANCUN_TIMESTAMP),
388 Self::Prague => Some(ARBITRUM_ONE_PRAGUE_TIMESTAMP),
389 _ => None,
390 }
391 }
392
393 pub const fn mainnet() -> [(Self, ForkCondition); 18] {
395 [
396 (Self::Frontier, ForkCondition::Block(MAINNET_FRONTIER_BLOCK)),
397 (Self::Homestead, ForkCondition::Block(MAINNET_HOMESTEAD_BLOCK)),
398 (Self::Dao, ForkCondition::Block(MAINNET_DAO_BLOCK)),
399 (Self::Tangerine, ForkCondition::Block(MAINNET_TANGERINE_BLOCK)),
400 (Self::SpuriousDragon, ForkCondition::Block(MAINNET_SPURIOUS_DRAGON_BLOCK)),
401 (Self::Byzantium, ForkCondition::Block(MAINNET_BYZANTIUM_BLOCK)),
402 (Self::Constantinople, ForkCondition::Block(MAINNET_CONSTANTINOPLE_BLOCK)),
403 (Self::Petersburg, ForkCondition::Block(MAINNET_PETERSBURG_BLOCK)),
404 (Self::Istanbul, ForkCondition::Block(MAINNET_ISTANBUL_BLOCK)),
405 (Self::MuirGlacier, ForkCondition::Block(MAINNET_MUIR_GLACIER_BLOCK)),
406 (Self::Berlin, ForkCondition::Block(MAINNET_BERLIN_BLOCK)),
407 (Self::London, ForkCondition::Block(MAINNET_LONDON_BLOCK)),
408 (Self::ArrowGlacier, ForkCondition::Block(MAINNET_ARROW_GLACIER_BLOCK)),
409 (Self::GrayGlacier, ForkCondition::Block(MAINNET_GRAY_GLACIER_BLOCK)),
410 (
411 Self::Paris,
412 ForkCondition::TTD {
413 activation_block_number: MAINNET_PARIS_BLOCK,
414 fork_block: None,
415 total_difficulty: MAINNET_PARIS_TTD,
416 },
417 ),
418 (Self::Shanghai, ForkCondition::Timestamp(MAINNET_SHANGHAI_TIMESTAMP)),
419 (Self::Cancun, ForkCondition::Timestamp(MAINNET_CANCUN_TIMESTAMP)),
420 (Self::Prague, ForkCondition::Timestamp(MAINNET_PRAGUE_TIMESTAMP)),
421 ]
422 }
423
424 pub const fn sepolia() -> [(Self, ForkCondition); 16] {
426 [
427 (Self::Frontier, ForkCondition::Block(0)),
428 (Self::Homestead, ForkCondition::Block(0)),
429 (Self::Dao, ForkCondition::Block(0)),
430 (Self::Tangerine, ForkCondition::Block(0)),
431 (Self::SpuriousDragon, ForkCondition::Block(0)),
432 (Self::Byzantium, ForkCondition::Block(0)),
433 (Self::Constantinople, ForkCondition::Block(0)),
434 (Self::Petersburg, ForkCondition::Block(0)),
435 (Self::Istanbul, ForkCondition::Block(0)),
436 (Self::MuirGlacier, ForkCondition::Block(0)),
437 (Self::Berlin, ForkCondition::Block(0)),
438 (Self::London, ForkCondition::Block(0)),
439 (
440 Self::Paris,
441 ForkCondition::TTD {
442 activation_block_number: SEPOLIA_PARIS_BLOCK,
443 fork_block: Some(SEPOLIA_PARIS_FORK_BLOCK),
444 total_difficulty: SEPOLIA_PARIS_TTD,
445 },
446 ),
447 (Self::Shanghai, ForkCondition::Timestamp(SEPOLIA_SHANGHAI_TIMESTAMP)),
448 (Self::Cancun, ForkCondition::Timestamp(SEPOLIA_CANCUN_TIMESTAMP)),
449 (Self::Prague, ForkCondition::Timestamp(SEPOLIA_PRAGUE_TIMESTAMP)),
450 ]
451 }
452
453 pub const fn holesky() -> [(Self, ForkCondition); 16] {
455 [
456 (Self::Frontier, ForkCondition::Block(0)),
457 (Self::Homestead, ForkCondition::Block(0)),
458 (Self::Dao, ForkCondition::Block(0)),
459 (Self::Tangerine, ForkCondition::Block(0)),
460 (Self::SpuriousDragon, ForkCondition::Block(0)),
461 (Self::Byzantium, ForkCondition::Block(0)),
462 (Self::Constantinople, ForkCondition::Block(0)),
463 (Self::Petersburg, ForkCondition::Block(0)),
464 (Self::Istanbul, ForkCondition::Block(0)),
465 (Self::MuirGlacier, ForkCondition::Block(0)),
466 (Self::Berlin, ForkCondition::Block(0)),
467 (Self::London, ForkCondition::Block(0)),
468 (
469 Self::Paris,
470 ForkCondition::TTD {
471 activation_block_number: 0,
472 fork_block: Some(0),
473 total_difficulty: U256::ZERO,
474 },
475 ),
476 (Self::Shanghai, ForkCondition::Timestamp(HOLESKY_SHANGHAI_TIMESTAMP)),
477 (Self::Cancun, ForkCondition::Timestamp(HOLESKY_CANCUN_TIMESTAMP)),
478 (Self::Prague, ForkCondition::Timestamp(HOLESKY_PRAGUE_TIMESTAMP)),
479 ]
480 }
481
482 pub const fn hoodi() -> [(Self, ForkCondition); 16] {
484 [
485 (Self::Frontier, ForkCondition::Block(0)),
486 (Self::Homestead, ForkCondition::Block(0)),
487 (Self::Dao, ForkCondition::Block(0)),
488 (Self::Tangerine, ForkCondition::Block(0)),
489 (Self::SpuriousDragon, ForkCondition::Block(0)),
490 (Self::Byzantium, ForkCondition::Block(0)),
491 (Self::Constantinople, ForkCondition::Block(0)),
492 (Self::Petersburg, ForkCondition::Block(0)),
493 (Self::Istanbul, ForkCondition::Block(0)),
494 (Self::MuirGlacier, ForkCondition::Block(0)),
495 (Self::Berlin, ForkCondition::Block(0)),
496 (Self::London, ForkCondition::Block(0)),
497 (
498 Self::Paris,
499 ForkCondition::TTD {
500 activation_block_number: 0,
501 fork_block: Some(0),
502 total_difficulty: U256::ZERO,
503 },
504 ),
505 (Self::Shanghai, ForkCondition::Timestamp(0)),
506 (Self::Cancun, ForkCondition::Timestamp(0)),
507 (Self::Prague, ForkCondition::Timestamp(HOODI_PRAGUE_TIMESTAMP)),
508 ]
509 }
510
511 pub const fn from_mainnet_block_number(num: u64) -> Self {
513 match num {
514 _i if num < MAINNET_HOMESTEAD_BLOCK => Self::Frontier,
515 _i if num < MAINNET_DAO_BLOCK => Self::Homestead,
516 _i if num < MAINNET_TANGERINE_BLOCK => Self::Dao,
517 _i if num < MAINNET_SPURIOUS_DRAGON_BLOCK => Self::Tangerine,
518 _i if num < MAINNET_BYZANTIUM_BLOCK => Self::SpuriousDragon,
519 _i if num < MAINNET_CONSTANTINOPLE_BLOCK => Self::Byzantium,
520 _i if num < MAINNET_ISTANBUL_BLOCK => Self::Constantinople,
521 _i if num < MAINNET_MUIR_GLACIER_BLOCK => Self::Istanbul,
522 _i if num < MAINNET_BERLIN_BLOCK => Self::MuirGlacier,
523 _i if num < MAINNET_LONDON_BLOCK => Self::Berlin,
524 _i if num < MAINNET_ARROW_GLACIER_BLOCK => Self::London,
525 _i if num < MAINNET_PARIS_BLOCK => Self::ArrowGlacier,
526 _i if num < MAINNET_SHANGHAI_BLOCK => Self::Paris,
527 _i if num < MAINNET_CANCUN_BLOCK => Self::Shanghai,
528 _i if num < MAINNET_PRAGUE_BLOCK => Self::Cancun,
529 _ => Self::Prague,
530 }
531 }
532
533 pub fn from_chain_and_timestamp(chain: Chain, timestamp: u64) -> Option<Self> {
536 let named = chain.named()?;
537
538 match named {
539 NamedChain::Mainnet => Some(match timestamp {
540 _i if timestamp < MAINNET_HOMESTEAD_TIMESTAMP => Self::Frontier,
541 _i if timestamp < MAINNET_DAO_TIMESTAMP => Self::Homestead,
542 _i if timestamp < MAINNET_TANGERINE_TIMESTAMP => Self::Dao,
543 _i if timestamp < MAINNET_SPURIOUS_DRAGON_TIMESTAMP => Self::Tangerine,
544 _i if timestamp < MAINNET_BYZANTIUM_TIMESTAMP => Self::SpuriousDragon,
545 _i if timestamp < MAINNET_PETERSBURG_TIMESTAMP => Self::Byzantium,
546 _i if timestamp < MAINNET_ISTANBUL_TIMESTAMP => Self::Petersburg,
547 _i if timestamp < MAINNET_MUIR_GLACIER_TIMESTAMP => Self::Istanbul,
548 _i if timestamp < MAINNET_BERLIN_TIMESTAMP => Self::MuirGlacier,
549 _i if timestamp < MAINNET_LONDON_TIMESTAMP => Self::Berlin,
550 _i if timestamp < MAINNET_ARROW_GLACIER_TIMESTAMP => Self::London,
551 _i if timestamp < MAINNET_GRAY_GLACIER_TIMESTAMP => Self::ArrowGlacier,
552 _i if timestamp < MAINNET_PARIS_TIMESTAMP => Self::GrayGlacier,
553 _i if timestamp < MAINNET_SHANGHAI_TIMESTAMP => Self::Paris,
554 _i if timestamp < MAINNET_CANCUN_TIMESTAMP => Self::Shanghai,
555 _i if timestamp < MAINNET_PRAGUE_TIMESTAMP => Self::Cancun,
556 _ => Self::Prague,
557 }),
558 NamedChain::Sepolia => Some(match timestamp {
559 _i if timestamp < SEPOLIA_PARIS_TIMESTAMP => Self::London,
560 _i if timestamp < SEPOLIA_SHANGHAI_TIMESTAMP => Self::Paris,
561 _i if timestamp < SEPOLIA_CANCUN_TIMESTAMP => Self::Shanghai,
562 _i if timestamp < SEPOLIA_PRAGUE_TIMESTAMP => Self::Cancun,
563 _ => Self::Prague,
564 }),
565 NamedChain::Holesky => Some(match timestamp {
566 _i if timestamp < HOLESKY_SHANGHAI_TIMESTAMP => Self::Paris,
567 _i if timestamp < HOLESKY_CANCUN_TIMESTAMP => Self::Shanghai,
568 _i if timestamp < HOLESKY_PRAGUE_TIMESTAMP => Self::Cancun,
569 _ => Self::Prague,
570 }),
571 NamedChain::Hoodi => Some(match timestamp {
572 _i if timestamp < HOODI_PRAGUE_TIMESTAMP => Self::Cancun,
573 _ => Self::Prague,
574 }),
575 NamedChain::Arbitrum => Some(match timestamp {
576 _i if timestamp < ARBITRUM_ONE_SHANGHAI_TIMESTAMP => Self::Paris,
577 _i if timestamp < ARBITRUM_ONE_CANCUN_TIMESTAMP => Self::Shanghai,
578 _i if timestamp < ARBITRUM_ONE_PRAGUE_TIMESTAMP => Self::Cancun,
579 _ => Self::Prague,
580 }),
581 NamedChain::ArbitrumSepolia => Some(match timestamp {
582 _i if timestamp < ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP => Self::Paris,
583 _i if timestamp < ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP => Self::Shanghai,
584 _i if timestamp < ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP => Self::Cancun,
585 _ => Self::Prague,
586 }),
587 _ => None,
588 }
589 }
590}
591
592#[auto_impl::auto_impl(&, Arc)]
594pub trait EthereumHardforks {
595 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition;
598
599 fn is_ethereum_fork_active_at_timestamp(&self, fork: EthereumHardfork, timestamp: u64) -> bool {
601 self.ethereum_fork_activation(fork).active_at_timestamp(timestamp)
602 }
603
604 fn is_ethereum_fork_active_at_block(&self, fork: EthereumHardfork, block_number: u64) -> bool {
606 self.ethereum_fork_activation(fork).active_at_block(block_number)
607 }
608
609 fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
612 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
613 }
614
615 fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
617 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
618 }
619
620 fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
622 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
623 }
624
625 fn is_osaka_active_at_timestamp(&self, timestamp: u64) -> bool {
627 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp)
628 }
629
630 fn is_byzantium_active_at_block(&self, block_number: u64) -> bool {
633 self.is_ethereum_fork_active_at_block(EthereumHardfork::Byzantium, block_number)
634 }
635
636 fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool {
639 self.is_ethereum_fork_active_at_block(EthereumHardfork::SpuriousDragon, block_number)
640 }
641
642 fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
645 self.is_ethereum_fork_active_at_block(EthereumHardfork::Homestead, block_number)
646 }
647
648 fn is_london_active_at_block(&self, block_number: u64) -> bool {
651 self.is_ethereum_fork_active_at_block(EthereumHardfork::London, block_number)
652 }
653
654 fn is_constantinople_active_at_block(&self, block_number: u64) -> bool {
657 self.is_ethereum_fork_active_at_block(EthereumHardfork::Constantinople, block_number)
658 }
659
660 fn is_paris_active_at_block(&self, block_number: u64) -> bool {
663 self.is_ethereum_fork_active_at_block(EthereumHardfork::Paris, block_number)
664 }
665}
666
667#[derive(Debug, Clone)]
670pub struct EthereumChainHardforks {
671 forks: Vec<(EthereumHardfork, ForkCondition)>,
672}
673
674impl EthereumChainHardforks {
675 pub fn new(forks: impl IntoIterator<Item = (EthereumHardfork, ForkCondition)>) -> Self {
677 let mut forks = forks.into_iter().collect::<Vec<_>>();
678 forks.sort();
679 Self { forks }
680 }
681
682 pub fn mainnet() -> Self {
684 Self::new(EthereumHardfork::mainnet())
685 }
686
687 pub fn sepolia() -> Self {
689 Self::new(EthereumHardfork::sepolia())
690 }
691
692 pub fn holesky() -> Self {
694 Self::new(EthereumHardfork::holesky())
695 }
696
697 pub fn hoodi() -> Self {
699 Self::new(EthereumHardfork::hoodi())
700 }
701}
702
703impl EthereumHardforks for EthereumChainHardforks {
704 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
705 let Ok(idx) = self.forks.binary_search_by(|(f, _)| f.cmp(&fork)) else {
706 return ForkCondition::Never;
707 };
708
709 self.forks[idx].1
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use super::*;
716 use alloc::vec::Vec;
717 use core::str::FromStr;
718
719 #[test]
720 fn check_hardfork_from_str() {
721 let hardfork_str = [
722 "frOntier",
723 "homEstead",
724 "dao",
725 "tAngerIne",
726 "spurIousdrAgon",
727 "byzAntium",
728 "constantinople",
729 "petersburg",
730 "istanbul",
731 "muirglacier",
732 "bErlin",
733 "lonDon",
734 "arrowglacier",
735 "grayglacier",
736 "PARIS",
737 "ShAnGhAI",
738 "CaNcUn",
739 "PrAguE",
740 ];
741 let expected_hardforks = [
742 EthereumHardfork::Frontier,
743 EthereumHardfork::Homestead,
744 EthereumHardfork::Dao,
745 EthereumHardfork::Tangerine,
746 EthereumHardfork::SpuriousDragon,
747 EthereumHardfork::Byzantium,
748 EthereumHardfork::Constantinople,
749 EthereumHardfork::Petersburg,
750 EthereumHardfork::Istanbul,
751 EthereumHardfork::MuirGlacier,
752 EthereumHardfork::Berlin,
753 EthereumHardfork::London,
754 EthereumHardfork::ArrowGlacier,
755 EthereumHardfork::GrayGlacier,
756 EthereumHardfork::Paris,
757 EthereumHardfork::Shanghai,
758 EthereumHardfork::Cancun,
759 EthereumHardfork::Prague,
760 ];
761
762 let hardforks: Vec<EthereumHardfork> =
763 hardfork_str.iter().map(|h| EthereumHardfork::from_str(h).unwrap()).collect();
764
765 assert_eq!(hardforks, expected_hardforks);
766 }
767
768 #[test]
769 fn check_nonexistent_hardfork_from_str() {
770 assert!(EthereumHardfork::from_str("not a hardfork").is_err());
771 }
772
773 #[test]
774 fn test_reverse_lookup_by_chain_id() {
775 let test_cases = [
777 (Chain::mainnet(), MAINNET_FRONTIER_TIMESTAMP - 1, EthereumHardfork::Frontier),
781 (Chain::mainnet(), MAINNET_FRONTIER_TIMESTAMP, EthereumHardfork::Frontier),
782 (Chain::mainnet(), MAINNET_HOMESTEAD_TIMESTAMP, EthereumHardfork::Homestead),
783 (Chain::mainnet(), MAINNET_DAO_TIMESTAMP, EthereumHardfork::Dao),
784 (Chain::mainnet(), MAINNET_TANGERINE_TIMESTAMP, EthereumHardfork::Tangerine),
785 (Chain::mainnet(), MAINNET_SPURIOUS_DRAGON_TIMESTAMP, EthereumHardfork::SpuriousDragon),
786 (Chain::mainnet(), MAINNET_BYZANTIUM_TIMESTAMP, EthereumHardfork::Byzantium),
787 (Chain::mainnet(), MAINNET_PETERSBURG_TIMESTAMP, EthereumHardfork::Petersburg),
788 (Chain::mainnet(), MAINNET_ISTANBUL_TIMESTAMP, EthereumHardfork::Istanbul),
789 (Chain::mainnet(), MAINNET_MUIR_GLACIER_TIMESTAMP, EthereumHardfork::MuirGlacier),
790 (Chain::mainnet(), MAINNET_BERLIN_TIMESTAMP, EthereumHardfork::Berlin),
791 (Chain::mainnet(), MAINNET_LONDON_TIMESTAMP, EthereumHardfork::London),
792 (Chain::mainnet(), MAINNET_ARROW_GLACIER_TIMESTAMP, EthereumHardfork::ArrowGlacier),
793 (Chain::mainnet(), MAINNET_GRAY_GLACIER_TIMESTAMP, EthereumHardfork::GrayGlacier),
794 (Chain::mainnet(), MAINNET_PARIS_TIMESTAMP, EthereumHardfork::Paris),
795 (Chain::mainnet(), MAINNET_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
796 (Chain::mainnet(), MAINNET_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
797 (Chain::sepolia(), SEPOLIA_PARIS_TIMESTAMP - 1, EthereumHardfork::London),
800 (Chain::sepolia(), SEPOLIA_PARIS_TIMESTAMP, EthereumHardfork::Paris),
801 (Chain::sepolia(), SEPOLIA_SHANGHAI_TIMESTAMP - 1, EthereumHardfork::Paris),
802 (Chain::sepolia(), SEPOLIA_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
803 (Chain::sepolia(), SEPOLIA_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
804 (Chain::sepolia(), SEPOLIA_PRAGUE_TIMESTAMP - 1, EthereumHardfork::Cancun),
805 (Chain::sepolia(), SEPOLIA_PRAGUE_TIMESTAMP + 1, EthereumHardfork::Prague),
806 (Chain::holesky(), HOLESKY_PARIS_TIMESTAMP - 1, EthereumHardfork::Paris),
809 (Chain::holesky(), HOLESKY_PARIS_TIMESTAMP, EthereumHardfork::Paris),
810 (Chain::holesky(), HOLESKY_SHANGHAI_TIMESTAMP - 1, EthereumHardfork::Paris),
811 (Chain::holesky(), HOLESKY_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
812 (Chain::holesky(), HOLESKY_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
813 (Chain::holesky(), HOLESKY_PRAGUE_TIMESTAMP - 1, EthereumHardfork::Cancun),
814 (Chain::holesky(), HOLESKY_PRAGUE_TIMESTAMP + 1, EthereumHardfork::Prague),
815 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_PARIS_TIMESTAMP - 1, EthereumHardfork::Paris),
818 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_PARIS_TIMESTAMP, EthereumHardfork::Paris),
819 (
820 Chain::arbitrum_mainnet(),
821 ARBITRUM_ONE_SHANGHAI_TIMESTAMP - 1,
822 EthereumHardfork::Paris,
823 ),
824 (
825 Chain::arbitrum_mainnet(),
826 ARBITRUM_ONE_SHANGHAI_TIMESTAMP,
827 EthereumHardfork::Shanghai,
828 ),
829 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
830 (
831 Chain::arbitrum_mainnet(),
832 ARBITRUM_ONE_PRAGUE_TIMESTAMP - 1,
833 EthereumHardfork::Cancun,
834 ),
835 (
836 Chain::arbitrum_mainnet(),
837 ARBITRUM_ONE_PRAGUE_TIMESTAMP + 1,
838 EthereumHardfork::Prague,
839 ),
840 (
843 Chain::arbitrum_sepolia(),
844 ARBITRUM_SEPOLIA_PARIS_TIMESTAMP - 1,
845 EthereumHardfork::Paris,
846 ),
847 (Chain::arbitrum_sepolia(), ARBITRUM_SEPOLIA_PARIS_TIMESTAMP, EthereumHardfork::Paris),
848 (
849 Chain::arbitrum_sepolia(),
850 ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP - 1,
851 EthereumHardfork::Paris,
852 ),
853 (
854 Chain::arbitrum_sepolia(),
855 ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP,
856 EthereumHardfork::Shanghai,
857 ),
858 (
859 Chain::arbitrum_sepolia(),
860 ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP,
861 EthereumHardfork::Cancun,
862 ),
863 (
864 Chain::arbitrum_sepolia(),
865 ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP - 1,
866 EthereumHardfork::Cancun,
867 ),
868 (
869 Chain::arbitrum_sepolia(),
870 ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP + 1,
871 EthereumHardfork::Prague,
872 ),
873 ];
874
875 for (chain_id, timestamp, expected) in test_cases {
876 assert_eq!(
877 EthereumHardfork::from_chain_and_timestamp(chain_id, timestamp),
878 Some(expected),
879 "chain {chain_id} at timestamp {timestamp}"
880 );
881 }
882
883 assert_eq!(
885 EthereumHardfork::from_chain_and_timestamp(Chain::from_id(99999), 1000000),
886 None
887 );
888 }
889
890 #[test]
891 fn test_timestamp_functions_consistency() {
892 let test_cases = [
893 (MAINNET_LONDON_TIMESTAMP, EthereumHardfork::London),
894 (MAINNET_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
895 (MAINNET_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
896 ];
897
898 for (timestamp, fork) in test_cases {
899 assert_eq!(
900 EthereumHardfork::from_chain_and_timestamp(Chain::mainnet(), timestamp),
901 Some(fork)
902 );
903 assert_eq!(fork.activation_timestamp(Chain::mainnet()), Some(timestamp));
904 }
905 }
906}