Skip to main content

foundry_cheatcodes/
json.rs

1//! Implementations of [`Json`](spec::Group::Json) cheatcodes.
2
3use crate::{Cheatcode, Cheatcodes, Result, Vm::*, string};
4use alloy_dyn_abi::{DynSolType, DynSolValue, Resolver, eip712_parser::EncodeType};
5use alloy_primitives::{Address, B256, I256, U256, hex};
6use alloy_sol_types::SolValue;
7use foundry_common::{fmt::StructDefinitions, fs};
8use foundry_config::fs_permissions::FsAccessKind;
9use foundry_evm_core::evm::FoundryEvmNetwork;
10use serde_json::{Map, Value};
11use std::{
12    borrow::Cow,
13    collections::{BTreeMap, BTreeSet},
14};
15
16impl Cheatcode for keyExistsCall {
17    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
18        let Self { json, key } = self;
19        check_json_key_exists(json, key)
20    }
21}
22
23impl Cheatcode for keyExistsJsonCall {
24    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
25        let Self { json, key } = self;
26        check_json_key_exists(json, key)
27    }
28}
29
30impl Cheatcode for parseJson_0Call {
31    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
32        let Self { json } = self;
33        parse_json(json, "$", state.struct_defs())
34    }
35}
36
37impl Cheatcode for parseJson_1Call {
38    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
39        let Self { json, key } = self;
40        parse_json(json, key, state.struct_defs())
41    }
42}
43
44impl Cheatcode for parseJsonUintCall {
45    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
46        let Self { json, key } = self;
47        parse_json_coerce(json, key, &DynSolType::Uint(256))
48    }
49}
50
51impl Cheatcode for parseJsonUintArrayCall {
52    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
53        let Self { json, key } = self;
54        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Uint(256))))
55    }
56}
57
58impl Cheatcode for parseJsonIntCall {
59    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
60        let Self { json, key } = self;
61        parse_json_coerce(json, key, &DynSolType::Int(256))
62    }
63}
64
65impl Cheatcode for parseJsonIntArrayCall {
66    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
67        let Self { json, key } = self;
68        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Int(256))))
69    }
70}
71
72impl Cheatcode for parseJsonBoolCall {
73    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
74        let Self { json, key } = self;
75        parse_json_coerce(json, key, &DynSolType::Bool)
76    }
77}
78
79impl Cheatcode for parseJsonBoolArrayCall {
80    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
81        let Self { json, key } = self;
82        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bool)))
83    }
84}
85
86impl Cheatcode for parseJsonAddressCall {
87    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
88        let Self { json, key } = self;
89        parse_json_coerce(json, key, &DynSolType::Address)
90    }
91}
92
93impl Cheatcode for parseJsonAddressArrayCall {
94    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
95        let Self { json, key } = self;
96        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Address)))
97    }
98}
99
100impl Cheatcode for parseJsonStringCall {
101    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
102        let Self { json, key } = self;
103        parse_json_coerce(json, key, &DynSolType::String)
104    }
105}
106
107impl Cheatcode for parseJsonStringArrayCall {
108    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
109        let Self { json, key } = self;
110        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::String)))
111    }
112}
113
114impl Cheatcode for parseJsonBytesCall {
115    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
116        let Self { json, key } = self;
117        parse_json_coerce(json, key, &DynSolType::Bytes)
118    }
119}
120
121impl Cheatcode for parseJsonBytesArrayCall {
122    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
123        let Self { json, key } = self;
124        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bytes)))
125    }
126}
127
128impl Cheatcode for parseJsonBytes32Call {
129    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
130        let Self { json, key } = self;
131        parse_json_coerce(json, key, &DynSolType::FixedBytes(32))
132    }
133}
134
135impl Cheatcode for parseJsonBytes32ArrayCall {
136    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
137        let Self { json, key } = self;
138        parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32))))
139    }
140}
141
142impl Cheatcode for parseJsonType_0Call {
143    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
144        let Self { json, typeDescription } = self;
145        parse_json_coerce(json, "$", &resolve_type(typeDescription, state.struct_defs())?)
146            .map(|v| v.abi_encode())
147    }
148}
149
150impl Cheatcode for parseJsonType_1Call {
151    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
152        let Self { json, key, typeDescription } = self;
153        parse_json_coerce(json, key, &resolve_type(typeDescription, state.struct_defs())?)
154            .map(|v| v.abi_encode())
155    }
156}
157
158impl Cheatcode for parseJsonTypeArrayCall {
159    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
160        let Self { json, key, typeDescription } = self;
161        let ty = resolve_type(typeDescription, state.struct_defs())?;
162        parse_json_coerce(json, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
163    }
164}
165
166impl Cheatcode for parseJsonKeysCall {
167    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
168        let Self { json, key } = self;
169        parse_json_keys(json, key)
170    }
171}
172
173impl Cheatcode for serializeJsonCall {
174    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
175        let Self { objectKey, value } = self;
176        *state.serialized_jsons.entry(objectKey.into()).or_default() = serde_json::from_str(value)?;
177        Ok(value.abi_encode())
178    }
179}
180
181impl Cheatcode for serializeBool_0Call {
182    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
183        let Self { objectKey, valueKey, value } = self;
184        serialize_json(state, objectKey, valueKey, (*value).into())
185    }
186}
187
188impl Cheatcode for serializeUint_0Call {
189    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
190        let Self { objectKey, valueKey, value } = self;
191        serialize_json(state, objectKey, valueKey, (*value).into())
192    }
193}
194
195impl Cheatcode for serializeInt_0Call {
196    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
197        let Self { objectKey, valueKey, value } = self;
198        serialize_json(state, objectKey, valueKey, (*value).into())
199    }
200}
201
202impl Cheatcode for serializeAddress_0Call {
203    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
204        let Self { objectKey, valueKey, value } = self;
205        serialize_json(state, objectKey, valueKey, (*value).into())
206    }
207}
208
209impl Cheatcode for serializeBytes32_0Call {
210    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
211        let Self { objectKey, valueKey, value } = self;
212        serialize_json(state, objectKey, valueKey, DynSolValue::FixedBytes(*value, 32))
213    }
214}
215
216impl Cheatcode for serializeString_0Call {
217    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
218        let Self { objectKey, valueKey, value } = self;
219        serialize_json(state, objectKey, valueKey, value.clone().into())
220    }
221}
222
223impl Cheatcode for serializeBytes_0Call {
224    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
225        let Self { objectKey, valueKey, value } = self;
226        serialize_json(state, objectKey, valueKey, value.to_vec().into())
227    }
228}
229
230impl Cheatcode for serializeBool_1Call {
231    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
232        let Self { objectKey, valueKey, values } = self;
233        serialize_json(
234            state,
235            objectKey,
236            valueKey,
237            DynSolValue::Array(values.iter().copied().map(DynSolValue::Bool).collect()),
238        )
239    }
240}
241
242impl Cheatcode for serializeUint_1Call {
243    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
244        let Self { objectKey, valueKey, values } = self;
245        serialize_json(
246            state,
247            objectKey,
248            valueKey,
249            DynSolValue::Array(values.iter().map(|v| DynSolValue::Uint(*v, 256)).collect()),
250        )
251    }
252}
253
254impl Cheatcode for serializeInt_1Call {
255    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
256        let Self { objectKey, valueKey, values } = self;
257        serialize_json(
258            state,
259            objectKey,
260            valueKey,
261            DynSolValue::Array(values.iter().map(|v| DynSolValue::Int(*v, 256)).collect()),
262        )
263    }
264}
265
266impl Cheatcode for serializeAddress_1Call {
267    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
268        let Self { objectKey, valueKey, values } = self;
269        serialize_json(
270            state,
271            objectKey,
272            valueKey,
273            DynSolValue::Array(values.iter().copied().map(DynSolValue::Address).collect()),
274        )
275    }
276}
277
278impl Cheatcode for serializeBytes32_1Call {
279    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
280        let Self { objectKey, valueKey, values } = self;
281        serialize_json(
282            state,
283            objectKey,
284            valueKey,
285            DynSolValue::Array(values.iter().map(|v| DynSolValue::FixedBytes(*v, 32)).collect()),
286        )
287    }
288}
289
290impl Cheatcode for serializeString_1Call {
291    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
292        let Self { objectKey, valueKey, values } = self;
293        serialize_json(
294            state,
295            objectKey,
296            valueKey,
297            DynSolValue::Array(values.iter().cloned().map(DynSolValue::String).collect()),
298        )
299    }
300}
301
302impl Cheatcode for serializeBytes_1Call {
303    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
304        let Self { objectKey, valueKey, values } = self;
305        serialize_json(
306            state,
307            objectKey,
308            valueKey,
309            DynSolValue::Array(
310                values.iter().cloned().map(Into::into).map(DynSolValue::Bytes).collect(),
311            ),
312        )
313    }
314}
315
316impl Cheatcode for serializeJsonType_0Call {
317    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
318        let Self { typeDescription, value } = self;
319        let ty = resolve_type(typeDescription, state.struct_defs())?;
320        let value = ty.abi_decode(value)?;
321        let value = foundry_common::fmt::serialize_value_as_json(value, state.struct_defs())?;
322        Ok(value.to_string().abi_encode())
323    }
324}
325
326impl Cheatcode for serializeJsonType_1Call {
327    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
328        let Self { objectKey, valueKey, typeDescription, value } = self;
329        let ty = resolve_type(typeDescription, state.struct_defs())?;
330        let value = ty.abi_decode(value)?;
331        serialize_json(state, objectKey, valueKey, value)
332    }
333}
334
335impl Cheatcode for serializeUintToHexCall {
336    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
337        let Self { objectKey, valueKey, value } = self;
338        let hex = format!("0x{value:x}");
339        serialize_json(state, objectKey, valueKey, hex.into())
340    }
341}
342
343impl Cheatcode for writeJson_0Call {
344    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
345        let Self { json, path } = self;
346        let json = serde_json::from_str(json).unwrap_or_else(|_| Value::String(json.to_owned()));
347        let json_string = serde_json::to_string_pretty(&json)?;
348        super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
349    }
350}
351
352impl Cheatcode for writeJson_1Call {
353    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
354        let Self { json: value, path, valueKey } = self;
355
356        // Read, parse, and update the JSON object.
357        // If the file doesn't exist, start with an empty JSON object so the file is created.
358        let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
359        let mut data = if data_path.exists() {
360            let data_string = fs::locked_read_to_string(&data_path)?;
361            serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string))
362        } else {
363            Value::Object(Default::default())
364        };
365        upsert_json_value(&mut data, value, valueKey)?;
366
367        // Write the updated content back to the file
368        let json_string = serde_json::to_string_pretty(&data)?;
369        super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
370    }
371}
372
373pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result {
374    let json = parse_json_str(json)?;
375    let values = select(&json, key)?;
376    let exists = !values.is_empty();
377    Ok(exists.abi_encode())
378}
379
380pub(super) fn parse_json(json: &str, path: &str, defs: Option<&StructDefinitions>) -> Result {
381    let value = parse_json_str(json)?;
382    let selected = select(&value, path)?;
383    let sol = json_to_sol(defs, &selected)?;
384    Ok(encode(sol))
385}
386
387pub(super) fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result {
388    let json = parse_json_str(json)?;
389    let [value] = select(&json, path)?[..] else {
390        bail!("path {path:?} must return exactly one JSON value");
391    };
392
393    parse_json_as(value, ty).map(|v| v.abi_encode())
394}
395
396/// Parses given [serde_json::Value] as a [DynSolValue].
397pub(super) fn parse_json_as(value: &Value, ty: &DynSolType) -> Result<DynSolValue> {
398    let to_string = |v: &Value| {
399        let mut s = v.to_string();
400        s.retain(|c: char| c != '"');
401        s
402    };
403
404    match (value, ty) {
405        (Value::Array(array), ty) => parse_json_array(array, ty),
406        (Value::Object(object), ty) => parse_json_map(object, ty),
407        (Value::String(s), DynSolType::String) => Ok(DynSolValue::String(s.clone())),
408        (Value::String(s), DynSolType::Uint(_) | DynSolType::Int(_)) => string::parse_value(s, ty),
409        _ => string::parse_value(&to_string(value), ty),
410    }
411}
412
413pub(super) fn parse_json_array(array: &[Value], ty: &DynSolType) -> Result<DynSolValue> {
414    match ty {
415        DynSolType::Tuple(types) => {
416            ensure!(array.len() == types.len(), "array length mismatch");
417            let values = array
418                .iter()
419                .zip(types)
420                .map(|(e, ty)| parse_json_as(e, ty))
421                .collect::<Result<Vec<_>>>()?;
422
423            Ok(DynSolValue::Tuple(values))
424        }
425        DynSolType::Array(inner) => {
426            let values =
427                array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
428            Ok(DynSolValue::Array(values))
429        }
430        DynSolType::FixedArray(inner, len) => {
431            ensure!(array.len() == *len, "array length mismatch");
432            let values =
433                array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
434            Ok(DynSolValue::FixedArray(values))
435        }
436        _ => bail!("expected {ty}, found array"),
437    }
438}
439
440pub(super) fn parse_json_map(map: &Map<String, Value>, ty: &DynSolType) -> Result<DynSolValue> {
441    let Some((name, fields, types)) = ty.as_custom_struct() else {
442        bail!("expected {ty}, found JSON object");
443    };
444
445    let mut values = Vec::with_capacity(fields.len());
446    for (field, ty) in fields.iter().zip(types.iter()) {
447        let Some(value) = map.get(field) else { bail!("field {field:?} not found in JSON object") };
448        values.push(parse_json_as(value, ty)?);
449    }
450
451    Ok(DynSolValue::CustomStruct {
452        name: name.to_string(),
453        prop_names: fields.to_vec(),
454        tuple: values,
455    })
456}
457
458pub(super) fn parse_json_keys(json: &str, key: &str) -> Result {
459    let json = parse_json_str(json)?;
460    let values = select(&json, key)?;
461    let [value] = values[..] else {
462        bail!("key {key:?} must return exactly one JSON object");
463    };
464    let Value::Object(object) = value else {
465        bail!("JSON value at {key:?} is not an object");
466    };
467    let keys = object.keys().collect::<Vec<_>>();
468    Ok(keys.abi_encode())
469}
470
471fn parse_json_str(json: &str) -> Result<Value> {
472    serde_json::from_str(json).map_err(|e| fmt_err!("failed parsing JSON: {e}"))
473}
474
475fn json_to_sol(defs: Option<&StructDefinitions>, json: &[&Value]) -> Result<Vec<DynSolValue>> {
476    let mut sol = Vec::with_capacity(json.len());
477    for value in json {
478        sol.push(json_value_to_token(value, defs)?);
479    }
480    Ok(sol)
481}
482
483fn select<'a>(value: &'a Value, mut path: &str) -> Result<Vec<&'a Value>> {
484    // Handle the special case of the root key
485    if path == "." {
486        path = "$";
487    }
488    // format error with debug string because json_path errors may contain newlines
489    jsonpath_lib::select(value, &canonicalize_json_path(path))
490        .map_err(|e| fmt_err!("failed selecting from JSON: {:?}", e.to_string()))
491}
492
493fn encode(values: Vec<DynSolValue>) -> Vec<u8> {
494    // Double `abi_encode` is intentional
495    let bytes = match &values[..] {
496        [] => Vec::new(),
497        [one] => one.abi_encode(),
498        _ => DynSolValue::Array(values).abi_encode(),
499    };
500    bytes.abi_encode()
501}
502
503/// Canonicalize a json path key to always start from the root of the document.
504/// Read more about json path syntax: <https://goessner.net/articles/JsonPath/>
505pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> {
506    if path.starts_with('$') { path.into() } else { format!("${path}").into() }
507}
508
509/// Converts a JSON [`Value`] to a [`DynSolValue`] by trying to guess encoded type. For safer
510/// decoding, use [`parse_json_as`].
511///
512/// The function is designed to run recursively, so that in case of an object
513/// it will call itself to convert each of it's value and encode the whole as a
514/// Tuple
515#[instrument(target = "cheatcodes", level = "trace", ret)]
516pub(super) fn json_value_to_token(
517    value: &Value,
518    defs: Option<&StructDefinitions>,
519) -> Result<DynSolValue> {
520    if let Some(defs) = defs {
521        _json_value_to_token(value, defs)
522    } else {
523        _json_value_to_token(value, &StructDefinitions::default())
524    }
525}
526
527fn _json_value_to_token(value: &Value, defs: &StructDefinitions) -> Result<DynSolValue> {
528    match value {
529        Value::Null => Ok(DynSolValue::FixedBytes(B256::ZERO, 32)),
530        Value::Bool(boolean) => Ok(DynSolValue::Bool(*boolean)),
531        Value::Array(array) => array
532            .iter()
533            .map(|v| _json_value_to_token(v, defs))
534            .collect::<Result<_>>()
535            .map(DynSolValue::Array),
536        Value::Object(map) => {
537            // Try to find a struct definition that matches the object keys.
538            let keys: BTreeSet<_> = map.keys().map(|s| s.as_str()).collect();
539            let matching_def = defs.values().find(|fields| {
540                fields.len() == keys.len()
541                    && fields.iter().map(|(name, _)| name.as_str()).collect::<BTreeSet<_>>() == keys
542            });
543
544            if let Some(fields) = matching_def {
545                // Found a struct with matching field names, use the order from the definition.
546                fields
547                    .iter()
548                    .map(|(name, _)| {
549                        // unwrap is safe because we know the key exists.
550                        _json_value_to_token(map.get(name).unwrap(), defs)
551                    })
552                    .collect::<Result<_>>()
553                    .map(DynSolValue::Tuple)
554            } else {
555                // Fallback to alphabetical sorting if no matching struct is found.
556                // See: [#3647](https://github.com/foundry-rs/foundry/pull/3647)
557                let ordered_object: BTreeMap<_, _> =
558                    map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
559                ordered_object
560                    .values()
561                    .map(|value| _json_value_to_token(value, defs))
562                    .collect::<Result<_>>()
563                    .map(DynSolValue::Tuple)
564            }
565        }
566        Value::Number(number) => {
567            if let Some(f) = number.as_f64() {
568                // Check if the number has decimal digits because the EVM does not support floating
569                // point math
570                if f.fract() == 0.0 {
571                    // Use the string representation of the `serde_json` Number type instead of
572                    // calling f.to_string(), because some numbers are wrongly rounded up after
573                    // being convented to f64.
574                    // Example: 18446744073709551615 becomes 18446744073709552000 after parsing it
575                    // to f64.
576                    let s = number.to_string();
577
578                    // Coerced to scientific notation, so short-circuit to using fallback.
579                    // This will not have a problem with hex numbers, as for parsing these
580                    // We'd need to prefix this with 0x.
581                    // See also <https://docs.soliditylang.org/en/latest/types.html#rational-and-integer-literals>
582                    if s.contains('e') {
583                        // Calling Number::to_string with powers of ten formats the number using
584                        // scientific notation and causes from_dec_str to fail. Using format! with
585                        // f64 keeps the full number representation.
586                        // Example: 100000000000000000000 becomes 1e20 when Number::to_string is
587                        // used.
588                        let fallback_s = f.to_string();
589                        if let Ok(n) = fallback_s.parse() {
590                            return Ok(DynSolValue::Uint(n, 256));
591                        }
592                        if let Ok(n) = I256::from_dec_str(&fallback_s) {
593                            return Ok(DynSolValue::Int(n, 256));
594                        }
595                    }
596
597                    if let Ok(n) = s.parse() {
598                        return Ok(DynSolValue::Uint(n, 256));
599                    }
600                    if let Ok(n) = s.parse() {
601                        return Ok(DynSolValue::Int(n, 256));
602                    }
603                }
604            }
605
606            Err(fmt_err!("unsupported JSON number: {number}"))
607        }
608        Value::String(string) => {
609            //  Hanfl hex strings
610            if let Some(mut val) = string.strip_prefix("0x") {
611                let s;
612                if val.len() == 39 {
613                    return Err(format!("Cannot parse \"{val}\" as an address. If you want to specify address, prepend zero to the value.").into());
614                }
615                if !val.len().is_multiple_of(2) {
616                    s = format!("0{val}");
617                    val = &s[..];
618                }
619                if let Ok(bytes) = hex::decode(val) {
620                    return Ok(match bytes.len() {
621                        20 => DynSolValue::Address(Address::from_slice(&bytes)),
622                        32 => DynSolValue::FixedBytes(B256::from_slice(&bytes), 32),
623                        _ => DynSolValue::Bytes(bytes),
624                    });
625                }
626            }
627
628            // Handle large numbers that were potentially encoded as strings because they exceed the
629            // capacity of a 64-bit integer.
630            // Note that number-like strings that *could* fit in an `i64`/`u64` will fall through
631            // and be treated as literal strings.
632            if let Ok(n) = string.parse::<I256>()
633                && i64::try_from(n).is_err()
634            {
635                return Ok(DynSolValue::Int(n, 256));
636            } else if let Ok(n) = string.parse::<U256>()
637                && u64::try_from(n).is_err()
638            {
639                return Ok(DynSolValue::Uint(n, 256));
640            }
641
642            // Otherwise, treat as a regular string
643            Ok(DynSolValue::String(string.to_owned()))
644        }
645    }
646}
647
648/// Serializes a key:value pair to a specific object. If the key is valueKey, the value is
649/// expected to be an object, which will be set as the root object for the provided object key,
650/// overriding the whole root object if the object key already exists. By calling this function
651/// multiple times, the user can serialize multiple KV pairs to the same object. The value can be of
652/// any type, even a new object in itself. The function will return a stringified version of the
653/// object, so that the user can use that as a value to a new invocation of the same function with a
654/// new object key. This enables the user to reuse the same function to crate arbitrarily complex
655/// object structures (JSON).
656fn serialize_json<FEN: FoundryEvmNetwork>(
657    state: &mut Cheatcodes<FEN>,
658    object_key: &str,
659    value_key: &str,
660    value: DynSolValue,
661) -> Result {
662    let value = foundry_common::fmt::serialize_value_as_json(value, state.struct_defs())?;
663    let map = state.serialized_jsons.entry(object_key.into()).or_default();
664    map.insert(value_key.into(), value);
665    let stringified = serde_json::to_string(map).unwrap();
666    Ok(stringified.abi_encode())
667}
668
669/// Resolves a [DynSolType] from user input.
670pub(super) fn resolve_type(
671    type_description: &str,
672    struct_defs: Option<&StructDefinitions>,
673) -> Result<DynSolType> {
674    let ordered_ty = |ty| -> Result<DynSolType> {
675        if let Some(defs) = struct_defs { reorder_type(ty, defs) } else { Ok(ty) }
676    };
677
678    if let Ok(ty) = DynSolType::parse(type_description) {
679        return ordered_ty(ty);
680    };
681
682    if let Ok(encoded) = EncodeType::parse(type_description) {
683        let main_type = encoded.types[0].type_name;
684        let mut resolver = Resolver::default();
685        for t in &encoded.types {
686            resolver.ingest(t.to_owned());
687        }
688
689        // Get the alphabetically-sorted type from the resolver, and reorder if necessary.
690        return ordered_ty(resolver.resolve(main_type)?);
691    }
692
693    bail!("type description should be a valid Solidity type or a EIP712 `encodeType` string")
694}
695
696/// Upserts a value into a JSON object based on a dot-separated key.
697///
698/// This function navigates through a mutable `serde_json::Value` object using a
699/// path-like key. It creates nested JSON objects if they do not exist along the path.
700/// The value is inserted at the final key in the path.
701///
702/// # Arguments
703///
704/// * `data` - A mutable reference to the `serde_json::Value` to be modified.
705/// * `value` - The string representation of the value to upsert. This string is first parsed as
706///   JSON, and if that fails, it's treated as a plain JSON string.
707/// * `key` - A dot-separated string representing the path to the location for upserting.
708pub(super) fn upsert_json_value(data: &mut Value, value: &str, key: &str) -> Result<()> {
709    // Parse the path key into segments.
710    let canonical_key = canonicalize_json_path(key);
711    let parts: Vec<&str> = canonical_key
712        .strip_prefix("$.")
713        .unwrap_or(key)
714        .split('.')
715        .filter(|s| !s.is_empty())
716        .collect();
717
718    if parts.is_empty() {
719        return Err(fmt_err!("'valueKey' cannot be empty or just '$'"));
720    }
721
722    // Separate the final key from the path.
723    // Traverse the objects, creating intermediary ones if necessary.
724    if let Some((key_to_insert, path_to_parent)) = parts.split_last() {
725        let mut current_level = data;
726
727        for segment in path_to_parent {
728            if !current_level.is_object() {
729                return Err(fmt_err!("path segment '{segment}' does not resolve to an object."));
730            }
731            current_level = current_level
732                .as_object_mut()
733                .unwrap()
734                .entry(segment.to_string())
735                .or_insert(Value::Object(Map::new()));
736        }
737
738        // Upsert the new value
739        if let Some(parent_obj) = current_level.as_object_mut() {
740            parent_obj.insert(
741                key_to_insert.to_string(),
742                serde_json::from_str(value).unwrap_or_else(|_| Value::String(value.to_owned())),
743            );
744        } else {
745            return Err(fmt_err!("final destination is not an object, cannot insert key."));
746        }
747    }
748
749    Ok(())
750}
751
752/// Recursively traverses a `DynSolType` and reorders the fields of any
753/// `CustomStruct` variants according to the provided `StructDefinitions`.
754///
755/// This is necessary because the EIP-712 resolver sorts struct fields alphabetically,
756/// but we want to respect the order defined in the Solidity source code.
757fn reorder_type(ty: DynSolType, struct_defs: &StructDefinitions) -> Result<DynSolType> {
758    match ty {
759        DynSolType::CustomStruct { name, prop_names, tuple } => {
760            if let Some(def) = struct_defs.get(&name)? {
761                // The incoming `prop_names` and `tuple` are alphabetically sorted.
762                let type_map: std::collections::HashMap<String, DynSolType> =
763                    prop_names.into_iter().zip(tuple).collect();
764
765                let mut sorted_props = Vec::with_capacity(def.len());
766                let mut sorted_tuple = Vec::with_capacity(def.len());
767                for (field_name, _) in def {
768                    sorted_props.push(field_name.clone());
769                    if let Some(field_ty) = type_map.get(field_name) {
770                        sorted_tuple.push(reorder_type(field_ty.clone(), struct_defs)?);
771                    } else {
772                        bail!(
773                            "mismatch between struct definition and type description: field '{field_name}' not found in provided type for struct '{name}'"
774                        );
775                    }
776                }
777                Ok(DynSolType::CustomStruct { name, prop_names: sorted_props, tuple: sorted_tuple })
778            } else {
779                // No definition found, so we can't reorder. However, we still reorder its children
780                // in case they have known structs.
781                let new_tuple = tuple
782                    .into_iter()
783                    .map(|t| reorder_type(t, struct_defs))
784                    .collect::<Result<Vec<_>>>()?;
785                Ok(DynSolType::CustomStruct { name, prop_names, tuple: new_tuple })
786            }
787        }
788        DynSolType::Array(inner) => {
789            Ok(DynSolType::Array(Box::new(reorder_type(*inner, struct_defs)?)))
790        }
791        DynSolType::FixedArray(inner, len) => {
792            Ok(DynSolType::FixedArray(Box::new(reorder_type(*inner, struct_defs)?), len))
793        }
794        DynSolType::Tuple(inner) => Ok(DynSolType::Tuple(
795            inner.into_iter().map(|t| reorder_type(t, struct_defs)).collect::<Result<Vec<_>>>()?,
796        )),
797        _ => Ok(ty),
798    }
799}
800
801#[cfg(test)]
802mod tests {
803    use super::*;
804    use alloy_primitives::FixedBytes;
805    use foundry_common::fmt::{TypeDefMap, serialize_value_as_json};
806    use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
807    use std::collections::HashSet;
808
809    fn valid_value(value: &DynSolValue) -> bool {
810        (match value {
811            DynSolValue::String(s) if s == "{}" => false,
812
813            DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => false,
814
815            DynSolValue::Array(v) | DynSolValue::FixedArray(v) => v.iter().all(valid_value),
816            _ => true,
817        }) && value.as_type().is_some()
818    }
819
820    /// [DynSolValue::Bytes] of length 32 and 20 are converted to [DynSolValue::FixedBytes] and
821    /// [DynSolValue::Address] respectively. Thus, we can't distinguish between address and bytes of
822    /// length 20 during decoding. Because of that, there are issues with handling of arrays of
823    /// those types.
824    fn fixup_guessable(value: DynSolValue) -> DynSolValue {
825        match value {
826            DynSolValue::Array(mut v) | DynSolValue::FixedArray(mut v) => {
827                if let Some(DynSolValue::Bytes(_)) = v.first() {
828                    v.retain(|v| {
829                        let len = v.as_bytes().unwrap().len();
830                        len != 32 && len != 20
831                    })
832                }
833                DynSolValue::Array(v.into_iter().map(fixup_guessable).collect())
834            }
835            DynSolValue::FixedBytes(v, _) => DynSolValue::FixedBytes(v, 32),
836            DynSolValue::Bytes(v) if v.len() == 32 => {
837                DynSolValue::FixedBytes(FixedBytes::from_slice(&v), 32)
838            }
839            DynSolValue::Bytes(v) if v.len() == 20 => DynSolValue::Address(Address::from_slice(&v)),
840            _ => value,
841        }
842    }
843
844    fn guessable_types() -> impl proptest::strategy::Strategy<Value = DynSolValue> {
845        any::<DynSolValue>().prop_map(fixup_guessable).prop_filter("invalid value", valid_value)
846    }
847
848    /// A proptest strategy for generating a (simple) `DynSolValue::CustomStruct`
849    /// and its corresponding `StructDefinitions` object.
850    fn custom_struct_strategy() -> impl Strategy<Value = (StructDefinitions, DynSolValue)> {
851        // Define a strategy for basic field names and values.
852        let field_name_strat = "[a-z]{4,12}";
853        let field_value_strat = prop_oneof![
854            any::<bool>().prop_map(DynSolValue::Bool),
855            any::<u32>().prop_map(|v| DynSolValue::Uint(U256::from(v), 256)),
856            any::<[u8; 20]>().prop_map(Address::from).prop_map(DynSolValue::Address),
857            any::<[u8; 32]>().prop_map(B256::from).prop_map(|b| DynSolValue::FixedBytes(b, 32)),
858            ".*".prop_map(DynSolValue::String),
859        ];
860
861        // Combine them to create a list of unique fields that preserve the random order.
862        let fields_strat = proptest::collection::vec((field_name_strat, field_value_strat), 1..8)
863            .prop_map(|fields| {
864                let mut unique_fields = Vec::with_capacity(fields.len());
865                let mut seen_names = HashSet::new();
866                for (name, value) in fields {
867                    if seen_names.insert(name.clone()) {
868                        unique_fields.push((name, value));
869                    }
870                }
871                unique_fields
872            });
873
874        // Generate the `CustomStruct` and its definition.
875        ("[A-Z][a-z]{4,8}", fields_strat).prop_map(|(struct_name, fields)| {
876            let (prop_names, tuple): (Vec<String>, Vec<DynSolValue>) =
877                fields.clone().into_iter().unzip();
878            let def_fields: Vec<(String, String)> = fields
879                .iter()
880                .map(|(name, value)| (name.clone(), value.as_type().unwrap().to_string()))
881                .collect();
882            let mut defs_map = TypeDefMap::default();
883            defs_map.insert(struct_name.clone(), def_fields);
884            (defs_map.into(), DynSolValue::CustomStruct { name: struct_name, prop_names, tuple })
885        })
886    }
887
888    // Tests to ensure that conversion [DynSolValue] -> [serde_json::Value] -> [DynSolValue]
889    proptest::proptest! {
890        #[test]
891        fn test_json_roundtrip_guessed(v in guessable_types()) {
892            let json = serialize_value_as_json(v.clone(), None).unwrap();
893            let value = json_value_to_token(&json, None).unwrap();
894
895            // do additional abi_encode -> abi_decode to avoid zero signed integers getting decoded as unsigned and causing assert_eq to fail.
896            let decoded = v.as_type().unwrap().abi_decode(&value.abi_encode()).unwrap();
897            assert_eq!(decoded, v);
898        }
899
900        #[test]
901        fn test_json_roundtrip(v in any::<DynSolValue>().prop_filter("filter out values without type", |v| v.as_type().is_some())) {
902            let json = serialize_value_as_json(v.clone(), None).unwrap();
903            let value = parse_json_as(&json, &v.as_type().unwrap()).unwrap();
904            assert_eq!(value, v);
905        }
906
907        #[test]
908        fn test_json_roundtrip_with_struct_defs((struct_defs, v) in custom_struct_strategy()) {
909            let json = serialize_value_as_json(v.clone(), Some(&struct_defs)).unwrap();
910            let sol_type = v.as_type().unwrap();
911            let parsed_value = parse_json_as(&json, &sol_type).unwrap();
912            assert_eq!(parsed_value, v);
913        }
914    }
915
916    #[test]
917    fn test_resolve_type_with_definitions() -> Result<()> {
918        // Define a struct with fields in a specific order (not alphabetical)
919        let mut struct_defs = TypeDefMap::new();
920        struct_defs.insert(
921            "Apple".to_string(),
922            vec![
923                ("color".to_string(), "string".to_string()),
924                ("sweetness".to_string(), "uint8".to_string()),
925                ("sourness".to_string(), "uint8".to_string()),
926            ],
927        );
928        struct_defs.insert(
929            "FruitStall".to_string(),
930            vec![
931                ("name".to_string(), "string".to_string()),
932                ("apples".to_string(), "Apple[]".to_string()),
933            ],
934        );
935
936        // Simulate resolver output: type string, using alphabetical order for fields.
937        let ty_desc = "FruitStall(Apple[] apples,string name)Apple(string color,uint8 sourness,uint8 sweetness)";
938
939        // Resolve type and ensure struct definition order is preserved.
940        let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
941        if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
942            assert_eq!(name, "FruitStall");
943            assert_eq!(prop_names, vec!["name", "apples"]);
944            assert_eq!(tuple.len(), 2);
945            assert_eq!(tuple[0], DynSolType::String);
946
947            if let DynSolType::Array(apple_ty_boxed) = &tuple[1]
948                && let DynSolType::CustomStruct { name, prop_names, tuple } = &**apple_ty_boxed
949            {
950                assert_eq!(*name, "Apple");
951                // Check that the inner struct's fields are also in definition order.
952                assert_eq!(*prop_names, vec!["color", "sweetness", "sourness"]);
953                assert_eq!(
954                    *tuple,
955                    vec![DynSolType::String, DynSolType::Uint(8), DynSolType::Uint(8)]
956                );
957
958                return Ok(());
959            }
960        }
961        panic!("Expected FruitStall and Apple to be CustomStruct");
962    }
963
964    #[test]
965    fn test_resolve_type_without_definitions() -> Result<()> {
966        // Simulate resolver output: type string, using alphabetical order for fields.
967        let ty_desc = "Person(bool active,uint256 age,string name)";
968
969        // Resolve the type without providing any struct definitions and ensure that original
970        // (alphabetical) order is unchanged.
971        let ty = resolve_type(ty_desc, None).unwrap();
972        if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
973            assert_eq!(name, "Person");
974            assert_eq!(prop_names, vec!["active", "age", "name"]);
975            assert_eq!(tuple.len(), 3);
976            assert_eq!(tuple, vec![DynSolType::Bool, DynSolType::Uint(256), DynSolType::String]);
977            return Ok(());
978        }
979        panic!("Expected Person to be CustomStruct");
980    }
981
982    #[test]
983    fn test_resolve_type_for_array_of_structs() -> Result<()> {
984        // Define a struct with fields in a specific, non-alphabetical order.
985        let mut struct_defs = TypeDefMap::new();
986        struct_defs.insert(
987            "Item".to_string(),
988            vec![
989                ("name".to_string(), "string".to_string()),
990                ("price".to_string(), "uint256".to_string()),
991                ("id".to_string(), "uint256".to_string()),
992            ],
993        );
994
995        // Simulate resolver output: type string, using alphabetical order for fields.
996        let ty_desc = "Item(uint256 id,string name,uint256 price)";
997
998        // Resolve type and ensure struct definition order is preserved.
999        let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1000        let array_ty = DynSolType::Array(Box::new(ty));
1001        if let DynSolType::Array(item_ty) = array_ty
1002            && let DynSolType::CustomStruct { name, prop_names, tuple } = *item_ty
1003        {
1004            assert_eq!(name, "Item");
1005            assert_eq!(prop_names, vec!["name", "price", "id"]);
1006            assert_eq!(
1007                tuple,
1008                vec![DynSolType::String, DynSolType::Uint(256), DynSolType::Uint(256)]
1009            );
1010            return Ok(());
1011        }
1012        panic!("Expected CustomStruct in array");
1013    }
1014
1015    #[test]
1016    fn test_parse_json_missing_field() {
1017        // Define a struct with a specific field order.
1018        let mut struct_defs = TypeDefMap::new();
1019        struct_defs.insert(
1020            "Person".to_string(),
1021            vec![
1022                ("name".to_string(), "string".to_string()),
1023                ("age".to_string(), "uint256".to_string()),
1024            ],
1025        );
1026
1027        // JSON missing the "age" field
1028        let json_str = r#"{ "name": "Alice" }"#;
1029
1030        // Simulate resolver output: type string, using alphabetical order for fields.
1031        let type_description = "Person(uint256 age,string name)";
1032        let ty = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1033
1034        // Now, attempt to parse the incomplete JSON using the ordered type.
1035        let json_value: Value = serde_json::from_str(json_str).unwrap();
1036        let result = parse_json_as(&json_value, &ty);
1037
1038        // Should fail with a missing field error because `parse_json_map` requires all fields.
1039        assert!(result.is_err());
1040        assert!(result.unwrap_err().to_string().contains("field \"age\" not found in JSON object"));
1041    }
1042
1043    #[test]
1044    fn test_serialize_json_with_struct_def_order() {
1045        // Define a struct with a specific, non-alphabetical field order.
1046        let mut struct_defs = TypeDefMap::new();
1047        struct_defs.insert(
1048            "Item".to_string(),
1049            vec![
1050                ("name".to_string(), "string".to_string()),
1051                ("id".to_string(), "uint256".to_string()),
1052                ("active".to_string(), "bool".to_string()),
1053            ],
1054        );
1055
1056        // Create a DynSolValue instance for the struct.
1057        let item_struct = DynSolValue::CustomStruct {
1058            name: "Item".to_string(),
1059            prop_names: vec!["name".to_string(), "id".to_string(), "active".to_string()],
1060            tuple: vec![
1061                DynSolValue::String("Test Item".to_string()),
1062                DynSolValue::Uint(U256::from(123), 256),
1063                DynSolValue::Bool(true),
1064            ],
1065        };
1066
1067        // Serialize the value to JSON and verify that the order is preserved.
1068        let json_value = serialize_value_as_json(item_struct, Some(&struct_defs.into())).unwrap();
1069        let json_string = serde_json::to_string(&json_value).unwrap();
1070        assert_eq!(json_string, r#"{"name":"Test Item","id":123,"active":true}"#);
1071    }
1072
1073    #[test]
1074    fn test_json_full_cycle_typed_with_struct_defs() {
1075        // Define a struct with a specific, non-alphabetical field order.
1076        let mut struct_defs = TypeDefMap::new();
1077        struct_defs.insert(
1078            "Wallet".to_string(),
1079            vec![
1080                ("owner".to_string(), "address".to_string()),
1081                ("balance".to_string(), "uint256".to_string()),
1082                ("id".to_string(), "bytes32".to_string()),
1083            ],
1084        );
1085
1086        // Create the "original" DynSolValue instance.
1087        let owner_address = Address::from([1; 20]);
1088        let wallet_id = B256::from([2; 32]);
1089        let original_wallet = DynSolValue::CustomStruct {
1090            name: "Wallet".to_string(),
1091            prop_names: vec!["owner".to_string(), "balance".to_string(), "id".to_string()],
1092            tuple: vec![
1093                DynSolValue::Address(owner_address),
1094                DynSolValue::Uint(U256::from(5000), 256),
1095                DynSolValue::FixedBytes(wallet_id, 32),
1096            ],
1097        };
1098
1099        // Serialize it. The resulting JSON should respect the struct definition order.
1100        let json_value =
1101            serialize_value_as_json(original_wallet.clone(), Some(&struct_defs.clone().into()))
1102                .unwrap();
1103        let json_string = serde_json::to_string(&json_value).unwrap();
1104        assert_eq!(
1105            json_string,
1106            format!(r#"{{"owner":"{owner_address}","balance":5000,"id":"{wallet_id}"}}"#)
1107        );
1108
1109        // Resolve the type, which should also respect the struct definition order.
1110        let type_description = "Wallet(uint256 balance,bytes32 id,address owner)";
1111        let resolved_type = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1112
1113        // Parse the JSON using the correctly ordered resolved type. Ensure that it is identical to
1114        // the original one.
1115        let parsed_value = parse_json_as(&json_value, &resolved_type).unwrap();
1116        assert_eq!(parsed_value, original_wallet);
1117    }
1118}