1use crate::{eip2935, eip4788, eip6110, eip7002, eip7251, eip7840::BlobParams, eip8282};
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>,
81}
82
83#[derive(PartialEq, Eq, Clone, Debug)]
85#[cfg_attr(feature = "serde", derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr))]
86pub enum SystemContract {
87 BeaconRoots,
89 BuilderDepositContract,
91 BuilderExitContract,
93 ConsolidationRequestPredeploy,
95 DepositContract,
97 HistoryStorage,
99 WithdrawalRequestPredeploy,
101 Other(String),
103}
104
105impl PartialOrd for SystemContract {
106 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
107 Some(self.cmp(other))
108 }
109}
110
111impl Ord for SystemContract {
112 fn cmp(&self, other: &Self) -> Ordering {
113 self.to_string().cmp(&other.to_string())
114 }
115}
116
117impl fmt::Display for SystemContract {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let str = match self {
120 Self::BeaconRoots => "BEACON_ROOTS",
121 Self::BuilderDepositContract => "BUILDER_DEPOSIT_CONTRACT",
122 Self::BuilderExitContract => "BUILDER_EXIT_CONTRACT",
123 Self::ConsolidationRequestPredeploy => "CONSOLIDATION_REQUEST_PREDEPLOY",
124 Self::DepositContract => "DEPOSIT_CONTRACT",
125 Self::HistoryStorage => "HISTORY_STORAGE",
126 Self::WithdrawalRequestPredeploy => "WITHDRAWAL_REQUEST_PREDEPLOY",
127 Self::Other(name) => return write!(f, "{name}"),
128 };
129 write!(f, "{str}_ADDRESS")
130 }
131}
132
133impl str::FromStr for SystemContract {
134 type Err = Infallible;
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 let system_contract = match s {
138 "BEACON_ROOTS_ADDRESS" => Self::BeaconRoots,
139 "BUILDER_DEPOSIT_CONTRACT_ADDRESS" => Self::BuilderDepositContract,
140 "BUILDER_EXIT_CONTRACT_ADDRESS" => Self::BuilderExitContract,
141 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS" => Self::ConsolidationRequestPredeploy,
142 "DEPOSIT_CONTRACT_ADDRESS" => Self::DepositContract,
143 "HISTORY_STORAGE_ADDRESS" => Self::HistoryStorage,
144 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS" => Self::WithdrawalRequestPredeploy,
145 _ => Self::Other(s.into()),
146 };
147 Ok(system_contract)
148 }
149}
150
151impl SystemContract {
152 pub const ALL: [Self; 7] = [
154 Self::BeaconRoots,
155 Self::BuilderDepositContract,
156 Self::BuilderExitContract,
157 Self::ConsolidationRequestPredeploy,
158 Self::DepositContract,
159 Self::HistoryStorage,
160 Self::WithdrawalRequestPredeploy,
161 ];
162
163 pub const fn cancun() -> [(Self, Address); 1] {
165 [(Self::BeaconRoots, eip4788::BEACON_ROOTS_ADDRESS)]
166 }
167
168 pub fn prague(deposit_contract: Option<Address>) -> [(Self, Address); 4] {
172 [
173 (Self::HistoryStorage, eip2935::HISTORY_STORAGE_ADDRESS),
174 (Self::ConsolidationRequestPredeploy, eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS),
175 (Self::WithdrawalRequestPredeploy, eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS),
176 (
177 Self::DepositContract,
178 deposit_contract.unwrap_or(eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS),
179 ),
180 ]
181 }
182
183 pub const fn amsterdam() -> [(Self, Address); 2] {
186 [
187 (Self::BuilderDepositContract, eip8282::BUILDER_DEPOSIT_CONTRACT_ADDRESS),
188 (Self::BuilderExitContract, eip8282::BUILDER_EXIT_CONTRACT_ADDRESS),
189 ]
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn system_contract_str() {
199 assert_eq!(SystemContract::BeaconRoots.to_string(), "BEACON_ROOTS_ADDRESS");
200 assert_eq!(
201 SystemContract::BuilderDepositContract.to_string(),
202 "BUILDER_DEPOSIT_CONTRACT_ADDRESS"
203 );
204 assert_eq!(
205 SystemContract::BuilderExitContract.to_string(),
206 "BUILDER_EXIT_CONTRACT_ADDRESS"
207 );
208 assert_eq!(
209 SystemContract::ConsolidationRequestPredeploy.to_string(),
210 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS"
211 );
212 assert_eq!(SystemContract::DepositContract.to_string(), "DEPOSIT_CONTRACT_ADDRESS");
213 assert_eq!(SystemContract::HistoryStorage.to_string(), "HISTORY_STORAGE_ADDRESS");
214 assert_eq!(
215 SystemContract::WithdrawalRequestPredeploy.to_string(),
216 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS"
217 );
218 }
219
220 #[cfg(feature = "serde")]
221 #[test]
222 fn system_contract_serde_roundtrip() {
223 for contract in &SystemContract::ALL {
224 assert_eq!(
225 *contract,
226 serde_json::from_value::<SystemContract>(serde_json::to_value(contract).unwrap())
227 .unwrap()
228 );
229 }
230 }
231
232 #[cfg(feature = "serde")]
233 #[test]
234 fn hoodie_prague_eth_config() {
235 let raw = r#"
236 {
237 "activationTime": 1742999832,
238 "blobSchedule": {
239 "baseFeeUpdateFraction": 5007716,
240 "max": 9,
241 "target": 6
242 },
243 "chainId": "0x88bb0",
244 "forkId": "0x0929e24e",
245 "precompiles": {
246 "BLAKE2F": "0x0000000000000000000000000000000000000009",
247 "BLS12_G1ADD": "0x000000000000000000000000000000000000000b",
248 "BLS12_G1MSM": "0x000000000000000000000000000000000000000c",
249 "BLS12_G2ADD": "0x000000000000000000000000000000000000000d",
250 "BLS12_G2MSM": "0x000000000000000000000000000000000000000e",
251 "BLS12_MAP_FP2_TO_G2": "0x0000000000000000000000000000000000000011",
252 "BLS12_MAP_FP_TO_G1": "0x0000000000000000000000000000000000000010",
253 "BLS12_PAIRING_CHECK": "0x000000000000000000000000000000000000000f",
254 "BN254_ADD": "0x0000000000000000000000000000000000000006",
255 "BN254_MUL": "0x0000000000000000000000000000000000000007",
256 "BN254_PAIRING": "0x0000000000000000000000000000000000000008",
257 "ECREC": "0x0000000000000000000000000000000000000001",
258 "ID": "0x0000000000000000000000000000000000000004",
259 "KZG_POINT_EVALUATION": "0x000000000000000000000000000000000000000a",
260 "MODEXP": "0x0000000000000000000000000000000000000005",
261 "RIPEMD160": "0x0000000000000000000000000000000000000003",
262 "SHA256": "0x0000000000000000000000000000000000000002"
263 },
264 "systemContracts": {
265 "BEACON_ROOTS_ADDRESS": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
266 "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS": "0x0000bbddc7ce488642fb579f8b00f3a590007251",
267 "DEPOSIT_CONTRACT_ADDRESS": "0x00000000219ab540356cbb839cbe05303d7705fa",
268 "HISTORY_STORAGE_ADDRESS": "0x0000f90827f1c53a10cb7a02335b175320002935",
269 "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS": "0x00000961ef480eb55e80d19ad83579a64c007002"
270 }
271 }
272 "#;
273
274 let fork_config = serde_json::from_str::<EthForkConfig>(raw).unwrap();
275 assert_eq!(
276 serde_json::to_string(&fork_config).unwrap(),
277 raw.chars().filter(|c| !c.is_whitespace()).collect::<String>()
278 );
279 }
280}