Skip to content

Commit 7ccb5d9

Browse files
committed
use alloy_chains::Chain as config key
1 parent 598f73a commit 7ccb5d9

File tree

4 files changed

+101
-168
lines changed

4 files changed

+101
-168
lines changed

crates/cheatcodes/src/fork.rs

Lines changed: 50 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,35 @@ use crate::{
44
Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*, json::parse_json_as,
55
toml::toml_to_json_value,
66
};
7+
use alloy_chains::Chain;
78
use alloy_dyn_abi::{DynSolType, DynSolValue};
89
use alloy_sol_types::SolValue;
9-
use eyre::OptionExt;
1010
use foundry_evm_core::ContextExt;
11+
use std::borrow::Cow;
1112

1213
impl Cheatcode for readForkChainIdsCall {
1314
fn apply(&self, state: &mut crate::Cheatcodes) -> Result {
1415
let Self {} = self;
15-
Ok(state
16-
.config
17-
.forks
18-
.keys()
19-
.map(|name| alloy_chains::Chain::from_named(name.parse().unwrap()).id())
20-
.collect::<Vec<_>>()
21-
.abi_encode())
16+
Ok(state.config.forks.keys().map(|chain| chain.id()).collect::<Vec<_>>().abi_encode())
2217
}
2318
}
2419

2520
impl Cheatcode for readForkChainsCall {
2621
fn apply(&self, state: &mut crate::Cheatcodes) -> Result {
2722
let Self {} = self;
28-
Ok(state.config.forks.keys().collect::<Vec<_>>().abi_encode())
23+
Ok(state
24+
.config
25+
.forks
26+
.keys()
27+
.map(|chain| get_chain_name(chain).to_string())
28+
.collect::<Vec<_>>()
29+
.abi_encode())
2930
}
3031
}
3132

3233
impl Cheatcode for readForkChainCall {
3334
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
34-
Ok(get_active_fork_chain_name(ccx)?.abi_encode())
35+
Ok(get_chain_name(&get_active_fork_chain(ccx)?).to_string().abi_encode())
3536
}
3637
}
3738

@@ -41,42 +42,35 @@ impl Cheatcode for readForkChainIdCall {
4142
}
4243
}
4344

44-
fn resolve_rpc_url(name: &'static str, state: &mut crate::Cheatcodes) -> Result {
45-
// Get the chain ID from the chain_configs
46-
if let Some(config) = state.config.forks.get(name) {
45+
fn resolve_rpc_url(chain: Chain, state: &mut crate::Cheatcodes) -> Result {
46+
if let Some(config) = state.config.forks.get(&chain) {
4747
let rpc = match config.rpc_endpoint {
4848
Some(ref url) => url.clone().resolve(),
49-
None => state.config.rpc_endpoint(name)?,
49+
None => state.config.rpc_endpoint(&get_chain_name(&chain))?,
5050
};
5151

5252
return Ok(rpc.url()?.abi_encode());
5353
}
5454

55-
bail!("[fork.{name}] subsection not found in [fork] of 'foundry.toml'")
55+
bail!("'rpc_endpoint' not found in [fork.{chain}] subsection of 'foundry.toml'")
5656
}
5757

5858
impl Cheatcode for readForkChainRpcUrlCall {
5959
fn apply(&self, state: &mut crate::Cheatcodes) -> Result {
6060
let Self { id } = self;
61-
let name = get_chain_name(id.to::<u64>())?;
62-
resolve_rpc_url(name, state)
61+
resolve_rpc_url(Chain::from_id(id.to::<u64>()), state)
6362
}
6463
}
6564

6665
impl Cheatcode for readForkRpcUrlCall {
6766
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
68-
let name = get_active_fork_chain_name(ccx)?;
69-
resolve_rpc_url(name, ccx.state)
67+
resolve_rpc_url(get_active_fork_chain(ccx)?, ccx.state)
7068
}
7169
}
7270

7371
/// Gets the alloy chain name for a given chain id.
74-
fn get_chain_name(id: u64) -> Result<&'static str> {
75-
let chain = alloy_chains::Chain::from_id(id)
76-
.named()
77-
.ok_or_eyre("unknown name for active forked chain")?;
78-
79-
Ok(chain.as_str())
72+
fn get_chain_name(chain: &Chain) -> Cow<'static, str> {
73+
chain.named().map_or(Cow::Owned(chain.id().to_string()), |name| Cow::Borrowed(name.as_str()))
8074
}
8175

8276
/// Gets the chain id of the active fork. Panics if no fork is selected.
@@ -88,9 +82,9 @@ fn get_active_fork_chain_id(ccx: &mut CheatsCtxt) -> Result<u64> {
8882
Ok(env.cfg.chain_id)
8983
}
9084

