reth_rpc_eth_api/helpers/
config.rs1use alloy_consensus::BlockHeader;
4use alloy_eips::{
5 eip7840::BlobParams,
6 eip7910::{EthConfig, EthForkConfig, SystemContract},
7};
8use alloy_evm::precompiles::Precompile;
9use alloy_primitives::{address, Address};
10use jsonrpsee::{core::RpcResult, proc_macros::rpc};
11use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks, Head};
12use reth_errors::{ProviderError, RethError};
13use reth_evm::{precompiles::PrecompilesMap, ConfigureEvm, Evm};
14use reth_node_api::NodePrimitives;
15use reth_primitives_traits::header::HeaderMut;
16use reth_revm::db::EmptyDB;
17use reth_rpc_eth_types::EthApiError;
18use reth_storage_api::BlockReaderIdExt;
19use std::collections::BTreeMap;
20
21#[cfg_attr(not(feature = "client"), rpc(server, namespace = "eth"))]
23#[cfg_attr(feature = "client", rpc(server, client, namespace = "eth"))]
24pub trait EthConfigApi {
25 #[method(name = "config")]
27 fn config(&self) -> RpcResult<EthConfig>;
28}
29
30#[derive(Debug, Clone)]
34pub struct EthConfigHandler<Provider, Evm> {
35 provider: Provider,
36 evm_config: Evm,
37}
38
39impl<Provider, Evm> EthConfigHandler<Provider, Evm>
40where
41 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
42 + BlockReaderIdExt<Header: HeaderMut>
43 + 'static,
44 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Provider::Header>> + 'static,
45{
46 pub const fn new(provider: Provider, evm_config: Evm) -> Self {
48 Self { provider, evm_config }
49 }
50
51 fn build_fork_config_at(
53 &self,
54 timestamp: u64,
55 precompiles: BTreeMap<String, Address>,
56 ) -> EthForkConfig {
57 let chain_spec = self.provider.chain_spec();
58
59 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
60
61 if chain_spec.is_cancun_active_at_timestamp(timestamp) {
62 system_contracts.extend(SystemContract::cancun());
63 }
64
65 if chain_spec.is_prague_active_at_timestamp(timestamp) {
66 system_contracts
67 .extend(SystemContract::prague(chain_spec.deposit_contract().map(|c| c.address)));
68 }
69
70 if chain_spec.is_amsterdam_active_at_timestamp(timestamp) {
71 system_contracts.extend(amsterdam_system_contracts());
72 }
73
74 let fork_id = chain_spec
76 .fork_id(&Head { timestamp, number: u64::MAX, ..Default::default() })
77 .hash
78 .0
79 .into();
80
81 EthForkConfig {
82 activation_time: timestamp,
83 blob_schedule: chain_spec
84 .blob_params_at_timestamp(timestamp)
85 .unwrap_or_else(BlobParams::cancun),
87 chain_id: chain_spec.chain().id(),
88 fork_id,
89 precompiles,
90 system_contracts,
91 }
92 }
93
94 fn config(&self) -> Result<EthConfig, RethError> {
95 let chain_spec = self.provider.chain_spec();
96 let latest = self
97 .provider
98 .latest_header()?
99 .ok_or_else(|| ProviderError::BestBlockNotFound)?
100 .into_header();
101
102 let current_precompiles = evm_to_precompiles_map(
103 self.evm_config.evm_for_block(EmptyDB::default(), &latest).map_err(RethError::other)?,
104 );
105
106 let mut fork_timestamps =
107 chain_spec.forks_iter().filter_map(|(_, cond)| cond.as_timestamp()).collect::<Vec<_>>();
108 fork_timestamps.sort_unstable();
109 fork_timestamps.dedup();
110
111 let current_fork_idx = match fork_timestamps.iter().position(|ts| &latest.timestamp() < ts)
112 {
113 None => fork_timestamps.len().checked_sub(1),
115 Some(0) => None,
117 Some(idx) => Some(idx - 1),
119 };
120 let (current_fork_idx, current_fork_timestamp) = current_fork_idx
121 .and_then(|idx| fork_timestamps.get(idx).map(|ts| (idx, *ts)))
122 .ok_or_else(|| RethError::msg("no active timestamp fork found"))?;
123
124 let current = self.build_fork_config_at(current_fork_timestamp, current_precompiles);
125
126 let mut config = EthConfig { current, next: None, last: None };
127
128 if let Some(next_fork_timestamp) = fork_timestamps.get(current_fork_idx + 1).copied() {
129 let fake_header = {
130 let mut header = latest.clone();
131 header.set_timestamp(next_fork_timestamp);
132 header
133 };
134 let next_precompiles = evm_to_precompiles_map(
135 self.evm_config
136 .evm_for_block(EmptyDB::default(), &fake_header)
137 .map_err(RethError::other)?,
138 );
139
140 config.next = Some(self.build_fork_config_at(next_fork_timestamp, next_precompiles));
141 } else {
142 return Ok(config);
144 }
145
146 let last_fork_timestamp = fork_timestamps.last().copied().unwrap();
147 let fake_header = {
148 let mut header = latest;
149 header.set_timestamp(last_fork_timestamp);
150 header
151 };
152 let last_precompiles = evm_to_precompiles_map(
153 self.evm_config
154 .evm_for_block(EmptyDB::default(), &fake_header)
155 .map_err(RethError::other)?,
156 );
157
158 config.last = Some(self.build_fork_config_at(last_fork_timestamp, last_precompiles));
159
160 Ok(config)
161 }
162}
163
164impl<Provider, Evm> EthConfigApiServer for EthConfigHandler<Provider, Evm>
165where
166 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
167 + BlockReaderIdExt<Header: HeaderMut>
168 + 'static,
169 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Provider::Header>> + 'static,
170{
171 fn config(&self) -> RpcResult<EthConfig> {
172 Ok(self.config().map_err(EthApiError::from)?)
173 }
174}
175
176const BUILDER_DEPOSIT_CONTRACT_ADDRESS: Address =
179 address!("0x0000BFF46984E3725691FA540A8C7589300D8282");
180
181const BUILDER_EXIT_CONTRACT_ADDRESS: Address =
184 address!("0x000064D678505AD48F8CCB093BC65613800E8282");
185
186fn amsterdam_system_contracts() -> [(SystemContract, Address); 2] {
189 [
190 (
191 SystemContract::Other("BUILDER_DEPOSIT_CONTRACT_ADDRESS".to_string()),
192 BUILDER_DEPOSIT_CONTRACT_ADDRESS,
193 ),
194 (
195 SystemContract::Other("BUILDER_EXIT_CONTRACT_ADDRESS".to_string()),
196 BUILDER_EXIT_CONTRACT_ADDRESS,
197 ),
198 ]
199}
200
201fn evm_to_precompiles_map(
202 evm: impl Evm<Precompiles = PrecompilesMap>,
203) -> BTreeMap<String, Address> {
204 let precompiles = evm.precompiles();
205 precompiles
206 .addresses()
207 .filter_map(|address| {
208 Some((precompiles.get(address)?.precompile_id().name().to_string(), *address))
209 })
210 .collect()
211}