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