feat: use new ChainHardforks type on ChainSpec (#9065)

This commit is contained in:
joshieDo
2024-06-27 19:39:35 +02:00
committed by GitHub
parent c23fe39dd3
commit 50ee497c75
57 changed files with 1708 additions and 1465 deletions

View File

@ -23,11 +23,15 @@ crc = "3"
# misc
serde = { workspace = true, features = ["derive"], optional = true }
thiserror-no-std = { workspace = true, default-features = false }
once_cell.workspace = true
dyn-clone.workspace = true
rustc-hash.workspace = true
# arbitrary utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }
auto_impl.workspace = true
[dev-dependencies]
arbitrary = { workspace = true, features = ["derive"] }

View File

@ -1,23 +0,0 @@
use crate::{ForkCondition, Hardfork};
use alloy_primitives::uint;
/// Dev hardforks
pub const DEV_HARDFORKS: [(Hardfork, ForkCondition); 14] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Dao, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(1561651)),
(Hardfork::Berlin, ForkCondition::Block(4460644)),
(Hardfork::London, ForkCondition::Block(5062605)),
(
Hardfork::Paris,
ForkCondition::TTD { fork_block: None, total_difficulty: uint!(10_790_000_U256) },
),
(Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)),
(Hardfork::Cancun, ForkCondition::Timestamp(1705473120)),
];

View File

@ -1,94 +0,0 @@
use crate::{ForkCondition, Hardfork};
use alloy_primitives::{uint, U256};
/// Ethereum mainnet hardforks
pub const MAINNET_HARDFORKS: [(Hardfork, ForkCondition); 17] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(1150000)),
(Hardfork::Dao, ForkCondition::Block(1920000)),
(Hardfork::Tangerine, ForkCondition::Block(2463000)),
(Hardfork::SpuriousDragon, ForkCondition::Block(2675000)),
(Hardfork::Byzantium, ForkCondition::Block(4370000)),
(Hardfork::Constantinople, ForkCondition::Block(7280000)),
(Hardfork::Petersburg, ForkCondition::Block(7280000)),
(Hardfork::Istanbul, ForkCondition::Block(9069000)),
(Hardfork::MuirGlacier, ForkCondition::Block(9200000)),
(Hardfork::Berlin, ForkCondition::Block(12244000)),
(Hardfork::London, ForkCondition::Block(12965000)),
(Hardfork::ArrowGlacier, ForkCondition::Block(13773000)),
(Hardfork::GrayGlacier, ForkCondition::Block(15050000)),
(
Hardfork::Paris,
ForkCondition::TTD {
fork_block: None,
total_difficulty: uint!(58_750_000_000_000_000_000_000_U256),
},
),
(Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)),
(Hardfork::Cancun, ForkCondition::Timestamp(1710338135)),
];
/// Ethereum Goerli hardforks
pub const GOERLI_HARDFORKS: [(Hardfork, ForkCondition); 14] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Dao, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(1561651)),
(Hardfork::Berlin, ForkCondition::Block(4460644)),
(Hardfork::London, ForkCondition::Block(5062605)),
(
Hardfork::Paris,
ForkCondition::TTD { fork_block: None, total_difficulty: uint!(10_790_000_U256) },
),
(Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)),
(Hardfork::Cancun, ForkCondition::Timestamp(1705473120)),
];
/// Ethereum Sepolia hardforks
pub const SEPOLIA_HARDFORKS: [(Hardfork, ForkCondition); 15] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Dao, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(0)),
(Hardfork::London, ForkCondition::Block(0)),
(
Hardfork::Paris,
ForkCondition::TTD {
fork_block: Some(1735371),
total_difficulty: uint!(17_000_000_000_000_000_U256),
},
),
(Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)),
(Hardfork::Cancun, ForkCondition::Timestamp(1706655072)),
];
/// Ethereum Holesky hardforks
pub const HOLESKY_HARDFORKS: [(Hardfork, ForkCondition); 15] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Dao, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(0)),
(Hardfork::London, ForkCondition::Block(0)),
(Hardfork::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Hardfork::Shanghai, ForkCondition::Timestamp(1696000704)),
(Hardfork::Cancun, ForkCondition::Timestamp(1707305664)),
];

View File

@ -1,9 +0,0 @@
/// Ethereum chains
pub mod ethereum;
/// Optimism chains
#[cfg(feature = "optimism")]
pub mod optimism;
/// Dev chain
pub mod dev;

View File

@ -1,105 +0,0 @@
use crate::{ForkCondition, Hardfork};
use alloy_primitives::U256;
/// Optimism mainnet hardforks
pub const OP_MAINNET_HARDFORKS: [(Hardfork, ForkCondition); 21] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(3950000)),
(Hardfork::London, ForkCondition::Block(105235063)),
(Hardfork::ArrowGlacier, ForkCondition::Block(105235063)),
(Hardfork::GrayGlacier, ForkCondition::Block(105235063)),
(
Hardfork::Paris,
ForkCondition::TTD { fork_block: Some(105235063), total_difficulty: U256::ZERO },
),
(Hardfork::Bedrock, ForkCondition::Block(105235063)),
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
(Hardfork::Shanghai, ForkCondition::Timestamp(1704992401)),
(Hardfork::Canyon, ForkCondition::Timestamp(1704992401)),
(Hardfork::Cancun, ForkCondition::Timestamp(1710374401)),
(Hardfork::Ecotone, ForkCondition::Timestamp(1710374401)),
(Hardfork::Fjord, ForkCondition::Timestamp(1720627201)),
];
/// Optimism Sepolia hardforks
pub const OP_SEPOLIA_HARDFORKS: [(Hardfork, ForkCondition); 21] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(0)),
(Hardfork::London, ForkCondition::Block(0)),
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
(Hardfork::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Hardfork::Bedrock, ForkCondition::Block(0)),
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
(Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)),
(Hardfork::Canyon, ForkCondition::Timestamp(1699981200)),
(Hardfork::Cancun, ForkCondition::Timestamp(1708534800)),
(Hardfork::Ecotone, ForkCondition::Timestamp(1708534800)),
(Hardfork::Fjord, ForkCondition::Timestamp(1716998400)),
];
/// Base Sepolia hardforks
pub const BASE_SEPOLIA_HARDFORKS: [(Hardfork, ForkCondition); 21] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(0)),
(Hardfork::London, ForkCondition::Block(0)),
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
(Hardfork::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Hardfork::Bedrock, ForkCondition::Block(0)),
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
(Hardfork::Shanghai, ForkCondition::Timestamp(1699981200)),
(Hardfork::Canyon, ForkCondition::Timestamp(1699981200)),
(Hardfork::Cancun, ForkCondition::Timestamp(1708534800)),
(Hardfork::Ecotone, ForkCondition::Timestamp(1708534800)),
(Hardfork::Fjord, ForkCondition::Timestamp(1716998400)),
];
/// Base Mainnet hardforks
pub const BASE_MAINNET_HARDFORKS: [(Hardfork, ForkCondition); 21] = [
(Hardfork::Frontier, ForkCondition::Block(0)),
(Hardfork::Homestead, ForkCondition::Block(0)),
(Hardfork::Tangerine, ForkCondition::Block(0)),
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
(Hardfork::Byzantium, ForkCondition::Block(0)),
(Hardfork::Constantinople, ForkCondition::Block(0)),
(Hardfork::Petersburg, ForkCondition::Block(0)),
(Hardfork::Istanbul, ForkCondition::Block(0)),
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
(Hardfork::Berlin, ForkCondition::Block(0)),
(Hardfork::London, ForkCondition::Block(0)),
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
(Hardfork::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Hardfork::Bedrock, ForkCondition::Block(0)),
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
(Hardfork::Shanghai, ForkCondition::Timestamp(1704992401)),
(Hardfork::Canyon, ForkCondition::Timestamp(1704992401)),
(Hardfork::Cancun, ForkCondition::Timestamp(1710374401)),
(Hardfork::Ecotone, ForkCondition::Timestamp(1710374401)),
(Hardfork::Fjord, ForkCondition::Timestamp(1720627201)),
];

