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 Bpo1,
58 Bpo2,
60 Bpo3,
62 Bpo4,
64 Bpo5,
66 Amsterdam,
68 }
69);
70
71impl EthereumHardfork {
72 pub fn activation_block(&self, chain: Chain) -> Option<u64> {
74 if chain == Chain::mainnet() {
75 return self.mainnet_activation_block();
76 }
77 if chain == Chain::sepolia() {
78 return self.sepolia_activation_block();
79 }
80 if chain == Chain::holesky() {
81 return self.holesky_activation_block();
82 }
83 if chain == Chain::hoodi() {
84 return self.hoodi_activation_block();
85 }
86
87 None
88 }
89
90 pub const fn mainnet_activation_block(&self) -> Option<u64> {
92 match self {
93 Self::Frontier => Some(MAINNET_FRONTIER_BLOCK),
94 Self::Homestead => Some(MAINNET_HOMESTEAD_BLOCK),
95 Self::Dao => Some(MAINNET_DAO_BLOCK),
96 Self::Tangerine => Some(MAINNET_TANGERINE_BLOCK),
97 Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_BLOCK),
98 Self::Byzantium => Some(MAINNET_BYZANTIUM_BLOCK),
99 Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_BLOCK),
100 Self::Petersburg => Some(MAINNET_PETERSBURG_BLOCK),
101 Self::Istanbul => Some(MAINNET_ISTANBUL_BLOCK),
102 Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_BLOCK),
103 Self::Berlin => Some(MAINNET_BERLIN_BLOCK),
104 Self::London => Some(MAINNET_LONDON_BLOCK),
105 Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_BLOCK),
106 Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_BLOCK),
107 Self::Paris => Some(MAINNET_PARIS_BLOCK),
108 Self::Shanghai => Some(MAINNET_SHANGHAI_BLOCK),
109 Self::Cancun => Some(MAINNET_CANCUN_BLOCK),
110 Self::Prague => Some(MAINNET_PRAGUE_BLOCK),
111 _ => None,
112 }
113 }
114
115 pub const fn sepolia_activation_block(&self) -> Option<u64> {
117 match self {
118 Self::Frontier
119 | Self::Homestead
120 | Self::Dao
121 | Self::Tangerine
122 | Self::SpuriousDragon
123 | Self::Byzantium
124 | Self::Constantinople
125 | Self::Petersburg
126 | Self::Istanbul
127 | Self::MuirGlacier
128 | Self::Berlin
129 | Self::London
130 | Self::ArrowGlacier
131 | Self::GrayGlacier => Some(0),
132 Self::Paris => Some(SEPOLIA_PARIS_BLOCK),
133 Self::Shanghai => Some(SEPOLIA_SHANGHAI_BLOCK),
134 Self::Cancun => Some(SEPOLIA_CANCUN_BLOCK),
135 Self::Prague => Some(SEPOLIA_PRAGUE_BLOCK),
136 _ => None,
137 }
138 }
139
140 const fn holesky_activation_block(&self) -> Option<u64> {
142 match self {
143 Self::Frontier
144 | Self::Homestead
145 | Self::Dao
146 | Self::Tangerine
147 | Self::SpuriousDragon
148 | Self::Byzantium
149 | Self::Constantinople
150 | Self::Petersburg
151 | Self::Istanbul
152 | Self::MuirGlacier
153 | Self::Berlin
154 | Self::London
155 | Self::ArrowGlacier
156 | Self::GrayGlacier
157 | Self::Paris => Some(0),
158 Self::Shanghai => Some(HOLESKY_SHANGHAI_BLOCK),
159 Self::Cancun => Some(HOLESKY_CANCUN_BLOCK),
160 Self::Prague => Some(HOLESKY_PRAGUE_BLOCK),
161 _ => None,
162 }
163 }
164
165 const fn hoodi_activation_block(&self) -> Option<u64> {
167 match self {
168 Self::Frontier
169 | Self::Homestead
170 | Self::Dao
171 | Self::Tangerine
172 | Self::SpuriousDragon
173 | Self::Byzantium
174 | Self::Constantinople
175 | Self::Petersburg
176 | Self::Istanbul
177 | Self::MuirGlacier
178 | Self::Berlin
179 | Self::London
180 | Self::ArrowGlacier
181 | Self::GrayGlacier
182 | Self::Paris
183 | Self::Shanghai
184 | Self::Cancun => Some(0),
185 Self::Prague => Some(HOODI_PRAGUE_BLOCK),
186 _ => None,
187 }
188 }
189
190 pub const fn arbitrum_sepolia_activation_block(&self) -> Option<u64> {
192 match self {
193 Self::Frontier
194 | Self::Homestead
195 | Self::Dao
196 | Self::Tangerine
197 | Self::SpuriousDragon
198 | Self::Byzantium
199 | Self::Constantinople
200 | Self::Petersburg
201 | Self::Istanbul
202 | Self::MuirGlacier
203 | Self::Berlin
204 | Self::London
205 | Self::ArrowGlacier
206 | Self::GrayGlacier
207 | Self::Paris => Some(0),
208 Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_BLOCK),
209 Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_BLOCK),
210 Self::Prague => Some(ARBITRUM_SEPOLIA_PRAGUE_BLOCK),
211 _ => None,
212 }
213 }
214
215 pub const fn arbitrum_activation_block(&self) -> Option<u64> {
217 match self {
218 Self::Frontier
219 | Self::Homestead
220 | Self::Dao
221 | Self::Tangerine
222 | Self::SpuriousDragon
223 | Self::Byzantium
224 | Self::Constantinople
225 | Self::Petersburg
226 | Self::Istanbul
227 | Self::MuirGlacier
228 | Self::Berlin
229 | Self::London
230 | Self::ArrowGlacier
231 | Self::GrayGlacier
232 | Self::Paris => Some(0),
233 Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_BLOCK),
234 Self::Cancun => Some(ARBITRUM_ONE_CANCUN_BLOCK),
235 Self::Prague => Some(ARBITRUM_ONE_PRAGUE_BLOCK),
236 _ => None,
237 }
238 }
239
240 pub fn activation_timestamp(&self, chain: Chain) -> Option<u64> {
242 if chain == Chain::mainnet() {
243 return self.mainnet_activation_timestamp();
244 }
245 if chain == Chain::sepolia() {
246 return self.sepolia_activation_timestamp();
247 }
248 if chain == Chain::holesky() {
249 return self.holesky_activation_timestamp();
250 }
251 if chain == Chain::hoodi() {
252 return self.hoodi_activation_timestamp();
253 }
254
255 None
256 }
257
258 pub const fn mainnet_activation_timestamp(&self) -> Option<u64> {
260 match self {
261 Self::Frontier => Some(MAINNET_FRONTIER_TIMESTAMP),
262 Self::Homestead => Some(MAINNET_HOMESTEAD_TIMESTAMP),
263 Self::Dao => Some(MAINNET_DAO_TIMESTAMP),
264 Self::Tangerine => Some(MAINNET_TANGERINE_TIMESTAMP),
265 Self::SpuriousDragon => Some(MAINNET_SPURIOUS_DRAGON_TIMESTAMP),
266 Self::Byzantium => Some(MAINNET_BYZANTIUM_TIMESTAMP),
267 Self::Constantinople => Some(MAINNET_CONSTANTINOPLE_TIMESTAMP),
268 Self::Petersburg => Some(MAINNET_PETERSBURG_TIMESTAMP),
269 Self::Istanbul => Some(MAINNET_ISTANBUL_TIMESTAMP),
270 Self::MuirGlacier => Some(MAINNET_MUIR_GLACIER_TIMESTAMP),
271 Self::Berlin => Some(MAINNET_BERLIN_TIMESTAMP),
272 Self::London => Some(MAINNET_LONDON_TIMESTAMP),
273 Self::ArrowGlacier => Some(MAINNET_ARROW_GLACIER_TIMESTAMP),
274 Self::GrayGlacier => Some(MAINNET_GRAY_GLACIER_TIMESTAMP),
275 Self::Paris => Some(MAINNET_PARIS_TIMESTAMP),
276 Self::Shanghai => Some(MAINNET_SHANGHAI_TIMESTAMP),
277 Self::Cancun => Some(MAINNET_CANCUN_TIMESTAMP),
278 Self::Prague => Some(MAINNET_PRAGUE_TIMESTAMP),
279 Self::Osaka => Some(MAINNET_OSAKA_TIMESTAMP),
280 Self::Bpo1 => Some(MAINNET_BPO1_TIMESTAMP),
281 Self::Bpo2 => Some(MAINNET_BPO2_TIMESTAMP),
282 _ => None,
283 }
284 }
285
286 pub const fn sepolia_activation_timestamp(&self) -> Option<u64> {
288 match self {
289 Self::Frontier
290 | Self::Homestead
291 | Self::Dao
292 | Self::Tangerine
293 | Self::SpuriousDragon
294 | Self::Byzantium
295 | Self::Constantinople
296 | Self::Petersburg
297 | Self::Istanbul
298 | Self::MuirGlacier
299 | Self::Berlin
300 | Self::London
301 | Self::ArrowGlacier
302 | Self::GrayGlacier
303 | Self::Paris => Some(SEPOLIA_PARIS_TIMESTAMP),
304 Self::Shanghai => Some(SEPOLIA_SHANGHAI_TIMESTAMP),
305 Self::Cancun => Some(SEPOLIA_CANCUN_TIMESTAMP),
306 Self::Prague => Some(SEPOLIA_PRAGUE_TIMESTAMP),
307 Self::Osaka => Some(SEPOLIA_OSAKA_TIMESTAMP),
308 Self::Bpo1 => Some(SEPOLIA_BPO1_TIMESTAMP),
309 Self::Bpo2 => Some(SEPOLIA_BPO2_TIMESTAMP),
310 _ => None,
311 }
312 }
313
314 pub const fn holesky_activation_timestamp(&self) -> Option<u64> {
316 match self {
317 Self::Frontier
318 | Self::Homestead
319 | Self::Dao
320 | Self::Tangerine
321 | Self::SpuriousDragon
322 | Self::Byzantium
323 | Self::Constantinople
324 | Self::Petersburg
325 | Self::Istanbul
326 | Self::MuirGlacier
327 | Self::Berlin
328 | Self::London
329 | Self::ArrowGlacier
330 | Self::GrayGlacier
331 | Self::Paris => Some(HOLESKY_PARIS_TIMESTAMP),
332 Self::Shanghai => Some(HOLESKY_SHANGHAI_TIMESTAMP),
333 Self::Cancun => Some(HOLESKY_CANCUN_TIMESTAMP),
334 Self::Prague => Some(HOLESKY_PRAGUE_TIMESTAMP),
335 Self::Osaka => Some(HOLESKY_OSAKA_TIMESTAMP),
336 Self::Bpo1 => Some(HOLESKY_BPO1_TIMESTAMP),
337 Self::Bpo2 => Some(HOLESKY_BPO2_TIMESTAMP),
338 _ => None,
339 }
340 }
341
342 pub const fn hoodi_activation_timestamp(&self) -> Option<u64> {
344 match self {
345 Self::Frontier
346 | Self::Homestead
347 | Self::Dao
348 | Self::Tangerine
349 | Self::SpuriousDragon
350 | Self::Byzantium
351 | Self::Constantinople
352 | Self::Petersburg
353 | Self::Istanbul
354 | Self::MuirGlacier
355 | Self::Berlin
356 | Self::London
357 | Self::ArrowGlacier
358 | Self::GrayGlacier
359 | Self::Paris
360 | Self::Shanghai
361 | Self::Cancun => Some(0),
362 Self::Prague => Some(HOODI_PRAGUE_TIMESTAMP),
363 Self::Osaka => Some(HOODI_OSAKA_TIMESTAMP),
364 Self::Bpo1 => Some(HOODI_BPO1_TIMESTAMP),
365 Self::Bpo2 => Some(HOODI_BPO2_TIMESTAMP),
366 _ => None,
367 }
368 }
369
370 pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option<u64> {
373 match self {
374 Self::Frontier
375 | Self::Homestead
376 | Self::Dao
377 | Self::Tangerine
378 | Self::SpuriousDragon
379 | Self::Byzantium
380 | Self::Constantinople
381 | Self::Petersburg
382 | Self::Istanbul
383 | Self::MuirGlacier
384 | Self::Berlin
385 | Self::London
386 | Self::ArrowGlacier
387 | Self::GrayGlacier
388 | Self::Paris => Some(ARBITRUM_SEPOLIA_PARIS_TIMESTAMP),
389 Self::Shanghai => Some(ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP),
390 Self::Cancun => Some(ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP),
391 Self::Prague => Some(ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP),
392 _ => None,
393 }
394 }
395
396 pub const fn arbitrum_activation_timestamp(&self) -> Option<u64> {
398 match self {
399 Self::Frontier
400 | Self::Homestead
401 | Self::Dao
402 | Self::Tangerine
403 | Self::SpuriousDragon
404 | Self::Byzantium
405 | Self::Constantinople
406 | Self::Petersburg
407 | Self::Istanbul
408 | Self::MuirGlacier
409 | Self::Berlin
410 | Self::London
411 | Self::ArrowGlacier
412 | Self::GrayGlacier
413 | Self::Paris => Some(ARBITRUM_ONE_PARIS_TIMESTAMP),
414 Self::Shanghai => Some(ARBITRUM_ONE_SHANGHAI_TIMESTAMP),
415 Self::Cancun => Some(ARBITRUM_ONE_CANCUN_TIMESTAMP),
416 Self::Prague => Some(ARBITRUM_ONE_PRAGUE_TIMESTAMP),
417 _ => None,
418 }
419 }
420
421 pub const fn mainnet() -> [(Self, ForkCondition); 21] {
423 [
424 (Self::Frontier, ForkCondition::Block(MAINNET_FRONTIER_BLOCK)),
425 (Self::Homestead, ForkCondition::Block(MAINNET_HOMESTEAD_BLOCK)),
426 (Self::Dao, ForkCondition::Block(MAINNET_DAO_BLOCK)),
427 (Self::Tangerine, ForkCondition::Block(MAINNET_TANGERINE_BLOCK)),
428 (Self::SpuriousDragon, ForkCondition::Block(MAINNET_SPURIOUS_DRAGON_BLOCK)),
429 (Self::Byzantium, ForkCondition::Block(MAINNET_BYZANTIUM_BLOCK)),
430 (Self::Constantinople, ForkCondition::Block(MAINNET_CONSTANTINOPLE_BLOCK)),
431 (Self::Petersburg, ForkCondition::Block(MAINNET_PETERSBURG_BLOCK)),
432 (Self::Istanbul, ForkCondition::Block(MAINNET_ISTANBUL_BLOCK)),
433 (Self::MuirGlacier, ForkCondition::Block(MAINNET_MUIR_GLACIER_BLOCK)),
434 (Self::Berlin, ForkCondition::Block(MAINNET_BERLIN_BLOCK)),
435 (Self::London, ForkCondition::Block(MAINNET_LONDON_BLOCK)),
436 (Self::ArrowGlacier, ForkCondition::Block(MAINNET_ARROW_GLACIER_BLOCK)),
437 (Self::GrayGlacier, ForkCondition::Block(MAINNET_GRAY_GLACIER_BLOCK)),
438 (
439 Self::Paris,
440 ForkCondition::TTD {
441 activation_block_number: MAINNET_PARIS_BLOCK,
442 fork_block: None,
443 total_difficulty: MAINNET_PARIS_TTD,
444 },
445 ),
446 (Self::Shanghai, ForkCondition::Timestamp(MAINNET_SHANGHAI_TIMESTAMP)),
447 (Self::Cancun, ForkCondition::Timestamp(MAINNET_CANCUN_TIMESTAMP)),
448 (Self::Prague, ForkCondition::Timestamp(MAINNET_PRAGUE_TIMESTAMP)),
449 (Self::Osaka, ForkCondition::Timestamp(MAINNET_OSAKA_TIMESTAMP)),
450 (Self::Bpo1, ForkCondition::Timestamp(MAINNET_BPO1_TIMESTAMP)),
451 (Self::Bpo2, ForkCondition::Timestamp(MAINNET_BPO2_TIMESTAMP)),
452 ]
453 }
454
455 pub const fn sepolia() -> [(Self, ForkCondition); 19] {
457 [
458 (Self::Frontier, ForkCondition::Block(0)),
459 (Self::Homestead, ForkCondition::Block(0)),
460 (Self::Dao, ForkCondition::Block(0)),
461 (Self::Tangerine, ForkCondition::Block(0)),
462 (Self::SpuriousDragon, ForkCondition::Block(0)),
463 (Self::Byzantium, ForkCondition::Block(0)),
464 (Self::Constantinople, ForkCondition::Block(0)),
465 (Self::Petersburg, ForkCondition::Block(0)),
466 (Self::Istanbul, ForkCondition::Block(0)),
467 (Self::MuirGlacier, ForkCondition::Block(0)),
468 (Self::Berlin, ForkCondition::Block(0)),
469 (Self::London, ForkCondition::Block(0)),
470 (
471 Self::Paris,
472 ForkCondition::TTD {
473 activation_block_number: SEPOLIA_PARIS_BLOCK,
474 fork_block: Some(SEPOLIA_PARIS_FORK_BLOCK),
475 total_difficulty: SEPOLIA_PARIS_TTD,
476 },
477 ),
478 (Self::Shanghai, ForkCondition::Timestamp(SEPOLIA_SHANGHAI_TIMESTAMP)),
479 (Self::Cancun, ForkCondition::Timestamp(SEPOLIA_CANCUN_TIMESTAMP)),
480 (Self::Prague, ForkCondition::Timestamp(SEPOLIA_PRAGUE_TIMESTAMP)),
481 (Self::Osaka, ForkCondition::Timestamp(SEPOLIA_OSAKA_TIMESTAMP)),
482 (Self::Bpo1, ForkCondition::Timestamp(SEPOLIA_BPO1_TIMESTAMP)),
483 (Self::Bpo2, ForkCondition::Timestamp(SEPOLIA_BPO2_TIMESTAMP)),
484 ]
485 }
486
487 pub const fn holesky() -> [(Self, ForkCondition); 19] {
489 [
490 (Self::Frontier, ForkCondition::Block(0)),
491 (Self::Homestead, ForkCondition::Block(0)),
492 (Self::Dao, ForkCondition::Block(0)),
493 (Self::Tangerine, ForkCondition::Block(0)),
494 (Self::SpuriousDragon, ForkCondition::Block(0)),
495 (Self::Byzantium, ForkCondition::Block(0)),
496 (Self::Constantinople, ForkCondition::Block(0)),
497 (Self::Petersburg, ForkCondition::Block(0)),
498 (Self::Istanbul, ForkCondition::Block(0)),
499 (Self::MuirGlacier, ForkCondition::Block(0)),
500 (Self::Berlin, ForkCondition::Block(0)),
501 (Self::London, ForkCondition::Block(0)),
502 (
503 Self::Paris,
504 ForkCondition::TTD {
505 activation_block_number: 0,
506 fork_block: Some(0),
507 total_difficulty: U256::ZERO,
508 },
509 ),
510 (Self::Shanghai, ForkCondition::Timestamp(HOLESKY_SHANGHAI_TIMESTAMP)),
511 (Self::Cancun, ForkCondition::Timestamp(HOLESKY_CANCUN_TIMESTAMP)),
512 (Self::Prague, ForkCondition::Timestamp(HOLESKY_PRAGUE_TIMESTAMP)),
513 (Self::Osaka, ForkCondition::Timestamp(HOLESKY_OSAKA_TIMESTAMP)),
514 (Self::Bpo1, ForkCondition::Timestamp(HOLESKY_BPO1_TIMESTAMP)),
515 (Self::Bpo2, ForkCondition::Timestamp(HOLESKY_BPO2_TIMESTAMP)),
516 ]
517 }
518
519 pub const fn hoodi() -> [(Self, ForkCondition); 19] {
521 [
522 (Self::Frontier, ForkCondition::Block(0)),
523 (Self::Homestead, ForkCondition::Block(0)),
524 (Self::Dao, ForkCondition::Block(0)),
525 (Self::Tangerine, ForkCondition::Block(0)),
526 (Self::SpuriousDragon, ForkCondition::Block(0)),
527 (Self::Byzantium, ForkCondition::Block(0)),
528 (Self::Constantinople, ForkCondition::Block(0)),
529 (Self::Petersburg, ForkCondition::Block(0)),
530 (Self::Istanbul, ForkCondition::Block(0)),
531 (Self::MuirGlacier, ForkCondition::Block(0)),
532 (Self::Berlin, ForkCondition::Block(0)),
533 (Self::London, ForkCondition::Block(0)),
534 (
535 Self::Paris,
536 ForkCondition::TTD {
537 activation_block_number: 0,
538 fork_block: Some(0),
539 total_difficulty: U256::ZERO,
540 },
541 ),
542 (Self::Shanghai, ForkCondition::Timestamp(0)),
543 (Self::Cancun, ForkCondition::Timestamp(0)),
544 (Self::Prague, ForkCondition::Timestamp(HOODI_PRAGUE_TIMESTAMP)),
545 (Self::Osaka, ForkCondition::Timestamp(HOODI_OSAKA_TIMESTAMP)),
546 (Self::Bpo1, ForkCondition::Timestamp(HOODI_BPO1_TIMESTAMP)),
547 (Self::Bpo2, ForkCondition::Timestamp(HOODI_BPO2_TIMESTAMP)),
548 ]
549 }
550
551 pub const fn devnet() -> [(Self, ForkCondition); 19] {
553 [
554 (Self::Frontier, ForkCondition::ZERO_BLOCK),
555 (Self::Homestead, ForkCondition::ZERO_BLOCK),
556 (Self::Dao, ForkCondition::ZERO_BLOCK),
557 (Self::Tangerine, ForkCondition::ZERO_BLOCK),
558 (Self::SpuriousDragon, ForkCondition::ZERO_BLOCK),
559 (Self::Byzantium, ForkCondition::ZERO_BLOCK),
560 (Self::Constantinople, ForkCondition::ZERO_BLOCK),
561 (Self::Petersburg, ForkCondition::ZERO_BLOCK),
562 (Self::Istanbul, ForkCondition::ZERO_BLOCK),
563 (Self::MuirGlacier, ForkCondition::ZERO_BLOCK),
564 (Self::Berlin, ForkCondition::ZERO_BLOCK),
565 (Self::London, ForkCondition::ZERO_BLOCK),
566 (
567 Self::Paris,
568 ForkCondition::TTD {
569 activation_block_number: 0,
570 fork_block: None,
571 total_difficulty: U256::ZERO,
572 },
573 ),
574 (Self::Shanghai, ForkCondition::ZERO_TIMESTAMP),
575 (Self::Cancun, ForkCondition::ZERO_TIMESTAMP),
576 (Self::Prague, ForkCondition::ZERO_TIMESTAMP),
577 (Self::Osaka, ForkCondition::ZERO_TIMESTAMP),
578 (Self::Bpo1, ForkCondition::ZERO_TIMESTAMP),
579 (Self::Bpo2, ForkCondition::ZERO_TIMESTAMP),
580 ]
581 }
582
583 pub const fn from_mainnet_block_number(num: u64) -> Self {
585 match num {
586 _i if num < MAINNET_HOMESTEAD_BLOCK => Self::Frontier,
587 _i if num < MAINNET_DAO_BLOCK => Self::Homestead,
588 _i if num < MAINNET_TANGERINE_BLOCK => Self::Dao,
589 _i if num < MAINNET_SPURIOUS_DRAGON_BLOCK => Self::Tangerine,
590 _i if num < MAINNET_BYZANTIUM_BLOCK => Self::SpuriousDragon,
591 _i if num < MAINNET_CONSTANTINOPLE_BLOCK => Self::Byzantium,
592 _i if num < MAINNET_ISTANBUL_BLOCK => Self::Constantinople,
593 _i if num < MAINNET_MUIR_GLACIER_BLOCK => Self::Istanbul,
594 _i if num < MAINNET_BERLIN_BLOCK => Self::MuirGlacier,
595 _i if num < MAINNET_LONDON_BLOCK => Self::Berlin,
596 _i if num < MAINNET_ARROW_GLACIER_BLOCK => Self::London,
597 _i if num < MAINNET_PARIS_BLOCK => Self::ArrowGlacier,
598 _i if num < MAINNET_SHANGHAI_BLOCK => Self::Paris,
599 _i if num < MAINNET_CANCUN_BLOCK => Self::Shanghai,
600 _i if num < MAINNET_PRAGUE_BLOCK => Self::Cancun,
601 _ => Self::Prague,
602 }
603 }
604
605 pub fn from_chain_and_timestamp(chain: Chain, timestamp: u64) -> Option<Self> {
608 let named = chain.named()?;
609
610 match named {
611 NamedChain::Mainnet => Some(match timestamp {
612 _i if timestamp < MAINNET_HOMESTEAD_TIMESTAMP => Self::Frontier,
613 _i if timestamp < MAINNET_DAO_TIMESTAMP => Self::Homestead,
614 _i if timestamp < MAINNET_TANGERINE_TIMESTAMP => Self::Dao,
615 _i if timestamp < MAINNET_SPURIOUS_DRAGON_TIMESTAMP => Self::Tangerine,
616 _i if timestamp < MAINNET_BYZANTIUM_TIMESTAMP => Self::SpuriousDragon,
617 _i if timestamp < MAINNET_PETERSBURG_TIMESTAMP => Self::Byzantium,
618 _i if timestamp < MAINNET_ISTANBUL_TIMESTAMP => Self::Petersburg,
619 _i if timestamp < MAINNET_MUIR_GLACIER_TIMESTAMP => Self::Istanbul,
620 _i if timestamp < MAINNET_BERLIN_TIMESTAMP => Self::MuirGlacier,
621 _i if timestamp < MAINNET_LONDON_TIMESTAMP => Self::Berlin,
622 _i if timestamp < MAINNET_ARROW_GLACIER_TIMESTAMP => Self::London,
623 _i if timestamp < MAINNET_GRAY_GLACIER_TIMESTAMP => Self::ArrowGlacier,
624 _i if timestamp < MAINNET_PARIS_TIMESTAMP => Self::GrayGlacier,
625 _i if timestamp < MAINNET_SHANGHAI_TIMESTAMP => Self::Paris,
626 _i if timestamp < MAINNET_CANCUN_TIMESTAMP => Self::Shanghai,
627 _i if timestamp < MAINNET_PRAGUE_TIMESTAMP => Self::Cancun,
628 _i if timestamp < MAINNET_OSAKA_TIMESTAMP => Self::Prague,
629 _ => Self::Osaka,
630 }),
631 NamedChain::Sepolia => Some(match timestamp {
632 _i if timestamp < SEPOLIA_PARIS_TIMESTAMP => Self::London,
633 _i if timestamp < SEPOLIA_SHANGHAI_TIMESTAMP => Self::Paris,
634 _i if timestamp < SEPOLIA_CANCUN_TIMESTAMP => Self::Shanghai,
635 _i if timestamp < SEPOLIA_PRAGUE_TIMESTAMP => Self::Cancun,
636 _i if timestamp < SEPOLIA_OSAKA_TIMESTAMP => Self::Prague,
637 _ => Self::Osaka,
638 }),
639 NamedChain::Holesky => Some(match timestamp {
640 _i if timestamp < HOLESKY_SHANGHAI_TIMESTAMP => Self::Paris,
641 _i if timestamp < HOLESKY_CANCUN_TIMESTAMP => Self::Shanghai,
642 _i if timestamp < HOLESKY_PRAGUE_TIMESTAMP => Self::Cancun,
643 _i if timestamp < HOLESKY_OSAKA_TIMESTAMP => Self::Prague,
644 _ => Self::Osaka,
645 }),
646 NamedChain::Hoodi => Some(match timestamp {
647 _i if timestamp < HOODI_PRAGUE_TIMESTAMP => Self::Cancun,
648 _i if timestamp < HOODI_OSAKA_TIMESTAMP => Self::Prague,
649 _ => Self::Osaka,
650 }),
651 NamedChain::Arbitrum => Some(match timestamp {
652 _i if timestamp < ARBITRUM_ONE_SHANGHAI_TIMESTAMP => Self::Paris,
653 _i if timestamp < ARBITRUM_ONE_CANCUN_TIMESTAMP => Self::Shanghai,
654 _i if timestamp < ARBITRUM_ONE_PRAGUE_TIMESTAMP => Self::Cancun,
655 _ => Self::Prague,
656 }),
657 NamedChain::ArbitrumSepolia => Some(match timestamp {
658 _i if timestamp < ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP => Self::Paris,
659 _i if timestamp < ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP => Self::Shanghai,
660 _i if timestamp < ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP => Self::Cancun,
661 _ => Self::Prague,
662 }),
663 _ => None,
664 }
665 }
666}
667
668#[auto_impl::auto_impl(&, Arc)]
670pub trait EthereumHardforks {
671 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition;
674
675 fn is_ethereum_fork_active_at_timestamp(&self, fork: EthereumHardfork, timestamp: u64) -> bool {
677 self.ethereum_fork_activation(fork).active_at_timestamp(timestamp)
678 }
679
680 fn is_ethereum_fork_active_at_block(&self, fork: EthereumHardfork, block_number: u64) -> bool {
682 self.ethereum_fork_activation(fork).active_at_block(block_number)
683 }
684
685 fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
688 self.is_ethereum_fork_active_at_block(EthereumHardfork::Homestead, block_number)
689 }
690
691 fn is_tangerine_whistle_active_at_block(&self, block_number: u64) -> bool {
694 self.is_ethereum_fork_active_at_block(EthereumHardfork::Tangerine, block_number)
695 }
696
697 fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool {
700 self.is_ethereum_fork_active_at_block(EthereumHardfork::SpuriousDragon, block_number)
701 }
702
703 fn is_byzantium_active_at_block(&self, block_number: u64) -> bool {
706 self.is_ethereum_fork_active_at_block(EthereumHardfork::Byzantium, block_number)
707 }
708
709 fn is_constantinople_active_at_block(&self, block_number: u64) -> bool {
712 self.is_ethereum_fork_active_at_block(EthereumHardfork::Constantinople, block_number)
713 }
714
715 fn is_petersburg_active_at_block(&self, block_number: u64) -> bool {
718 self.is_ethereum_fork_active_at_block(EthereumHardfork::Petersburg, block_number)
719 }
720
721 fn is_istanbul_active_at_block(&self, block_number: u64) -> bool {
724 self.is_ethereum_fork_active_at_block(EthereumHardfork::Istanbul, block_number)
725 }
726
727 fn is_berlin_active_at_block(&self, block_number: u64) -> bool {
730 self.is_ethereum_fork_active_at_block(EthereumHardfork::Berlin, block_number)
731 }
732
733 fn is_london_active_at_block(&self, block_number: u64) -> bool {
736 self.is_ethereum_fork_active_at_block(EthereumHardfork::London, block_number)
737 }
738
739 fn is_paris_active_at_block(&self, block_number: u64) -> bool {
742 self.is_ethereum_fork_active_at_block(EthereumHardfork::Paris, block_number)
743 }
744
745 fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
748 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
749 }
750
751 fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
753 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
754 }
755
756 fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
758 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
759 }
760
761 fn is_osaka_active_at_timestamp(&self, timestamp: u64) -> bool {
763 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp)
764 }
765
766 fn is_amsterdam_active_at_timestamp(&self, timestamp: u64) -> bool {
769 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Amsterdam, timestamp)
770 }
771
772 fn is_bpo1_active_at_timestamp(&self, timestamp: u64) -> bool {
774 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Bpo1, timestamp)
775 }
776
777 fn is_bpo2_active_at_timestamp(&self, timestamp: u64) -> bool {
779 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Bpo2, timestamp)
780 }
781
782 fn is_bpo3_active_at_timestamp(&self, timestamp: u64) -> bool {
784 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Bpo3, timestamp)
785 }
786
787 fn is_bpo4_active_at_timestamp(&self, timestamp: u64) -> bool {
789 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Bpo4, timestamp)
790 }
791
792 fn is_bpo5_active_at_timestamp(&self, timestamp: u64) -> bool {
794 self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Bpo5, timestamp)
795 }
796}
797
798#[derive(Debug, Clone)]
801pub struct EthereumChainHardforks {
802 forks: Vec<(EthereumHardfork, ForkCondition)>,
803}
804
805impl EthereumChainHardforks {
806 pub fn new(forks: impl IntoIterator<Item = (EthereumHardfork, ForkCondition)>) -> Self {
808 let mut forks = forks.into_iter().collect::<Vec<_>>();
809 forks.sort();
810 Self { forks }
811 }
812
813 pub fn mainnet() -> Self {
815 Self::new(EthereumHardfork::mainnet())
816 }
817
818 pub fn sepolia() -> Self {
820 Self::new(EthereumHardfork::sepolia())
821 }
822
823 pub fn holesky() -> Self {
825 Self::new(EthereumHardfork::holesky())
826 }
827
828 pub fn hoodi() -> Self {
830 Self::new(EthereumHardfork::hoodi())
831 }
832
833 pub fn devnet() -> Self {
835 Self::new(EthereumHardfork::devnet())
836 }
837}
838
839impl EthereumHardforks for EthereumChainHardforks {
840 fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
841 let Ok(idx) = self.forks.binary_search_by(|(f, _)| f.cmp(&fork)) else {
842 return ForkCondition::Never;
843 };
844
845 self.forks[idx].1
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use alloc::vec::Vec;
853 use core::str::FromStr;
854
855 #[test]
856 fn check_hardfork_from_str() {
857 let hardfork_str = [
858 "frOntier",
859 "homEstead",
860 "dao",
861 "tAngerIne",
862 "spurIousdrAgon",
863 "byzAntium",
864 "constantinople",
865 "petersburg",
866 "istanbul",
867 "muirglacier",
868 "bErlin",
869 "lonDon",
870 "arrowglacier",
871 "grayglacier",
872 "PARIS",
873 "ShAnGhAI",
874 "CaNcUn",
875 "PrAguE",
876 "OsAkA",
877 "Bpo1",
878 "BPO2",
879 "bpo3",
880 "bPo4",
881 "bpO5",
882 ];
883 let expected_hardforks = [
884 EthereumHardfork::Frontier,
885 EthereumHardfork::Homestead,
886 EthereumHardfork::Dao,
887 EthereumHardfork::Tangerine,
888 EthereumHardfork::SpuriousDragon,
889 EthereumHardfork::Byzantium,
890 EthereumHardfork::Constantinople,
891 EthereumHardfork::Petersburg,
892 EthereumHardfork::Istanbul,
893 EthereumHardfork::MuirGlacier,
894 EthereumHardfork::Berlin,
895 EthereumHardfork::London,
896 EthereumHardfork::ArrowGlacier,
897 EthereumHardfork::GrayGlacier,
898 EthereumHardfork::Paris,
899 EthereumHardfork::Shanghai,
900 EthereumHardfork::Cancun,
901 EthereumHardfork::Prague,
902 EthereumHardfork::Osaka,
903 EthereumHardfork::Bpo1,
904 EthereumHardfork::Bpo2,
905 EthereumHardfork::Bpo3,
906 EthereumHardfork::Bpo4,
907 EthereumHardfork::Bpo5,
908 ];
909
910 let hardforks: Vec<EthereumHardfork> =
911 hardfork_str.iter().map(|h| EthereumHardfork::from_str(h).unwrap()).collect();
912
913 assert_eq!(hardforks, expected_hardforks);
914 }
915
916 #[test]
917 fn check_nonexistent_hardfork_from_str() {
918 assert!(EthereumHardfork::from_str("not a hardfork").is_err());
919 }
920
921 #[test]
922 fn test_reverse_lookup_by_chain_id() {
923 let test_cases = [
925 (Chain::mainnet(), MAINNET_FRONTIER_TIMESTAMP - 1, EthereumHardfork::Frontier),
929 (Chain::mainnet(), MAINNET_FRONTIER_TIMESTAMP, EthereumHardfork::Frontier),
930 (Chain::mainnet(), MAINNET_HOMESTEAD_TIMESTAMP, EthereumHardfork::Homestead),
931 (Chain::mainnet(), MAINNET_DAO_TIMESTAMP, EthereumHardfork::Dao),
932 (Chain::mainnet(), MAINNET_TANGERINE_TIMESTAMP, EthereumHardfork::Tangerine),
933 (Chain::mainnet(), MAINNET_SPURIOUS_DRAGON_TIMESTAMP, EthereumHardfork::SpuriousDragon),
934 (Chain::mainnet(), MAINNET_BYZANTIUM_TIMESTAMP, EthereumHardfork::Byzantium),
935 (Chain::mainnet(), MAINNET_PETERSBURG_TIMESTAMP, EthereumHardfork::Petersburg),
936 (Chain::mainnet(), MAINNET_ISTANBUL_TIMESTAMP, EthereumHardfork::Istanbul),
937 (Chain::mainnet(), MAINNET_MUIR_GLACIER_TIMESTAMP, EthereumHardfork::MuirGlacier),
938 (Chain::mainnet(), MAINNET_BERLIN_TIMESTAMP, EthereumHardfork::Berlin),
939 (Chain::mainnet(), MAINNET_LONDON_TIMESTAMP, EthereumHardfork::London),
940 (Chain::mainnet(), MAINNET_ARROW_GLACIER_TIMESTAMP, EthereumHardfork::ArrowGlacier),
941 (Chain::mainnet(), MAINNET_GRAY_GLACIER_TIMESTAMP, EthereumHardfork::GrayGlacier),
942 (Chain::mainnet(), MAINNET_PARIS_TIMESTAMP, EthereumHardfork::Paris),
943 (Chain::mainnet(), MAINNET_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
944 (Chain::mainnet(), MAINNET_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
945 (Chain::mainnet(), MAINNET_PRAGUE_TIMESTAMP, EthereumHardfork::Prague),
946 (Chain::sepolia(), SEPOLIA_PARIS_TIMESTAMP - 1, EthereumHardfork::London),
949 (Chain::sepolia(), SEPOLIA_PARIS_TIMESTAMP, EthereumHardfork::Paris),
950 (Chain::sepolia(), SEPOLIA_SHANGHAI_TIMESTAMP - 1, EthereumHardfork::Paris),
951 (Chain::sepolia(), SEPOLIA_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
952 (Chain::sepolia(), SEPOLIA_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
953 (Chain::sepolia(), SEPOLIA_PRAGUE_TIMESTAMP - 1, EthereumHardfork::Cancun),
954 (Chain::sepolia(), SEPOLIA_PRAGUE_TIMESTAMP + 1, EthereumHardfork::Prague),
955 (Chain::sepolia(), SEPOLIA_OSAKA_TIMESTAMP, EthereumHardfork::Osaka),
956 (Chain::holesky(), HOLESKY_PARIS_TIMESTAMP - 1, EthereumHardfork::Paris),
959 (Chain::holesky(), HOLESKY_PARIS_TIMESTAMP, EthereumHardfork::Paris),
960 (Chain::holesky(), HOLESKY_SHANGHAI_TIMESTAMP - 1, EthereumHardfork::Paris),
961 (Chain::holesky(), HOLESKY_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
962 (Chain::holesky(), HOLESKY_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
963 (Chain::holesky(), HOLESKY_PRAGUE_TIMESTAMP - 1, EthereumHardfork::Cancun),
964 (Chain::holesky(), HOLESKY_PRAGUE_TIMESTAMP + 1, EthereumHardfork::Prague),
965 (Chain::holesky(), HOLESKY_OSAKA_TIMESTAMP, EthereumHardfork::Osaka),
966 (Chain::hoodi(), HOODI_PRAGUE_TIMESTAMP - 1, EthereumHardfork::Cancun),
969 (Chain::hoodi(), HOODI_PRAGUE_TIMESTAMP, EthereumHardfork::Prague),
970 (Chain::hoodi(), HOODI_OSAKA_TIMESTAMP, EthereumHardfork::Osaka),
971 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_PARIS_TIMESTAMP - 1, EthereumHardfork::Paris),
974 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_PARIS_TIMESTAMP, EthereumHardfork::Paris),
975 (
976 Chain::arbitrum_mainnet(),
977 ARBITRUM_ONE_SHANGHAI_TIMESTAMP - 1,
978 EthereumHardfork::Paris,
979 ),
980 (
981 Chain::arbitrum_mainnet(),
982 ARBITRUM_ONE_SHANGHAI_TIMESTAMP,
983 EthereumHardfork::Shanghai,
984 ),
985 (Chain::arbitrum_mainnet(), ARBITRUM_ONE_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
986 (
987 Chain::arbitrum_mainnet(),
988 ARBITRUM_ONE_PRAGUE_TIMESTAMP - 1,
989 EthereumHardfork::Cancun,
990 ),
991 (
992 Chain::arbitrum_mainnet(),
993 ARBITRUM_ONE_PRAGUE_TIMESTAMP + 1,
994 EthereumHardfork::Prague,
995 ),
996 (
999 Chain::arbitrum_sepolia(),
1000 ARBITRUM_SEPOLIA_PARIS_TIMESTAMP - 1,
1001 EthereumHardfork::Paris,
1002 ),
1003 (Chain::arbitrum_sepolia(), ARBITRUM_SEPOLIA_PARIS_TIMESTAMP, EthereumHardfork::Paris),
1004 (
1005 Chain::arbitrum_sepolia(),
1006 ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP - 1,
1007 EthereumHardfork::Paris,
1008 ),
1009 (
1010 Chain::arbitrum_sepolia(),
1011 ARBITRUM_SEPOLIA_SHANGHAI_TIMESTAMP,
1012 EthereumHardfork::Shanghai,
1013 ),
1014 (
1015 Chain::arbitrum_sepolia(),
1016 ARBITRUM_SEPOLIA_CANCUN_TIMESTAMP,
1017 EthereumHardfork::Cancun,
1018 ),
1019 (
1020 Chain::arbitrum_sepolia(),
1021 ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP - 1,
1022 EthereumHardfork::Cancun,
1023 ),
1024 (
1025 Chain::arbitrum_sepolia(),
1026 ARBITRUM_SEPOLIA_PRAGUE_TIMESTAMP + 1,
1027 EthereumHardfork::Prague,
1028 ),
1029 ];
1030
1031 for (chain_id, timestamp, expected) in test_cases {
1032 assert_eq!(
1033 EthereumHardfork::from_chain_and_timestamp(chain_id, timestamp),
1034 Some(expected),
1035 "chain {chain_id} at timestamp {timestamp}"
1036 );
1037 }
1038
1039 assert_eq!(
1041 EthereumHardfork::from_chain_and_timestamp(Chain::from_id(99999), 1000000),
1042 None
1043 );
1044 }
1045
1046 #[test]
1047 fn test_timestamp_functions_consistency() {
1048 let test_cases = [
1049 (MAINNET_LONDON_TIMESTAMP, EthereumHardfork::London),
1050 (MAINNET_SHANGHAI_TIMESTAMP, EthereumHardfork::Shanghai),
1051 (MAINNET_CANCUN_TIMESTAMP, EthereumHardfork::Cancun),
1052 ];
1053
1054 for (timestamp, fork) in test_cases {
1055 assert_eq!(
1056 EthereumHardfork::from_chain_and_timestamp(Chain::mainnet(), timestamp),
1057 Some(fork)
1058 );
1059 assert_eq!(fork.activation_timestamp(Chain::mainnet()), Some(timestamp));
1060 }
1061 }
1062
1063 macro_rules! test_chain_config {
1064 ($modname:ident, $ts_fn:ident, $bn_fn:ident) => {
1065 mod $modname {
1066 use super::*;
1067 #[test]
1068 fn test_chain_config() {
1069 for (fork, condition) in EthereumHardfork::$modname() {
1070 match condition {
1071 ForkCondition::Timestamp(ts) => {
1072 assert_eq!(fork.$ts_fn(), Some(ts));
1073 }
1074 ForkCondition::Block(bn) => {
1075 assert_eq!(fork.$bn_fn(), Some(bn));
1076 }
1077 ForkCondition::TTD { activation_block_number, .. } => {
1078 assert_eq!(fork.$bn_fn(), Some(activation_block_number));
1079 }
1080 _ => {}
1081 }
1082 }
1083 }
1084 }
1085 };
1086 }
1087
1088 test_chain_config!(mainnet, mainnet_activation_timestamp, mainnet_activation_block);
1089 test_chain_config!(sepolia, sepolia_activation_timestamp, sepolia_activation_block);
1090 test_chain_config!(holesky, holesky_activation_timestamp, holesky_activation_block);
1091 test_chain_config!(hoodi, hoodi_activation_timestamp, hoodi_activation_block);
1092}