91-
/// Gets the alloy chain name for the active fork. Panics if no fork is selected.
92-
fn get_active_fork_chain_name(ccx: &mut CheatsCtxt) -> Result<&'static str> {
93-
get_chain_name(get_active_fork_chain_id(ccx)?)
85+
/// Gets the alloy chain for the active fork. Panics if no fork is selected.
86+
fn get_active_fork_chain(ccx: &mut CheatsCtxt) -> Result<Chain> {
87+
get_active_fork_chain_id(ccx).map(Chain::from_id)
9488
}
9589

9690
// Helper macros to generate cheatcode implementations
@@ -99,7 +93,7 @@ macro_rules! impl_get_value_cheatcode {
9993
impl Cheatcode for $struct {
10094
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
10195
let Self { key } = self;
102-
let chain = get_active_fork_chain_id(ccx)?;
96+
let chain = get_active_fork_chain(ccx)?;
10397
get_value(chain, key, $sol_type, ccx.state)
10498
}
10599
}
@@ -108,7 +102,7 @@ macro_rules! impl_get_value_cheatcode {
108102
impl Cheatcode for $struct {
109103
fn apply(&self, state: &mut crate::Cheatcodes) -> Result {
110104
let Self { chain, key } = self;
111-
get_value(chain.to::<u64>(), key, $sol_type, state)
105+
get_value(Chain::from_id(chain.to::<u64>()), key, $sol_type, state)
112106
}
113107
}
114108
};
@@ -119,16 +113,15 @@ macro_rules! impl_get_array_cheatcode {
119113
impl Cheatcode for $struct {
120114
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
121115
let Self { key } = self;
122-
let chain = get_active_fork_chain_id(ccx)?;
123-
get_array(chain, key, $sol_type, ccx.state)
116+
get_array(get_active_fork_chain(ccx)?, key, $sol_type, ccx.state)
124117
}
125118
}
126119
};
127120
($struct:ident, $sol_type:expr) => {
128121
impl Cheatcode for $struct {
129122
fn apply(&self, state: &mut crate::Cheatcodes) -> Result {
130123
let Self { chain, key } = self;
131-
get_array(chain.to::<u64>(), key, $sol_type, state)
124+
get_array(Chain::from(chain.to::<u64>()), key, $sol_type, state)
132125
}
133126
}
134127
};
@@ -178,44 +171,46 @@ impl_get_array_cheatcode!(readForkStringArrayCall, &DynSolType::String, stateful
178171

179172
/// Generic helper to get any value from the TOML config.
180173
fn get_toml_value<'a>(
181-
name: &'a str,
174+
chain: Chain,
182175
key: &'a str,
183176
state: &'a crate::Cheatcodes,
184177
) -> Result<&'a toml::Value> {
185-
let config = state.config.forks.get(name).ok_or_else(|| {
186-
fmt_err!("[fork.{name}] subsection not found in [fork] of 'foundry.toml'")
178+
let config = state.config.forks.get(&chain).ok_or_else(|| {
179+
fmt_err!("[fork.{chain}] subsection not found in [fork] of 'foundry.toml'")
180+
})?;
181+
let value = config.vars.get(key).ok_or_else(|| {
182+
fmt_err!("variable '{key}' not found in '[fork.<chain_id: {chain}>]'", chain = chain.id())
187183
})?;
188-
let value = config
189-
.vars
190-
.get(key)
191-
.ok_or_else(|| fmt_err!("Variable '{key}' not found in [fork.{name}] configuration"))?;
192184

193185
Ok(value)
194186
}
195187

196188
/// Generic helper to get any single value from the TOML config.
197-
fn get_value(chain: u64, key: &str, ty: &DynSolType, state: &crate::Cheatcodes) -> Result {
198-
let name = get_chain_name(chain)?;
199-
let value = get_toml_value(name, key, state)?;
200-
let sol_value = parse_toml_element(value, ty, key, name)?;
189+
fn get_value(chain: Chain, key: &str, ty: &DynSolType, state: &crate::Cheatcodes) -> Result {
190+
let value = get_toml_value(chain, key, state)?;
191+
let sol_value = parse_toml_element(value, ty, key, chain)?;
201192
Ok(sol_value.abi_encode())
202193
}
203194

204195
/// Generic helper to get an array of values from the TOML config.
205-
fn get_array(chain: u64, key: &str, element_ty: &DynSolType, state: &crate::Cheatcodes) -> Result {
206-
let name = get_chain_name(chain)?;
207-
let value = get_toml_value(name, key, state)?;
208-
209-
let arr = value
210-
.as_array()
211-
.ok_or_else(|| fmt_err!("Variable '{key}' in [fork.{name}] must be an array"))?;
212-
196+
fn get_array(
197+
chain: Chain,
198+
key: &str,
199+
element_ty: &DynSolType,
200+
state: &crate::Cheatcodes,
201+
) -> Result {
202+
let arr = get_toml_value(chain, key, state)?.as_array().ok_or_else(|| {
203+
fmt_err!(
204+
"variable '{key}' in '[fork.<chain_id: {chain}>]' must be an array",
205+
chain = chain.id()
206+
)
207+
})?;
213208
let result: Result<Vec<_>> = arr
214209
.iter()
215210
.enumerate()
216211
.map(|(i, elem)| {
217212
let context = format!("{key}[{i}]");
218-
parse_toml_element(elem, element_ty, &context, name)
213+
parse_toml_element(elem, element_ty, &context, chain)
219214
})
220215
.collect();
221216

@@ -227,9 +222,9 @@ fn parse_toml_element(
227222
elem: &toml::Value,
228223
element_ty: &DynSolType,
229224
context: &str,
230-
fork_name: &str,
225+
chain: Chain,
231226
) -> Result<DynSolValue> {
232227
// Convert TOML value to JSON value and use existing JSON parsing logic
233228
parse_json_as(&toml_to_json_value(elem.to_owned()), element_ty)
234-
.map_err(|e| fmt_err!("Failed to parse '{context}' in [fork.{fork_name}]: {e}"))
229+
.map_err(|e| fmt_err!("Failed to parse '{context}' in [fork.{chain}]: {e}"))
235230
}

crates/config/src/fork_config.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,17 @@ use crate::{
44
resolve::{RE_PLACEHOLDER, UnresolvedEnvVarError, interpolate},
55
};
66

7+
use alloy_chains::Chain;
78
use serde::{Deserialize, Serialize};
89
use std::{collections::HashMap, ops::Deref};
910

1011
/// Fork-scoped config for tests and scripts.
1112
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
12-
pub struct ForkConfigs(pub HashMap<String, ForkChainConfig>);
13+
pub struct ForkConfigs(pub HashMap<Chain, ForkChainConfig>);
1314

1415
impl ForkConfigs {
15-
/// Normalize fork config chain keys and resolve environment variables in all configured fields.
16-
pub fn normalize_and_resolve(&mut self) -> Result<(), ExtractConfigError> {
17-
self.normalize_keys()?;
18-
self.resolve_env_vars()
19-
}
20-
21-
/// Normalize fork config chains, so that all have `alloy_chain::NamedChain` compatible names.
22-
fn normalize_keys(&mut self) -> Result<(), ExtractConfigError> {
23-
let mut normalized = HashMap::new();
24-
25-
for (key, config) in std::mem::take(&mut self.0) {
26-
// Determine the canonical key for this entry
27-
let canonical_key = if let Ok(chain_id) = key.parse::<u64>() {
28-
if let Some(named) = alloy_chains::Chain::from_id(chain_id).named() {
29-
named.as_str().to_string()
30-
} else {
31-
return Err(ExtractConfigError::new(figment::Error::from(format!(
32-
"chain id '{key}' is not supported. Check 'https://github.com/alloy-rs/chains' and consider opening a PR.",
33-
))));
34-
}
35-
} else if let Ok(named) = key.parse::<alloy_chains::NamedChain>() {
36-
named.as_str().to_string()
37-
} else {
38-
return Err(ExtractConfigError::new(figment::Error::from(format!(
39-
"chain name '{key}' is not supported. Check 'https://github.com/alloy-rs/chains' and consider opening a PR.",
40-
))));
41-
};
42-
43-
// Insert and check for conflicts
44-
if normalized.insert(canonical_key, config).is_some() {
45-
return Err(ExtractConfigError::new(figment::Error::from(
46-
"duplicate fork configuration.",
47-
)));
48-
}
49-
}
50-
51-
self.0 = normalized;
52-
Ok(())
53-
}
54-
5516
/// Resolve environment variables in all fork config fields
56-
fn resolve_env_vars(&mut self) -> Result<(), ExtractConfigError> {
17+
pub fn resolve_env_vars(&mut self) -> Result<(), ExtractConfigError> {
5718
for (name, fork_config) in &mut self.0 {
5819
// Take temporary ownership of the config, so that it can be consumed.
5920
let config = std::mem::take(fork_config);
@@ -76,7 +37,7 @@ impl ForkConfigs {
7637
}
7738

7839
impl Deref for ForkConfigs {
79-
type Target = HashMap<String, ForkChainConfig>;
40+
type Target = HashMap<Chain, ForkChainConfig>;
8041

8142
fn deref(&self) -> &Self::Target {
8243
&self.0

0 commit comments

Comments
 (0)