1use crate::{Cheatcode, Error, Result, Vm::*, string};
4use alloy_dyn_abi::DynSolType;
5use alloy_sol_types::SolValue;
6use std::{env, sync::OnceLock};
7
8pub static FORGE_CONTEXT: OnceLock<ForgeContext> = OnceLock::new();
10
11pub fn current_execution_context() -> Option<ForgeContext> {
13 FORGE_CONTEXT.get().copied()
14}
15
16impl Cheatcode for setEnvCall {
17 fn apply_context_free(&self) -> Result {
18 let Self { name: key, value } = self;
19 if key.is_empty() {
20 Err(fmt_err!("environment variable key can't be empty"))
21 } else if key.contains('=') {
22 Err(fmt_err!("environment variable key can't contain equal sign `=`"))
23 } else if key.contains('\0') {
24 Err(fmt_err!("environment variable key can't contain NUL character `\\0`"))
25 } else if value.contains('\0') {
26 Err(fmt_err!("environment variable value can't contain NUL character `\\0`"))
27 } else {
28 unsafe {
29 env::set_var(key, value);
30 }
31 Ok(Default::default())
32 }
33 }
34}
35
36impl Cheatcode for resolveEnvCall {
37 fn apply_context_free(&self) -> Result {
38 let Self { input } = self;
39 let resolved = foundry_config::resolve::interpolate(input)
40 .map_err(|e| fmt_err!("failed to resolve env var: {e}"))?;
41 Ok(resolved.abi_encode())
42 }
43}
44
45impl Cheatcode for envExistsCall {
46 fn apply_context_free(&self) -> Result {
47 let Self { name } = self;
48 Ok(env::var(name).is_ok().abi_encode())
49 }
50}
51
52impl Cheatcode for envBool_0Call {
53 fn apply_context_free(&self) -> Result {
54 let Self { name } = self;
55 env(name, &DynSolType::Bool)
56 }
57}
58
59impl Cheatcode for envUint_0Call {
60 fn apply_context_free(&self) -> Result {
61 let Self { name } = self;
62 env(name, &DynSolType::Uint(256))
63 }
64}
65
66impl Cheatcode for envInt_0Call {
67 fn apply_context_free(&self) -> Result {
68 let Self { name } = self;
69 env(name, &DynSolType::Int(256))
70 }
71}
72
73impl Cheatcode for envAddress_0Call {
74 fn apply_context_free(&self) -> Result {
75 let Self { name } = self;
76 env(name, &DynSolType::Address)
77 }
78}
79
80impl Cheatcode for envBytes32_0Call {
81 fn apply_context_free(&self) -> Result {
82 let Self { name } = self;
83 env(name, &DynSolType::FixedBytes(32))
84 }
85}
86
87impl Cheatcode for envString_0Call {
88 fn apply_context_free(&self) -> Result {
89 let Self { name } = self;
90 env(name, &DynSolType::String)
91 }
92}
93
94impl Cheatcode for envBytes_0Call {
95 fn apply_context_free(&self) -> Result {
96 let Self { name } = self;
97 env(name, &DynSolType::Bytes)
98 }
99}
100
101impl Cheatcode for envBool_1Call {
102 fn apply_context_free(&self) -> Result {
103 let Self { name, delim } = self;
104 env_array(name, delim, &DynSolType::Bool)
105 }
106}
107
108impl Cheatcode for envUint_1Call {
109 fn apply_context_free(&self) -> Result {
110 let Self { name, delim } = self;
111 env_array(name, delim, &DynSolType::Uint(256))
112 }
113}
114
115impl Cheatcode for envInt_1Call {
116 fn apply_context_free(&self) -> Result {
117 let Self { name, delim } = self;
118 env_array(name, delim, &DynSolType::Int(256))
119 }
120}
121
122impl Cheatcode for envAddress_1Call {
123 fn apply_context_free(&self) -> Result {
124 let Self { name, delim } = self;
125 env_array(name, delim, &DynSolType::Address)
126 }
127}
128
129impl Cheatcode for envBytes32_1Call {
130 fn apply_context_free(&self) -> Result {
131 let Self { name, delim } = self;
132 env_array(name, delim, &DynSolType::FixedBytes(32))
133 }
134}
135
136impl Cheatcode for envString_1Call {
137 fn apply_context_free(&self) -> Result {
138 let Self { name, delim } = self;
139 env_array(name, delim, &DynSolType::String)
140 }
141}
142
143impl Cheatcode for envBytes_1Call {
144 fn apply_context_free(&self) -> Result {
145 let Self { name, delim } = self;
146 env_array(name, delim, &DynSolType::Bytes)
147 }
148}
149
150impl Cheatcode for envOr_0Call {
152 fn apply_context_free(&self) -> Result {
153 let Self { name, defaultValue } = self;
154 env_default(name, defaultValue, &DynSolType::Bool)
155 }
156}
157
158impl Cheatcode for envOr_1Call {
160 fn apply_context_free(&self) -> Result {
161 let Self { name, defaultValue } = self;
162 env_default(name, defaultValue, &DynSolType::Uint(256))
163 }
164}
165
166impl Cheatcode for envOr_2Call {
168 fn apply_context_free(&self) -> Result {
169 let Self { name, defaultValue } = self;
170 env_default(name, defaultValue, &DynSolType::Int(256))
171 }
172}
173
174impl Cheatcode for envOr_3Call {
176 fn apply_context_free(&self) -> Result {
177 let Self { name, defaultValue } = self;
178 env_default(name, defaultValue, &DynSolType::Address)
179 }
180}
181
182impl Cheatcode for envOr_4Call {
184 fn apply_context_free(&self) -> Result {
185 let Self { name, defaultValue } = self;
186 env_default(name, defaultValue, &DynSolType::FixedBytes(32))
187 }
188}
189
190impl Cheatcode for envOr_5Call {
192 fn apply_context_free(&self) -> Result {
193 let Self { name, defaultValue } = self;
194 env_default(name, defaultValue, &DynSolType::String)
195 }
196}
197
198impl Cheatcode for envOr_6Call {
200 fn apply_context_free(&self) -> Result {
201 let Self { name, defaultValue } = self;
202 env_default(name, defaultValue, &DynSolType::Bytes)
203 }
204}
205
206impl Cheatcode for envOr_7Call {
208 fn apply_context_free(&self) -> Result {
209 let Self { name, delim, defaultValue } = self;
210 env_array_default(name, delim, defaultValue, &DynSolType::Bool)
211 }
212}
213
214impl Cheatcode for envOr_8Call {
216 fn apply_context_free(&self) -> Result {
217 let Self { name, delim, defaultValue } = self;
218 env_array_default(name, delim, defaultValue, &DynSolType::Uint(256))
219 }
220}
221
222impl Cheatcode for envOr_9Call {
224 fn apply_context_free(&self) -> Result {
225 let Self { name, delim, defaultValue } = self;
226 env_array_default(name, delim, defaultValue, &DynSolType::Int(256))
227 }
228}
229
230impl Cheatcode for envOr_10Call {
232 fn apply_context_free(&self) -> Result {
233 let Self { name, delim, defaultValue } = self;
234 env_array_default(name, delim, defaultValue, &DynSolType::Address)
235 }
236}
237
238impl Cheatcode for envOr_11Call {
240 fn apply_context_free(&self) -> Result {
241 let Self { name, delim, defaultValue } = self;
242 env_array_default(name, delim, defaultValue, &DynSolType::FixedBytes(32))
243 }
244}
245
246impl Cheatcode for envOr_12Call {
248 fn apply_context_free(&self) -> Result {
249 let Self { name, delim, defaultValue } = self;
250 env_array_default(name, delim, defaultValue, &DynSolType::String)
251 }
252}
253
254impl Cheatcode for envOr_13Call {
256 fn apply_context_free(&self) -> Result {
257 let Self { name, delim, defaultValue } = self;
258 let default = defaultValue.clone();
259 env_array_default(name, delim, &default, &DynSolType::Bytes)
260 }
261}
262
263impl Cheatcode for isContextCall {
264 fn apply_context_free(&self) -> Result {
265 let Self { context } = self;
266 Ok((FORGE_CONTEXT.get() == Some(context)).abi_encode())
267 }
268}
269
270pub fn set_execution_context(context: ForgeContext) {
273 let _ = FORGE_CONTEXT.set(context);
274}
275
276fn env(key: &str, ty: &DynSolType) -> Result {
277 get_env(key).and_then(|val| string::parse(&val, ty).map_err(map_env_err(key, &val)))
278}
279
280fn env_default<T: SolValue>(key: &str, default: &T, ty: &DynSolType) -> Result {
281 if env_is_missing(key) {
282 return Ok(default.abi_encode());
283 }
284 let val = get_env(key)?;
285 if val.is_empty() && !matches!(ty, DynSolType::String) {
288 return Ok(default.abi_encode());
289 }
290 string::parse(&val, ty).map_err(map_env_err(key, &val))
291}
292
293fn env_array(key: &str, delim: &str, ty: &DynSolType) -> Result {
294 get_env(key).and_then(|val| {
295 string::parse_array(val.split(delim).map(str::trim), ty).map_err(map_env_err(key, &val))
296 })
297}
298
299fn env_array_default<T: SolValue>(key: &str, delim: &str, default: &T, ty: &DynSolType) -> Result {
300 if env_is_missing(key) {
301 return Ok(default.abi_encode());
302 }
303 env_array(key, delim, ty)
304}
305
306fn env_is_missing(key: &str) -> bool {
310 matches!(env::var(key), Err(env::VarError::NotPresent))
311}
312
313fn get_env(key: &str) -> Result<String> {
314 match env::var(key) {
315 Ok(val) => Ok(val),
316 Err(env::VarError::NotPresent) => Err(fmt_err!("environment variable {key:?} not found")),
317 Err(env::VarError::NotUnicode(s)) => {
318 Err(fmt_err!("environment variable {key:?} was not valid unicode: {s:?}"))
319 }
320 }
321}
322
323fn map_env_err<'a>(key: &'a str, value: &'a str) -> impl FnOnce(Error) -> Error + 'a {
326 move |e| {
327 let mut e = e.to_string();
332 e = e.replacen(&format!("\"{value}\""), &format!("${key}"), 1);
333 e = e.replacen(&format!("\n{value}\n"), &format!("\n${key}\n"), 1);
334 Error::from(e)
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn env_or_returns_default_when_missing() {
344 let key = "env_or_missing";
345 unsafe {
346 env::remove_var(key);
347 }
348 let default = 7u64;
349 let encoded = default.abi_encode();
350 assert_eq!(env_default(key, &default, &DynSolType::Uint(256)).unwrap(), encoded);
351 assert_eq!(env_array_default(key, ",", &default, &DynSolType::Uint(256)).unwrap(), encoded);
352 }
353
354 #[test]
355 fn env_or_returns_default_when_empty() {
356 let key = "env_or_empty";
357 unsafe {
358 env::set_var(key, "");
359 }
360 let default = 7u64;
361 assert_eq!(
362 env_default(key, &default, &DynSolType::Uint(256)).unwrap(),
363 default.abi_encode()
364 );
365 assert_eq!(
366 env_array_default(key, ",", &default, &DynSolType::Uint(256)).unwrap(),
367 Vec::<u64>::new().abi_encode()
368 );
369 assert_eq!(
371 env_default(key, &"default", &DynSolType::String).unwrap(),
372 String::new().abi_encode()
373 );
374 unsafe {
375 env::remove_var(key);
376 }
377 }
378
379 #[test]
380 fn env_or_rejects_unparsable_value() {
381 let key = "env_or_unparsable";
382 unsafe {
383 env::set_var(key, "not_a_number");
384 }
385
386 let err = env_default(key, &7u64, &DynSolType::Uint(256)).unwrap_err().to_string();
387 assert!(err.contains("$env_or_unparsable"), "{err:?}");
388 let err =
389 env_array_default(key, ",", &7u64, &DynSolType::Uint(256)).unwrap_err().to_string();
390 assert!(err.contains("$env_or_unparsable"), "{err:?}");
391 unsafe {
392 env::remove_var(key);
393 }
394 }
395
396 #[test]
397 fn parse_env_uint() {
398 let key = "parse_env_uint";
399 let value = "t";
400 unsafe {
401 env::set_var(key, value);
402 }
403
404 let err = env(key, &DynSolType::Uint(256)).unwrap_err().to_string();
405 assert_eq!(err.matches("$parse_env_uint").count(), 2, "{err:?}");
406 unsafe {
407 env::remove_var(key);
408 }
409 }
410}