View File

@ -6,9 +6,7 @@ use alloc::{
vec::Vec,
};
use crate::{ForkCondition, Hardfork};
#[cfg(feature = "std")]
use std::collections::BTreeMap;
use crate::{hardforks::Hardforks, ForkCondition};
/// A container to pretty-print a hardfork.
///
@ -146,27 +144,22 @@ impl core::fmt::Display for DisplayHardforks {
impl DisplayHardforks {
/// Creates a new [`DisplayHardforks`] from an iterator of hardforks.
pub fn new(
hardforks: &BTreeMap<Hardfork, ForkCondition>,
known_paris_block: Option<u64>,
) -> Self {
pub fn new<H: Hardforks>(hardforks: &H, known_paris_block: Option<u64>) -> Self {
let mut pre_merge = Vec::new();
let mut with_merge = Vec::new();
let mut post_merge = Vec::new();
for (fork, condition) in hardforks {
for (fork, condition) in hardforks.forks_iter() {
let mut display_fork =
DisplayFork { name: fork.to_string(), activated_at: *condition, eip: None };
DisplayFork { name: fork.name().to_string(), activated_at: condition, eip: None };
match condition {
ForkCondition::Block(_) => {
pre_merge.push(display_fork);
}
ForkCondition::TTD { total_difficulty, .. } => {
display_fork.activated_at = ForkCondition::TTD {
fork_block: known_paris_block,
total_difficulty: *total_difficulty,
};
display_fork.activated_at =
ForkCondition::TTD { fork_block: known_paris_block, total_difficulty };
with_merge.push(display_fork);
}
ForkCondition::Timestamp(_) => {

View File

@ -1,702 +0,0 @@
use alloy_chains::Chain;
use core::{
fmt,
fmt::{Display, Formatter},
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use alloc::{format, string::String};
/// Represents the consensus type of a blockchain fork.
///
/// This enum defines two variants: `ProofOfWork` for hardforks that use a proof-of-work consensus
/// mechanism, and `ProofOfStake` for hardforks that use a proof-of-stake consensus mechanism.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ConsensusType {
/// Indicates a proof-of-work consensus mechanism.
ProofOfWork,
/// Indicates a proof-of-stake consensus mechanism.
ProofOfStake,
}
/// The name of an Ethereum hardfork.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Hardfork {
/// Frontier: <https://blog.ethereum.org/2015/03/03/ethereum-launch-process>.
Frontier,
/// Homestead: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md>.
Homestead,
/// The DAO fork: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md>.
Dao,
/// Tangerine: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md>.
Tangerine,
/// Spurious Dragon: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md>.
SpuriousDragon,
/// Byzantium: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md>.
Byzantium,
/// Constantinople: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md>.
Constantinople,
/// Petersburg: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md>.
Petersburg,
/// Istanbul: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md>.
Istanbul,
/// Muir Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md>.
MuirGlacier,
/// Berlin: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md>.
Berlin,
/// London: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md>.
London,
/// Arrow Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md>.
ArrowGlacier,
/// Gray Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md>.
GrayGlacier,
/// Paris: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md>.
Paris,
/// Bedrock: <https://blog.oplabs.co/introducing-optimism-bedrock>.
#[cfg(feature = "optimism")]
Bedrock,
/// Regolith: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#regolith>.
#[cfg(feature = "optimism")]
Regolith,
/// Shanghai: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md>.
Shanghai,
/// Canyon:
/// <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#canyon>.
#[cfg(feature = "optimism")]
Canyon,
// ArbOS11,
/// Cancun.
Cancun,
/// Ecotone: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#ecotone>.
#[cfg(feature = "optimism")]
Ecotone,
// ArbOS20Atlas,
// Upcoming
/// Prague: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/prague.md>
Prague,
/// Fjord: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#fjord>
#[cfg(feature = "optimism")]
Fjord,
}
impl Hardfork {
/// Retrieves the consensus type for the specified hardfork.
pub fn consensus_type(&self) -> ConsensusType {
if *self >= Self::Paris {
ConsensusType::ProofOfStake
} else {
ConsensusType::ProofOfWork
}
}
/// Checks if the hardfork uses Proof of Stake consensus.
pub fn is_proof_of_stake(&self) -> bool {
matches!(self.consensus_type(), ConsensusType::ProofOfStake)
}
/// Checks if the hardfork uses Proof of Work consensus.
pub fn is_proof_of_work(&self) -> bool {
matches!(self.consensus_type(), ConsensusType::ProofOfWork)
}
/// Retrieves the activation block for the specified hardfork on the given chain.
pub fn activation_block(&self, chain: Chain) -> Option<u64> {
if chain == Chain::mainnet() {
return self.mainnet_activation_block()
}
if chain == Chain::sepolia() {
return self.sepolia_activation_block()
}
if chain == Chain::holesky() {
return self.holesky_activation_block()
}
#[cfg(feature = "optimism")]
{
if chain == Chain::base_sepolia() {
return self.base_sepolia_activation_block()
}
if chain == Chain::base_mainnet() {
return self.base_mainnet_activation_block()
}
}
None
}
/// Retrieves the activation block for the specified hardfork on the Ethereum mainnet.
pub const fn mainnet_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier => Some(0),
Self::Homestead => Some(1150000),
Self::Dao => Some(1920000),
Self::Tangerine => Some(2463000),
Self::SpuriousDragon => Some(2675000),
Self::Byzantium => Some(4370000),
Self::Constantinople | Self::Petersburg => Some(7280000),
Self::Istanbul => Some(9069000),
Self::MuirGlacier => Some(9200000),
Self::Berlin => Some(12244000),
Self::London => Some(12965000),
Self::ArrowGlacier => Some(13773000),
Self::GrayGlacier => Some(15050000),
Self::Paris => Some(15537394),
Self::Shanghai => Some(17034870),
Self::Cancun => Some(19426587),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Sepolia testnet.
pub const fn sepolia_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Paris => Some(1735371),
Self::Shanghai => Some(2990908),
Self::Cancun => Some(5187023),
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier => Some(0),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Arbitrum Sepolia testnet.
pub const fn arbitrum_sepolia_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(10653737),
// Hardfork::ArbOS11 => Some(10653737),
Self::Cancun => Some(18683405),
// Hardfork::ArbOS20Atlas => Some(18683405),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Arbitrum One mainnet.
pub const fn arbitrum_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(184097479),
// Hardfork::ArbOS11 => Some(184097479),
Self::Cancun => Some(190301729),
// Hardfork::ArbOS20Atlas => Some(190301729),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Base Sepolia testnet.
#[cfg(feature = "optimism")]
pub const fn base_sepolia_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris |
Self::Bedrock |
Self::Regolith => Some(0),
Self::Shanghai | Self::Canyon => Some(2106456),
Self::Cancun | Self::Ecotone => Some(6383256),
Self::Fjord => Some(10615056),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Base mainnet.
#[cfg(feature = "optimism")]
pub const fn base_mainnet_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris |
Self::Bedrock |
Self::Regolith => Some(0),
Self::Shanghai | Self::Canyon => Some(9101527),
Self::Cancun | Self::Ecotone => Some(11188936),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the holesky testnet.
const fn holesky_activation_block(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(6698),
Self::Cancun => Some(894733),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the given chain.
pub fn activation_timestamp(&self, chain: Chain) -> Option<u64> {
if chain == Chain::mainnet() {
return self.mainnet_activation_timestamp()
}
if chain == Chain::sepolia() {
return self.sepolia_activation_timestamp()
}
if chain == Chain::holesky() {
return self.holesky_activation_timestamp()
}
#[cfg(feature = "optimism")]
{
if chain == Chain::base_sepolia() {
return self.base_sepolia_activation_timestamp()
}
if chain == Chain::base_mainnet() {
return self.base_mainnet_activation_timestamp()
}
}
None
}
/// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet.
pub const fn mainnet_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier => Some(1438226773),
Self::Homestead => Some(1457938193),
Self::Dao => Some(1468977640),
Self::Tangerine => Some(1476753571),
Self::SpuriousDragon => Some(1479788144),
Self::Byzantium => Some(1508131331),
Self::Constantinople | Self::Petersburg => Some(1551340324),
Self::Istanbul => Some(1575807909),
Self::MuirGlacier => Some(1577953849),
Self::Berlin => Some(1618481223),
Self::London => Some(1628166822),
Self::ArrowGlacier => Some(1639036523),
Self::GrayGlacier => Some(1656586444),
Self::Paris => Some(1663224162),
Self::Shanghai => Some(1681338455),
Self::Cancun => Some(1710338135),
// upcoming hardforks
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Sepolia testnet.
pub const fn sepolia_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1633267481),
Self::Shanghai => Some(1677557088),
Self::Cancun => Some(1706655072),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Holesky testnet.
pub const fn holesky_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Shanghai => Some(1696000704),
Self::Cancun => Some(1707305664),
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1695902100),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Arbitrum Sepolia
/// testnet.
pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1692726996),
Self::Shanghai => Some(1706634000),
// Hardfork::ArbOS11 => Some(1706634000),
Self::Cancun => Some(1709229600),
// Hardfork::ArbOS20Atlas => Some(1709229600),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Arbitrum One mainnet.
pub const fn arbitrum_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1622240000),
Self::Shanghai => Some(1708804873),
// Hardfork::ArbOS11 => Some(1708804873),
Self::Cancun => Some(1710424089),
// Hardfork::ArbOS20Atlas => Some(1710424089),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Base Sepolia testnet.
#[cfg(feature = "optimism")]
pub const fn base_sepolia_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris |
Self::Bedrock |
Self::Regolith => Some(1695768288),
Self::Shanghai | Self::Canyon => Some(1699981200),
Self::Cancun | Self::Ecotone => Some(1708534800),
Self::Fjord => Some(1716998400),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Base mainnet.
#[cfg(feature = "optimism")]
pub const fn base_mainnet_activation_timestamp(&self) -> Option<u64> {
#[allow(unreachable_patterns)]
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris |
Self::Bedrock |
Self::Regolith => Some(1686789347),
Self::Shanghai | Self::Canyon => Some(1704992401),
Self::Cancun | Self::Ecotone => Some(1710374401),
Self::Fjord => Some(1720627201),
_ => None,
}
}
}
impl FromStr for Hardfork {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"frontier" => Self::Frontier,
"homestead" => Self::Homestead,
"dao" => Self::Dao,
"tangerine" => Self::Tangerine,
"spuriousdragon" => Self::SpuriousDragon,
"byzantium" => Self::Byzantium,
"constantinople" => Self::Constantinople,
"petersburg" => Self::Petersburg,
"istanbul" => Self::Istanbul,
"muirglacier" => Self::MuirGlacier,
"berlin" => Self::Berlin,
"london" => Self::London,
"arrowglacier" => Self::ArrowGlacier,
"grayglacier" => Self::GrayGlacier,
"paris" => Self::Paris,
"shanghai" => Self::Shanghai,
"cancun" => Self::Cancun,
#[cfg(feature = "optimism")]
"bedrock" => Self::Bedrock,
#[cfg(feature = "optimism")]
"regolith" => Self::Regolith,
#[cfg(feature = "optimism")]
"canyon" => Self::Canyon,
#[cfg(feature = "optimism")]
"ecotone" => Self::Ecotone,
#[cfg(feature = "optimism")]
"fjord" => Self::Fjord,
"prague" => Self::Prague,
// "arbos11" => Hardfork::ArbOS11,
// "arbos20atlas" => Hardfork::ArbOS20Atlas,
_ => return Err(format!("Unknown hardfork: {s}")),
})
}
}
impl Display for Hardfork {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_hardfork_from_str() {
let hardfork_str = [
"frOntier",
"homEstead",
"dao",
"tAngerIne",
"spurIousdrAgon",
"byzAntium",
"constantinople",
"petersburg",
"istanbul",
"muirglacier",
"bErlin",
"lonDon",
"arrowglacier",
"grayglacier",
"PARIS",
"ShAnGhAI",
"CaNcUn",
"PrAguE",
];
let expected_hardforks = [
Hardfork::Frontier,
Hardfork::Homestead,
Hardfork::Dao,
Hardfork::Tangerine,
Hardfork::SpuriousDragon,
Hardfork::Byzantium,
Hardfork::Constantinople,
Hardfork::Petersburg,
Hardfork::Istanbul,
Hardfork::MuirGlacier,
Hardfork::Berlin,
Hardfork::London,
Hardfork::ArrowGlacier,
Hardfork::GrayGlacier,
Hardfork::Paris,
Hardfork::Shanghai,
Hardfork::Cancun,
Hardfork::Prague,
];
let hardforks: Vec<Hardfork> =
hardfork_str.iter().map(|h| Hardfork::from_str(h).unwrap()).collect();
assert_eq!(hardforks, expected_hardforks);
}
#[test]
#[cfg(feature = "optimism")]
fn check_op_hardfork_from_str() {
let hardfork_str = ["beDrOck", "rEgOlITH", "cAnYoN", "eCoToNe", "FJorD"];
let expected_hardforks = [
Hardfork::Bedrock,
Hardfork::Regolith,
Hardfork::Canyon,
Hardfork::Ecotone,
Hardfork::Fjord,
];
let hardforks: Vec<Hardfork> =
hardfork_str.iter().map(|h| Hardfork::from_str(h).unwrap()).collect();
assert_eq!(hardforks, expected_hardforks);
}
#[test]
fn check_nonexistent_hardfork_from_str() {
assert!(Hardfork::from_str("not a hardfork").is_err());
}
#[test]
fn check_consensus_type() {
let pow_hardforks = [
Hardfork::Frontier,
Hardfork::Homestead,
Hardfork::Dao,
Hardfork::Tangerine,
Hardfork::SpuriousDragon,
Hardfork::Byzantium,
Hardfork::Constantinople,
Hardfork::Petersburg,
Hardfork::Istanbul,
Hardfork::MuirGlacier,
Hardfork::Berlin,
Hardfork::London,
Hardfork::ArrowGlacier,
Hardfork::GrayGlacier,
];
let pos_hardforks = [Hardfork::Paris, Hardfork::Shanghai, Hardfork::Cancun];
#[cfg(feature = "optimism")]
let op_hardforks = [
Hardfork::Bedrock,
Hardfork::Regolith,
Hardfork::Canyon,
Hardfork::Ecotone,
Hardfork::Fjord,
];
for hardfork in &pow_hardforks {
assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfWork);
assert!(!hardfork.is_proof_of_stake());
assert!(hardfork.is_proof_of_work());
}
for hardfork in &pos_hardforks {
assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfStake);
assert!(hardfork.is_proof_of_stake());
assert!(!hardfork.is_proof_of_work());
}
#[cfg(feature = "optimism")]
for hardfork in &op_hardforks {
assert_eq!(hardfork.consensus_type(), ConsensusType::ProofOfStake);
assert!(hardfork.is_proof_of_stake());
assert!(!hardfork.is_proof_of_work());
}
}
}

