1use crate::{eip2935, eip4788, eip6110, eip7002, eip7251, eip7840::BlobParams};
4use alloc::{
5 collections::BTreeMap,
6 string::{String, ToString},
7};
8use alloy_primitives::{Address, Bytes};
9use core::{cmp::Ordering, convert::Infallible, fmt, str};
10
11#[derive(Clone, Debug, PartialEq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
15pub struct EthConfig {
16 pub current: EthForkConfig,
18 pub next: Option<EthForkConfig>,
20 pub last: Option<EthForkConfig>,
22}
23
24#[derive(Clone, Debug, PartialEq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
28pub struct EthForkConfig {
29 pub activation_time: u64,
35 pub blob_schedule: BlobParams,
39 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
45 pub chain_id: u64,
46 pub fork_id: Bytes,
50 pub precompiles: BTreeMap<String, Address>,
64 pub system_contracts: BTreeMap<SystemContract, Address>,
78}
79
80#[derive(PartialEq, Eq, Clone, Debug)]
82#[cfg_attr(feature = "serde", derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]
83pub enum SystemContract {
84 BeaconRoots,
86 ConsolidationRequestPredeploy,
88 DepositContract,
90 HistoryStorage,
92 WithdrawalRequestPredeploy,
94 Other(String),
96}
97
98impl PartialOrd for SystemContract {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(self.cmp(other))
101 }
102}
103
104impl Ord for SystemContract {
105 fn cmp(&self, other: &Self) -> Ordering {
106 self.to_string().cmp(&other.to_string())
107 }
108}
109
110impl fmt::Display for SystemContract {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 let str = match self {
113 Self::BeaconRoots => "BEACON_ROOTS",
114 Self::ConsolidationRequestPredeploy => "CONSOLIDATION_REQUEST_PREDEPLOY",
115 Self::DepositContract => "DEPOSIT_CONTRACT",
116 Self::HistoryStorage => "HISTORY_STORAGE",
117 Self::WithdrawalRequestPredeploy => "WITHDRAWAL_REQUEST_PREDEPLOY",
118 Self::Other(name) => return write!(f, "{name}"),
119 };
120 write!(f, "{str}_ADDRESS")
121 }
122}
123
124impl str::FromStr for SystemContract {
125 type Err = Infallible;
126
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 let system_contract = match s {
129 "BEACON_ROOTS_ADDRESS" => Self::BeaconRoots,
130 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS" => Self::ConsolidationRequestPredeploy,
131 "DEPOSIT_CONTRACT_ADDRESS" => Self::DepositContract,
132 "HISTORY_STORAGE_ADDRESS" => Self::HistoryStorage,
133 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS" => Self::WithdrawalRequestPredeploy,
134 _ => Self::Other(s.into()),
135 };
136 Ok(system_contract)
137 }
138}
139
140impl SystemContract {
141 pub const ALL: [Self; 5] = [
143 Self::BeaconRoots,
144 Self::ConsolidationRequestPredeploy,
145 Self::DepositContract,
146 Self::HistoryStorage,
147 Self::WithdrawalRequestPredeploy,
148 ];
149
150 pub const fn cancun() -> [(Self, Address); 1] {
152 [(Self::BeaconRoots, eip4788::BEACON_ROOTS_ADDRESS)]
153 }
154
155 pub fn prague(deposit_contract: Option<Address>) -> [(Self, Address); 4] {
159 [
160 (Self::HistoryStorage, eip2935::HISTORY_STORAGE_ADDRESS),
161 (Self::ConsolidationRequestPredeploy, eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS),
162 (Self::WithdrawalRequestPredeploy, eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
163 (
164 Self::DepositContract,
165 deposit_contract.unwrap_or(eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS),
166 ),
167 ]
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn system_contract_str() {
177 assert_eq!(SystemContract::BeaconRoots.to_string(), "BEACON_ROOTS_ADDRESS");
178 assert_eq!(
179 SystemContract::ConsolidationRequestPredeploy.to_string(),
180 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS"
181 );
182 assert_eq!(SystemContract::DepositContract.to_string(), "DEPOSIT_CONTRACT_ADDRESS");
183 assert_eq!(SystemContract::HistoryStorage.to_string(), "HISTORY_STORAGE_ADDRESS");
184 assert_eq!(
185 SystemContract::WithdrawalRequestPredeploy.to_string(),
186 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS"
187 );
188 }
189
190 #[cfg(feature = "serde")]
191 #[test]
192 fn system_contract_serde_roundtrip() {
193 for contract in &SystemContract::ALL {
194 assert_eq!(
195 *contract,
196 serde_json::from_value::<SystemContract>(serde_json::to_value(contract).unwrap())
197 .unwrap()
198 );
199 }
200 }
201
202 #[cfg(feature = "serde")]
203 #[test]
204 fn hoodie_prague_eth_config() {
205 let raw = r#"
206 {
207 "activationTime": 1742999832,
208 "blobSchedule": {
209 "baseFeeUpdateFraction": 5007716,
210 "max": 9,
211 "target": 6
212 },
213 "chainId": "0x88bb0",
214 "forkId": "0x0929e24e",
215 "precompiles": {
216 "BLAKE2F": "0x0000000000000000000000000000000000000009",
217 "BLS12_G1ADD": "0x000000000000000000000000000000000000000b",
218 "BLS12_G1MSM": "0x000000000000000000000000000000000000000c",
219 "BLS12_G2ADD": "0x000000000000000000000000000000000000000d",
220 "BLS12_G2MSM": "0x000000000000000000000000000000000000000e",
221 "BLS12_MAP_FP2_TO_G2": "0x0000000000000000000000000000000000000011",
222 "BLS12_MAP_FP_TO_G1": "0x0000000000000000000000000000000000000010",
223 "BLS12_PAIRING_CHECK": "0x000000000000000000000000000000000000000f",
224 "BN254_ADD": "0x0000000000000000000000000000000000000006",
225 "BN254_MUL": "0x0000000000000000000000000000000000000007",
226 "BN254_PAIRING": "0x0000000000000000000000000000000000000008",
227 "ECREC": "0x0000000000000000000000000000000000000001",
228 "ID": "0x0000000000000000000000000000000000000004",
229 "KZG_POINT_EVALUATION": "0x000000000000000000000000000000000000000a",
230 "MODEXP": "0x0000000000000000000000000000000000000005",
231 "RIPEMD160": "0x0000000000000000000000000000000000000003",
232 "SHA256": "0x0000000000000000000000000000000000000002"
233 },
234 "systemContracts": {
235 "BEACON_ROOTS_ADDRESS": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
236 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS": "0x0000bbddc7ce488642fb579f8b00f3a590007251",
237 "DEPOSIT_CONTRACT_ADDRESS": "0x00000000219ab540356cbb839cbe05303d7705fa",
238 "HISTORY_STORAGE_ADDRESS": "0x0000f90827f1c53a10cb7a02335b175320002935",
239 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS": "0x00000961ef480eb55e80d19ad83579a64c007002"
240 }
241 }
242 "#;
243
244 let fork_config = serde_json::from_str::<EthForkConfig>(raw).unwrap();
245 assert_eq!(
246 serde_json::to_string(&fork_config).unwrap(),
247 raw.chars().filter(|c| !c.is_whitespace()).collect::<String>()
248 );
249 }
250}