View File

@ -0,0 +1,32 @@
use crate::{ChainHardforks, EthereumHardfork, ForkCondition};
use alloy_primitives::U256;
use once_cell::sync::Lazy;
/// Dev hardforks
pub static DEV_HARDFORKS: Lazy<ChainHardforks> = Lazy::new(|| {
ChainHardforks::new(vec![
(EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Dao.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: None, total_difficulty: U256::ZERO },
),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(0)),
#[cfg(feature = "optimism")]
(crate::OptimismHardfork::Regolith.boxed(), ForkCondition::Timestamp(0)),
#[cfg(feature = "optimism")]
(crate::OptimismHardfork::Bedrock.boxed(), ForkCondition::Block(0)),
#[cfg(feature = "optimism")]
(crate::OptimismHardfork::Ecotone.boxed(), ForkCondition::Timestamp(0)),
])
});

View File

@ -0,0 +1,441 @@
use crate::{hardfork, ChainHardforks, ForkCondition, Hardfork};
use alloy_chains::Chain;
use alloy_primitives::{uint, U256};
use core::{
fmt,
fmt::{Display, Formatter},
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
hardfork!(
/// The name of an Ethereum hardfork.
EthereumHardfork {
/// Frontier: <https://blog.ethereum.org/2015/03/03/ethereum-launch-process>.
Frontier,
/// Homestead: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md>.
Homestead,
/// The DAO fork: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md>.
Dao,
/// Tangerine: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md>.
Tangerine,
/// Spurious Dragon: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md>.
SpuriousDragon,
/// Byzantium: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md>.
Byzantium,
/// Constantinople: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md>.
Constantinople,
/// Petersburg: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md>.
Petersburg,
/// Istanbul: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md>.
Istanbul,
/// Muir Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md>.
MuirGlacier,
/// Berlin: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md>.
Berlin,
/// London: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md>.
London,
/// Arrow Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md>.
ArrowGlacier,
/// Gray Glacier: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md>.
GrayGlacier,
/// Paris: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md>.
Paris,
/// Shanghai: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md>.
Shanghai,
/// Cancun.
Cancun,
/// Prague: <https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/prague.md>
Prague,
}
);
impl EthereumHardfork {
/// Retrieves the activation block for the specified hardfork on the given chain.
pub fn activation_block(&self, chain: Chain) -> Option<u64> {
if chain == Chain::mainnet() {
return self.mainnet_activation_block()
}
if chain == Chain::sepolia() {
return self.sepolia_activation_block()
}
if chain == Chain::holesky() {
return self.holesky_activation_block()
}
None
}
/// Retrieves the activation block for the specified hardfork on the Ethereum mainnet.
pub const fn mainnet_activation_block(&self) -> Option<u64> {
match self {
Self::Frontier => Some(0),
Self::Homestead => Some(1150000),
Self::Dao => Some(1920000),
Self::Tangerine => Some(2463000),
Self::SpuriousDragon => Some(2675000),
Self::Byzantium => Some(4370000),
Self::Constantinople | Self::Petersburg => Some(7280000),
Self::Istanbul => Some(9069000),
Self::MuirGlacier => Some(9200000),
Self::Berlin => Some(12244000),
Self::London => Some(12965000),
Self::ArrowGlacier => Some(13773000),
Self::GrayGlacier => Some(15050000),
Self::Paris => Some(15537394),
Self::Shanghai => Some(17034870),
Self::Cancun => Some(19426587),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Sepolia testnet.
pub const fn sepolia_activation_block(&self) -> Option<u64> {
match self {
Self::Paris => Some(1735371),
Self::Shanghai => Some(2990908),
Self::Cancun => Some(5187023),
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier => Some(0),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the holesky testnet.
const fn holesky_activation_block(&self) -> Option<u64> {
match self {
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(6698),
Self::Cancun => Some(894733),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Arbitrum Sepolia testnet.
pub const fn arbitrum_sepolia_activation_block(&self) -> Option<u64> {
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(10653737),
// Hardfork::ArbOS11 => Some(10653737),
Self::Cancun => Some(18683405),
// Hardfork::ArbOS20Atlas => Some(18683405),
_ => None,
}
}
/// Retrieves the activation block for the specified hardfork on the Arbitrum One mainnet.
pub const fn arbitrum_activation_block(&self) -> Option<u64> {
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(0),
Self::Shanghai => Some(184097479),
// Hardfork::ArbOS11 => Some(184097479),
Self::Cancun => Some(190301729),
// Hardfork::ArbOS20Atlas => Some(190301729),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the given chain.
pub fn activation_timestamp(&self, chain: Chain) -> Option<u64> {
if chain == Chain::mainnet() {
return self.mainnet_activation_timestamp()
}
if chain == Chain::sepolia() {
return self.sepolia_activation_timestamp()
}
if chain == Chain::holesky() {
return self.holesky_activation_timestamp()
}
None
}
/// Retrieves the activation timestamp for the specified hardfork on the Ethereum mainnet.
pub const fn mainnet_activation_timestamp(&self) -> Option<u64> {
match self {
Self::Frontier => Some(1438226773),
Self::Homestead => Some(1457938193),
Self::Dao => Some(1468977640),
Self::Tangerine => Some(1476753571),
Self::SpuriousDragon => Some(1479788144),
Self::Byzantium => Some(1508131331),
Self::Constantinople | Self::Petersburg => Some(1551340324),
Self::Istanbul => Some(1575807909),
Self::MuirGlacier => Some(1577953849),
Self::Berlin => Some(1618481223),
Self::London => Some(1628166822),
Self::ArrowGlacier => Some(1639036523),
Self::GrayGlacier => Some(1656586444),
Self::Paris => Some(1663224162),
Self::Shanghai => Some(1681338455),
Self::Cancun => Some(1710338135),
// upcoming hardforks
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Sepolia testnet.
pub const fn sepolia_activation_timestamp(&self) -> Option<u64> {
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1633267481),
Self::Shanghai => Some(1677557088),
Self::Cancun => Some(1706655072),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Holesky testnet.
pub const fn holesky_activation_timestamp(&self) -> Option<u64> {
match self {
Self::Shanghai => Some(1696000704),
Self::Cancun => Some(1707305664),
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1695902100),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Arbitrum Sepolia
/// testnet.
pub const fn arbitrum_sepolia_activation_timestamp(&self) -> Option<u64> {
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1692726996),
Self::Shanghai => Some(1706634000),
// Hardfork::ArbOS11 => Some(1706634000),
Self::Cancun => Some(1709229600),
// Hardfork::ArbOS20Atlas => Some(1709229600),
_ => None,
}
}
/// Retrieves the activation timestamp for the specified hardfork on the Arbitrum One mainnet.
pub const fn arbitrum_activation_timestamp(&self) -> Option<u64> {
match self {
Self::Frontier |
Self::Homestead |
Self::Dao |
Self::Tangerine |
Self::SpuriousDragon |
Self::Byzantium |
Self::Constantinople |
Self::Petersburg |
Self::Istanbul |
Self::MuirGlacier |
Self::Berlin |
Self::London |
Self::ArrowGlacier |
Self::GrayGlacier |
Self::Paris => Some(1622240000),
Self::Shanghai => Some(1708804873),
// Hardfork::ArbOS11 => Some(1708804873),
Self::Cancun => Some(1710424089),
// Hardfork::ArbOS20Atlas => Some(1710424089),
_ => None,
}
}
/// Ethereum mainnet list of hardforks.
pub const fn mainnet() -> [(Self, ForkCondition); 17] {
[
(Self::Frontier, ForkCondition::Block(0)),
(Self::Homestead, ForkCondition::Block(1150000)),
(Self::Dao, ForkCondition::Block(1920000)),
(Self::Tangerine, ForkCondition::Block(2463000)),
(Self::SpuriousDragon, ForkCondition::Block(2675000)),
(Self::Byzantium, ForkCondition::Block(4370000)),
(Self::Constantinople, ForkCondition::Block(7280000)),
(Self::Petersburg, ForkCondition::Block(7280000)),
(Self::Istanbul, ForkCondition::Block(9069000)),
(Self::MuirGlacier, ForkCondition::Block(9200000)),
(Self::Berlin, ForkCondition::Block(12244000)),
(Self::London, ForkCondition::Block(12965000)),
(Self::ArrowGlacier, ForkCondition::Block(13773000)),
(Self::GrayGlacier, ForkCondition::Block(15050000)),
(
Self::Paris,
ForkCondition::TTD {
fork_block: None,
total_difficulty: uint!(58_750_000_000_000_000_000_000_U256),
},
),
(Self::Shanghai, ForkCondition::Timestamp(1681338455)),
(Self::Cancun, ForkCondition::Timestamp(1710338135)),
]
}
/// Ethereum goerli list of hardforks.
pub const fn goerli() -> [(Self, ForkCondition); 14] {
[
(Self::Frontier, ForkCondition::Block(0)),
(Self::Homestead, ForkCondition::Block(0)),
(Self::Dao, ForkCondition::Block(0)),
(Self::Tangerine, ForkCondition::Block(0)),
(Self::SpuriousDragon, ForkCondition::Block(0)),
(Self::Byzantium, ForkCondition::Block(0)),
(Self::Constantinople, ForkCondition::Block(0)),
(Self::Petersburg, ForkCondition::Block(0)),
(Self::Istanbul, ForkCondition::Block(1561651)),
(Self::Berlin, ForkCondition::Block(4460644)),
(Self::London, ForkCondition::Block(5062605)),
(
Self::Paris,
ForkCondition::TTD { fork_block: None, total_difficulty: uint!(10_790_000_U256) },
),
(Self::Shanghai, ForkCondition::Timestamp(1678832736)),
(Self::Cancun, ForkCondition::Timestamp(1705473120)),
]
}
/// Ethereum sepolia list of hardforks.
pub const fn sepolia() -> [(Self, ForkCondition); 15] {
[
(Self::Frontier, ForkCondition::Block(0)),
(Self::Homestead, ForkCondition::Block(0)),
(Self::Dao, ForkCondition::Block(0)),
(Self::Tangerine, ForkCondition::Block(0)),
(Self::SpuriousDragon, ForkCondition::Block(0)),
(Self::Byzantium, ForkCondition::Block(0)),
(Self::Constantinople, ForkCondition::Block(0)),
(Self::Petersburg, ForkCondition::Block(0)),
(Self::Istanbul, ForkCondition::Block(0)),
(Self::MuirGlacier, ForkCondition::Block(0)),
(Self::Berlin, ForkCondition::Block(0)),
(Self::London, ForkCondition::Block(0)),
(
Self::Paris,
ForkCondition::TTD {
fork_block: Some(1735371),
total_difficulty: uint!(17_000_000_000_000_000_U256),
},
),
(Self::Shanghai, ForkCondition::Timestamp(1677557088)),
(Self::Cancun, ForkCondition::Timestamp(1706655072)),
]
}
/// Ethereum holesky list of hardforks.
pub const fn holesky() -> [(Self, ForkCondition); 15] {
[
(Self::Frontier, ForkCondition::Block(0)),
(Self::Homestead, ForkCondition::Block(0)),
(Self::Dao, ForkCondition::Block(0)),
(Self::Tangerine, ForkCondition::Block(0)),
(Self::SpuriousDragon, ForkCondition::Block(0)),
(Self::Byzantium, ForkCondition::Block(0)),
(Self::Constantinople, ForkCondition::Block(0)),
(Self::Petersburg, ForkCondition::Block(0)),
(Self::Istanbul, ForkCondition::Block(0)),
(Self::MuirGlacier, ForkCondition::Block(0)),
(Self::Berlin, ForkCondition::Block(0)),
(Self::London, ForkCondition::Block(0)),
(Self::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Self::Shanghai, ForkCondition::Timestamp(1696000704)),
(Self::Cancun, ForkCondition::Timestamp(1707305664)),
]
}
}
impl<const N: usize> From<[(EthereumHardfork, ForkCondition); N]> for ChainHardforks {
fn from(list: [(EthereumHardfork, ForkCondition); N]) -> Self {
Self::new(
list.into_iter()
.map(|(fork, cond)| (Box::new(fork) as Box<dyn Hardfork>, cond))
.collect(),
)
}
}

View File

@ -0,0 +1,52 @@
/// Macro that defines different variants of a chain specific enum. See [`crate::Hardfork`] as an
/// example.
#[macro_export]
macro_rules! hardfork {
($(#[$enum_meta:meta])* $enum:ident { $( $(#[$meta:meta])* $variant:ident ),* $(,)? }) => {
$(#[$enum_meta])*
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum $enum {
$( $(#[$meta])* $variant ),*
}
impl $enum {
/// Returns variant as `str`.
pub const fn name(&self) -> &'static str {
match self {
$( $enum::$variant => stringify!($variant), )*
}
}
/// Boxes `self` and returns it as `Box<dyn Hardfork>`.
pub fn boxed(self) -> Box<dyn Hardfork> {
Box::new(self)
}
}
impl FromStr for $enum {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
$(
s if s == stringify!($variant).to_lowercase() => Ok($enum::$variant),
)*
_ => return Err(format!("Unknown hardfork: {s}")),
}
}
}
impl Hardfork for $enum {
fn name(&self) -> &'static str {
self.name()
}
}
impl Display for $enum {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
}
}

View File

@ -0,0 +1,126 @@
mod macros;
mod ethereum;
pub use ethereum::EthereumHardfork;
mod optimism;
pub use optimism::OptimismHardfork;
mod dev;
pub use dev::DEV_HARDFORKS;
use core::{
any::Any,
hash::{Hash, Hasher},
};
use dyn_clone::DynClone;
#[cfg(not(feature = "std"))]
use alloc::{format, string::String};
/// Generic hardfork trait.
#[auto_impl::auto_impl(&, Box)]
pub trait Hardfork: Any + DynClone + Send + Sync + 'static {
/// Fork name.
fn name(&self) -> &'static str;
}
dyn_clone::clone_trait_object!(Hardfork);
impl core::fmt::Debug for dyn Hardfork + 'static {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(self.name()).finish()
}
}
impl PartialEq for dyn Hardfork + 'static {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name()
}
}
impl Eq for dyn Hardfork + 'static {}
impl Hash for dyn Hardfork + 'static {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name().hash(state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hardfork::optimism::OptimismHardfork;
use std::str::FromStr;
#[test]
fn check_hardfork_from_str() {
let hardfork_str = [
"frOntier",
"homEstead",
"dao",
"tAngerIne",
"spurIousdrAgon",
"byzAntium",
"constantinople",
"petersburg",
"istanbul",
"muirglacier",
"bErlin",
"lonDon",
"arrowglacier",
"grayglacier",
"PARIS",
"ShAnGhAI",
"CaNcUn",
"PrAguE",
];
let expected_hardforks = [
EthereumHardfork::Frontier,
EthereumHardfork::Homestead,
EthereumHardfork::Dao,
EthereumHardfork::Tangerine,
EthereumHardfork::SpuriousDragon,
EthereumHardfork::Byzantium,
EthereumHardfork::Constantinople,
EthereumHardfork::Petersburg,
EthereumHardfork::Istanbul,
EthereumHardfork::MuirGlacier,
EthereumHardfork::Berlin,
EthereumHardfork::London,
EthereumHardfork::ArrowGlacier,
EthereumHardfork::GrayGlacier,
EthereumHardfork::Paris,
EthereumHardfork::Shanghai,
EthereumHardfork::Cancun,
EthereumHardfork::Prague,
];
let hardforks: Vec<EthereumHardfork> =
hardfork_str.iter().map(|h| EthereumHardfork::from_str(h).unwrap()).collect();
assert_eq!(hardforks, expected_hardforks);
}
#[test]
fn check_op_hardfork_from_str() {
let hardfork_str = ["beDrOck", "rEgOlITH", "cAnYoN", "eCoToNe", "FJorD"];
let expected_hardforks = [
OptimismHardfork::Bedrock,
OptimismHardfork::Regolith,
OptimismHardfork::Canyon,
OptimismHardfork::Ecotone,
OptimismHardfork::Fjord,
];
let hardforks: Vec<OptimismHardfork> =
hardfork_str.iter().map(|h| OptimismHardfork::from_str(h).unwrap()).collect();
assert_eq!(hardforks, expected_hardforks);
}
#[test]
fn check_nonexistent_hardfork_from_str() {
assert!(EthereumHardfork::from_str("not a hardfork").is_err());
}
}

View File

@ -0,0 +1,337 @@
use crate::{hardfork, ChainHardforks, EthereumHardfork, ForkCondition, Hardfork};
use alloy_chains::Chain;
use alloy_primitives::U256;
use core::{
any::Any,
fmt::{self, Display, Formatter},
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
hardfork!(
/// The name of an optimism hardfork.
///
/// When building a list of hardforks for a chain, it's still expected to mix with [`EthereumHardfork`].
OptimismHardfork {
/// Bedrock: <https://blog.oplabs.co/introducing-optimism-bedrock>.
Bedrock,
/// Regolith: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#regolith>.
Regolith,
/// <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#canyon>.
Canyon,
/// Ecotone: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#ecotone>.
Ecotone,
/// Fjord: <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/superchain-upgrades.md#fjord>
Fjord,
}
);
impl OptimismHardfork {
/// Retrieves the activation block for the specified hardfork on the given chain.
pub fn activation_block<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> {
if chain == Chain::base_sepolia() {
return Self::base_sepolia_activation_block(fork)
}
if chain == Chain::base_mainnet() {
return Self::base_mainnet_activation_block(fork)
}
None
}
/// Retrieves the activation timestamp for the specified hardfork on the given chain.
pub fn activation_timestamp<H: Hardfork>(self, fork: H, chain: Chain) -> Option<u64> {
if chain == Chain::base_sepolia() {
return Self::base_sepolia_activation_timestamp(fork)
}
if chain == Chain::base_mainnet() {
return Self::base_mainnet_activation_timestamp(fork)
}
None
}
/// Retrieves the activation block for the specified hardfork on the Base Sepolia testnet.
pub fn base_sepolia_activation_block<H: Hardfork>(fork: H) -> Option<u64> {
match_hardfork(
fork,
|fork| match fork {
EthereumHardfork::Frontier |
EthereumHardfork::Homestead |
EthereumHardfork::Dao |
EthereumHardfork::Tangerine |
EthereumHardfork::SpuriousDragon |
EthereumHardfork::Byzantium |
EthereumHardfork::Constantinople |
EthereumHardfork::Petersburg |
EthereumHardfork::Istanbul |
EthereumHardfork::MuirGlacier |
EthereumHardfork::Berlin |
EthereumHardfork::London |
EthereumHardfork::ArrowGlacier |
EthereumHardfork::GrayGlacier |
EthereumHardfork::Paris |
EthereumHardfork::Shanghai => Some(2106456),
EthereumHardfork::Cancun => Some(6383256),
_ => None,
},
|fork| match fork {
Self::Bedrock | Self::Regolith => Some(0),
Self::Canyon => Some(2106456),
Self::Ecotone => Some(6383256),
Self::Fjord => Some(10615056),
},
)
}
/// Retrieves the activation block for the specified hardfork on the Base mainnet.
pub fn base_mainnet_activation_block<H: Hardfork>(fork: H) -> Option<u64> {
match_hardfork(
fork,
|fork| match fork {
EthereumHardfork::Frontier |
EthereumHardfork::Homestead |
EthereumHardfork::Dao |
EthereumHardfork::Tangerine |
EthereumHardfork::SpuriousDragon |
EthereumHardfork::Byzantium |
EthereumHardfork::Constantinople |
EthereumHardfork::Petersburg |
EthereumHardfork::Istanbul |
EthereumHardfork::MuirGlacier |
EthereumHardfork::Berlin |
EthereumHardfork::London |
EthereumHardfork::ArrowGlacier |
EthereumHardfork::GrayGlacier |
EthereumHardfork::Paris |
EthereumHardfork::Shanghai => Some(9101527),
EthereumHardfork::Cancun => Some(11188936),
_ => None,
},
|fork| match fork {
Self::Bedrock | Self::Regolith => Some(0),
Self::Canyon => Some(9101527),
Self::Ecotone => Some(11188936),
_ => None,
},
)
}
/// Retrieves the activation timestamp for the specified hardfork on the Base Sepolia testnet.
pub fn base_sepolia_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> {
match_hardfork(
fork,
|fork| match fork {
EthereumHardfork::Frontier |
EthereumHardfork::Homestead |
EthereumHardfork::Dao |
EthereumHardfork::Tangerine |
EthereumHardfork::SpuriousDragon |
EthereumHardfork::Byzantium |
EthereumHardfork::Constantinople |
EthereumHardfork::Petersburg |
EthereumHardfork::Istanbul |
EthereumHardfork::MuirGlacier |
EthereumHardfork::Berlin |
EthereumHardfork::London |
EthereumHardfork::ArrowGlacier |
EthereumHardfork::GrayGlacier |
EthereumHardfork::Paris |
EthereumHardfork::Shanghai => Some(1699981200),
EthereumHardfork::Cancun => Some(1708534800),
_ => None,
},
|fork| match fork {
Self::Bedrock | Self::Regolith => Some(1695768288),
Self::Canyon => Some(1699981200),
Self::Ecotone => Some(1708534800),
Self::Fjord => Some(1716998400),
},
)
}
/// Retrieves the activation timestamp for the specified hardfork on the Base mainnet.
pub fn base_mainnet_activation_timestamp<H: Hardfork>(fork: H) -> Option<u64> {
match_hardfork(
fork,
|fork| match fork {
EthereumHardfork::Frontier |
EthereumHardfork::Homestead |
EthereumHardfork::Dao |
EthereumHardfork::Tangerine |
EthereumHardfork::SpuriousDragon |
EthereumHardfork::Byzantium |
EthereumHardfork::Constantinople |
EthereumHardfork::Petersburg |
EthereumHardfork::Istanbul |
EthereumHardfork::MuirGlacier |
EthereumHardfork::Berlin |
EthereumHardfork::London |
EthereumHardfork::ArrowGlacier |
EthereumHardfork::GrayGlacier |
EthereumHardfork::Paris |
EthereumHardfork::Shanghai => Some(1704992401),
EthereumHardfork::Cancun => Some(1710374401),
_ => None,
},
|fork| match fork {
Self::Bedrock | Self::Regolith => Some(1686789347),
Self::Canyon => Some(1704992401),
Self::Ecotone => Some(1710374401),
Self::Fjord => Some(1720627201),
},
)
}
/// Optimism mainnet list of hardforks.
pub fn op_mainnet() -> ChainHardforks {
ChainHardforks::new(vec![
(EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Berlin.boxed(), ForkCondition::Block(3950000)),
(EthereumHardfork::London.boxed(), ForkCondition::Block(105235063)),
(EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Block(105235063)),
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(105235063)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(105235063), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(105235063)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(1704992401)),
(Self::Canyon.boxed(), ForkCondition::Timestamp(1704992401)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(1710374401)),
(Self::Ecotone.boxed(), ForkCondition::Timestamp(1710374401)),
(Self::Fjord.boxed(), ForkCondition::Timestamp(1720627201)),
])
}
/// Optimism sepolia list of hardforks.
pub fn op_sepolia() -> ChainHardforks {
ChainHardforks::new(vec![
(EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(1699981200)),
(Self::Canyon.boxed(), ForkCondition::Timestamp(1699981200)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(1708534800)),
(Self::Ecotone.boxed(), ForkCondition::Timestamp(1708534800)),
(Self::Fjord.boxed(), ForkCondition::Timestamp(1716998400)),
])
}
/// Base sepolia list of hardforks.
pub fn base_sepolia() -> ChainHardforks {
ChainHardforks::new(vec![
(EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(1699981200)),
(Self::Canyon.boxed(), ForkCondition::Timestamp(1699981200)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(1708534800)),
(Self::Ecotone.boxed(), ForkCondition::Timestamp(1708534800)),
(Self::Fjord.boxed(), ForkCondition::Timestamp(1716998400)),
])
}
/// Base mainnet list of hardforks.
pub fn base_mainnet() -> ChainHardforks {
ChainHardforks::new(vec![
(EthereumHardfork::Frontier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Block(0)),
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(1704992401)),
(Self::Canyon.boxed(), ForkCondition::Timestamp(1704992401)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(1710374401)),
(Self::Ecotone.boxed(), ForkCondition::Timestamp(1710374401)),
(Self::Fjord.boxed(), ForkCondition::Timestamp(1720627201)),
])
}
}
/// Match helper method since it's not possible to match on `dyn Hardfork`
fn match_hardfork<H, HF, OHF>(fork: H, hardfork_fn: HF, optimism_hardfork_fn: OHF) -> Option<u64>
where
H: Hardfork,
HF: Fn(&EthereumHardfork) -> Option<u64>,
OHF: Fn(&OptimismHardfork) -> Option<u64>,
{
let fork: &dyn Any = &fork;
if let Some(fork) = fork.downcast_ref::<EthereumHardfork>() {
return hardfork_fn(fork)
}
fork.downcast_ref::<OptimismHardfork>().and_then(optimism_hardfork_fn)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_hardfork() {
assert_eq!(
OptimismHardfork::base_mainnet_activation_block(EthereumHardfork::Cancun),
Some(11188936)
);
assert_eq!(
OptimismHardfork::base_mainnet_activation_block(OptimismHardfork::Canyon),
Some(9101527)
);
}
}

View File

@ -0,0 +1,56 @@
use crate::{
hardforks::{ChainHardforks, Hardforks},
EthereumHardfork, ForkCondition,
};
/// Helper methods for Ethereum forks.
pub trait EthereumHardforks: Hardforks {
/// Convenience method to check if [`EthereumHardfork::Shanghai`] is active at a given
/// timestamp.
fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Cancun`] is active at a given timestamp.
fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Prague`] is active at a given timestamp.
fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Byzantium`] is active at a given block
/// number.
fn is_byzantium_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::Byzantium).active_at_block(block_number)
}
/// Convenience method to check if [`EthereumHardfork::SpuriousDragon`] is active at a given
/// block number.
fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::SpuriousDragon).active_at_block(block_number)
}
/// Convenience method to check if [`EthereumHardfork::Homestead`] is active at a given block
/// number.
fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::Homestead).active_at_block(block_number)
}
/// The Paris hardfork (merge) is activated via block number. If we have knowledge of the block,
/// this function will return true if the block number is greater than or equal to the Paris
/// (merge) block.
fn is_paris_active_at_block(&self, block_number: u64) -> Option<bool> {
match self.fork(EthereumHardfork::Paris) {
ForkCondition::Block(paris_block) => Some(block_number >= paris_block),
ForkCondition::TTD { fork_block, .. } => {
fork_block.map(|paris_block| block_number >= paris_block)
}
_ => None,
}
}
}
impl EthereumHardforks for ChainHardforks {}

View File

@ -0,0 +1,131 @@
/// Ethereum helper methods
mod ethereum;
pub use ethereum::EthereumHardforks;
/// Optimism helper methods
mod optimism;
pub use optimism::OptimismHardforks;
use crate::{ForkCondition, Hardfork};
use rustc_hash::FxHashMap;
/// Generic trait over a set of ordered hardforks
pub trait Hardforks: Default + Clone {
/// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns
/// [`ForkCondition::Never`].
fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition;
/// Get an iterator of all hardforks with their respective activation conditions.
fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)>;
/// Convenience method to check if a fork is active at a given timestamp.
fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
self.fork(fork).active_at_timestamp(timestamp)
}
/// Convenience method to check if a fork is active at a given block number.
fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
self.fork(fork).active_at_block(block_number)
}
}
/// Ordered list of a chain hardforks that implement [`Hardfork`].
#[derive(Default, Clone, PartialEq, Eq)]
pub struct ChainHardforks {
forks: Vec<(Box<dyn Hardfork>, ForkCondition)>,
map: FxHashMap<&'static str, ForkCondition>,
}
impl ChainHardforks {
/// Creates a new [`ChainHardforks`] from a list which **must be ordered** by activation.
///
/// Equivalent Ethereum hardforks **must be included** as well.
pub fn new(forks: Vec<(Box<dyn Hardfork>, ForkCondition)>) -> Self {
let map = forks.iter().map(|(fork, condition)| (fork.name(), *condition)).collect();
Self { forks, map }
}
/// Total number of hardforks.
pub fn len(&self) -> usize {
self.forks.len()
}
/// Checks if the fork list is empty.
pub fn is_empty(&self) -> bool {
self.forks.is_empty()
}
/// Retrieves [`ForkCondition`] from `fork`. If `fork` is not present, returns
/// [`ForkCondition::Never`].
pub fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
self.get(fork).unwrap_or(ForkCondition::Never)
}
/// Retrieves [`ForkCondition`] from `fork` if it exists, otherwise `None`.
pub fn get<H: Hardfork>(&self, fork: H) -> Option<ForkCondition> {
self.map.get(fork.name()).copied()
}
/// Get an iterator of all hardforks with their respective activation conditions.
pub fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
self.forks.iter().map(|(f, b)| (&**f, *b))
}
/// Get last hardfork from the list.
pub fn last(&self) -> Option<(Box<dyn Hardfork>, ForkCondition)> {
self.forks.last().map(|(f, b)| (f.clone(), *b))
}
/// Convenience method to check if a fork is active at a given timestamp.
pub fn is_fork_active_at_timestamp<H: Hardfork>(&self, fork: H, timestamp: u64) -> bool {
self.fork(fork).active_at_timestamp(timestamp)
}
/// Convenience method to check if a fork is active at a given block number.
pub fn is_fork_active_at_block<H: Hardfork>(&self, fork: H, block_number: u64) -> bool {
self.fork(fork).active_at_block(block_number)
}
/// Inserts `fork` into list, updating with a new [`ForkCondition`] if it already exists.
pub fn insert<H: Hardfork>(&mut self, fork: H, condition: ForkCondition) {
match self.map.entry(fork.name()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
*entry.get_mut() = condition;
if let Some((_, inner)) =
self.forks.iter_mut().find(|(inner, _)| inner.name() == fork.name())
{
*inner = condition;
}
}
std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(condition);
self.forks.push((Box::new(fork), condition));
}
}
}
/// Removes `fork` from list.
pub fn remove<H: Hardfork>(&mut self, fork: H) {
self.forks.retain(|(inner_fork, _)| inner_fork.name() != fork.name());
self.map.remove(fork.name());
}
}
impl Hardforks for ChainHardforks {
fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
self.fork(fork)
}
fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> {
self.forks_iter()
}
}
impl core::fmt::Debug for ChainHardforks {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ChainHardforks")
.field("0", &self.forks_iter().map(|(hf, cond)| (hf.name(), cond)).collect::<Vec<_>>())
.finish()
}
}

View File

@ -0,0 +1,12 @@
use crate::{ChainHardforks, EthereumHardforks, OptimismHardfork};
/// Extends [`crate::EthereumHardforks`] with optimism helper methods.
pub trait OptimismHardforks: EthereumHardforks {
/// Convenience method to check if [`OptimismHardfork::Bedrock`] is active at a given block
/// number.
fn is_bedrock_active_at_block(&self, block_number: u64) -> bool {
self.fork(OptimismHardfork::Bedrock).active_at_block(block_number)
}
}
impl OptimismHardforks for ChainHardforks {}

View File

@ -22,19 +22,18 @@ mod display;
mod forkcondition;
mod forkid;
mod hardfork;
mod hardforks;
mod head;
pub use forkid::{
EnrForkIdEntry, ForkFilter, ForkFilterKey, ForkHash, ForkId, ForkTransition, ValidationError,
};
pub use hardfork::Hardfork;
pub use hardfork::{EthereumHardfork, Hardfork, OptimismHardfork, DEV_HARDFORKS};
pub use head::Head;
pub use display::DisplayHardforks;
pub use forkcondition::ForkCondition;
/// Chains hardforks
pub mod chains;
pub use hardforks::*;
#[cfg(any(test, feature = "arbitrary"))]
pub use arbitrary;