1use 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 parseJsonArrayLengthCall {
115 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
116 let Self { json, key } = self;
117 parse_json_array_length(json, key)
118 }
119}
120
121impl Cheatcode for parseJsonBytesCall {
122 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
123 let Self { json, key } = self;
124 parse_json_coerce(json, key, &DynSolType::Bytes)
125 }
126}
127
128impl Cheatcode for parseJsonBytesArrayCall {
129 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
130 let Self { json, key } = self;
131 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bytes)))
132 }
133}
134
135impl Cheatcode for parseJsonBytes32Call {
136 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
137 let Self { json, key } = self;
138 parse_json_coerce(json, key, &DynSolType::FixedBytes(32))
139 }
140}
141
142impl Cheatcode for parseJsonBytes32ArrayCall {
143 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
144 let Self { json, key } = self;
145 parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32))))
146 }
147}
148
149impl Cheatcode for parseJsonType_0Call {
150 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
151 let Self { json, typeDescription } = self;
152 parse_json_coerce(json, "$", &resolve_type(typeDescription, state.struct_defs())?)
153 .map(|v| v.abi_encode())
154 }
155}
156
157impl Cheatcode for parseJsonType_1Call {
158 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
159 let Self { json, key, typeDescription } = self;
160 parse_json_coerce(json, key, &resolve_type(typeDescription, state.struct_defs())?)
161 .map(|v| v.abi_encode())
162 }
163}
164
165impl Cheatcode for parseJsonTypeArrayCall {
166 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
167 let Self { json, key, typeDescription } = self;
168 let ty = resolve_type(typeDescription, state.struct_defs())?;
169 parse_json_coerce(json, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
170 }
171}
172
173impl Cheatcode for parseJsonKeysCall {
174 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
175 let Self { json, key } = self;
176 parse_json_keys(json, key)
177 }
178}
179
180impl Cheatcode for serializeJsonCall {
181 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
182 let Self { objectKey, value } = self;
183 *state.serialized_jsons.entry(objectKey.into()).or_default() = serde_json::from_str(value)?;
184 Ok(value.abi_encode())
185 }
186}
187
188impl Cheatcode for serializeBool_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 serializeUint_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 serializeInt_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 serializeAddress_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, (*value).into())
213 }
214}
215
216impl Cheatcode for serializeBytes32_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, DynSolValue::FixedBytes(*value, 32))
220 }
221}
222
223impl Cheatcode for serializeString_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.clone().into())
227 }
228}
229
230impl Cheatcode for serializeBytes_0Call {
231 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
232 let Self { objectKey, valueKey, value } = self;
233 serialize_json(state, objectKey, valueKey, value.to_vec().into())
234 }
235}
236
237impl Cheatcode for serializeBool_1Call {
238 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
239 let Self { objectKey, valueKey, values } = self;
240 serialize_json(
241 state,
242 objectKey,
243 valueKey,
244 DynSolValue::Array(values.iter().copied().map(DynSolValue::Bool).collect()),
245 )
246 }
247}
248
249impl Cheatcode for serializeUint_1Call {
250 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
251 let Self { objectKey, valueKey, values } = self;
252 serialize_json(
253 state,
254 objectKey,
255 valueKey,
256 DynSolValue::Array(values.iter().map(|v| DynSolValue::Uint(*v, 256)).collect()),
257 )
258 }
259}
260
261impl Cheatcode for serializeInt_1Call {
262 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
263 let Self { objectKey, valueKey, values } = self;
264 serialize_json(
265 state,
266 objectKey,
267 valueKey,
268 DynSolValue::Array(values.iter().map(|v| DynSolValue::Int(*v, 256)).collect()),
269 )
270 }
271}
272
273impl Cheatcode for serializeAddress_1Call {
274 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
275 let Self { objectKey, valueKey, values } = self;
276 serialize_json(
277 state,
278 objectKey,
279 valueKey,
280 DynSolValue::Array(values.iter().copied().map(DynSolValue::Address).collect()),
281 )
282 }
283}
284
285impl Cheatcode for serializeBytes32_1Call {
286 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
287 let Self { objectKey, valueKey, values } = self;
288 serialize_json(
289 state,
290 objectKey,
291 valueKey,
292 DynSolValue::Array(values.iter().map(|v| DynSolValue::FixedBytes(*v, 32)).collect()),
293 )
294 }
295}
296
297impl Cheatcode for serializeString_1Call {
298 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
299 let Self { objectKey, valueKey, values } = self;
300 serialize_json(
301 state,
302 objectKey,
303 valueKey,
304 DynSolValue::Array(values.iter().cloned().map(DynSolValue::String).collect()),
305 )
306 }
307}
308
309impl Cheatcode for serializeBytes_1Call {
310 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
311 let Self { objectKey, valueKey, values } = self;
312 serialize_json(
313 state,
314 objectKey,
315 valueKey,
316 DynSolValue::Array(
317 values.iter().cloned().map(Into::into).map(DynSolValue::Bytes).collect(),
318 ),
319 )
320 }
321}
322
323impl Cheatcode for serializeJsonType_0Call {
324 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
325 let Self { typeDescription, value } = self;
326 let ty = resolve_type(typeDescription, state.struct_defs())?;
327 let value = ty.abi_decode(value)?;
328 let value =
329 foundry_common::fmt::serialize_value_as_json(value, state.struct_defs(), false)?;
330 Ok(value.to_string().abi_encode())
331 }
332}
333
334impl Cheatcode for serializeJsonType_1Call {
335 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
336 let Self { objectKey, valueKey, typeDescription, value } = self;
337 let ty = resolve_type(typeDescription, state.struct_defs())?;
338 let value = ty.abi_decode(value)?;
339 serialize_json(state, objectKey, valueKey, value)
340 }
341}
342
343impl Cheatcode for serializeUintToHexCall {
344 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
345 let Self { objectKey, valueKey, value } = self;
346 let hex = format!("0x{value:x}");
347 serialize_json(state, objectKey, valueKey, hex.into())
348 }
349}
350
351impl Cheatcode for writeJson_0Call {
352 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
353 let Self { json, path } = self;
354 let json = serde_json::from_str(json).unwrap_or_else(|_| Value::String(json.to_owned()));
355 let json_string = serde_json::to_string_pretty(&json)?;
356 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
357 }
358}
359
360impl Cheatcode for writeJson_1Call {
361 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
362 let Self { json: value, path, valueKey } = self;
363
364 let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
367 let mut data = if data_path.exists() {
368 let data_string = fs::locked_read_to_string(&data_path)?;
369 serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string))
370 } else {
371 Value::Object(Default::default())
372 };
373 upsert_json_value(&mut data, value, valueKey)?;
374
375 let json_string = serde_json::to_string_pretty(&data)?;
377 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
378 }
379}
380
381pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result {
382 let json = parse_json_str(json)?;
383 let values = select(&json, key)?;
384 let exists = !values.is_empty();
385 Ok(exists.abi_encode())
386}
387
388pub(super) fn parse_json(json: &str, path: &str, defs: Option<&StructDefinitions>) -> Result {
389 let value = parse_json_str(json)?;
390 let selected = select(&value, path)?;
391 let sol = json_to_sol(defs, &selected)?;
392 Ok(encode(sol))
393}
394
395pub(super) fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result {
396 let json = parse_json_str(json)?;
397 let [value] = select(&json, path)?[..] else {
398 bail!("path {path:?} must return exactly one JSON value");
399 };
400
401 parse_json_as(value, ty).map(|v| v.abi_encode())
402}
403
404pub(super) fn parse_json_as(value: &Value, ty: &DynSolType) -> Result<DynSolValue> {
406 let to_string = |v: &Value| {
407 let mut s = v.to_string();
408 s.retain(|c: char| c != '"');
409 s
410 };
411
412 match (value, ty) {
413 (Value::Array(array), ty) => parse_json_array(array, ty),
414 (Value::Object(object), ty) => parse_json_map(object, ty),
415 (Value::String(s), DynSolType::String) => Ok(DynSolValue::String(s.clone())),
416 (Value::String(s), DynSolType::Uint(_) | DynSolType::Int(_)) => string::parse_value(s, ty),
417 _ => string::parse_value(&to_string(value), ty),
418 }
419}
420
421pub(super) fn parse_json_array(array: &[Value], ty: &DynSolType) -> Result<DynSolValue> {
422 match ty {
423 DynSolType::Tuple(types) => {
424 ensure!(array.len() == types.len(), "array length mismatch");
425 let values = array
426 .iter()
427 .zip(types)
428 .map(|(e, ty)| parse_json_as(e, ty))
429 .collect::<Result<Vec<_>>>()?;
430
431 Ok(DynSolValue::Tuple(values))
432 }
433 DynSolType::Array(inner) => {
434 let values =
435 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
436 Ok(DynSolValue::Array(values))
437 }
438 DynSolType::FixedArray(inner, len) => {
439 ensure!(array.len() == *len, "array length mismatch");
440 let values =
441 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
442 Ok(DynSolValue::FixedArray(values))
443 }
444 _ => bail!("expected {ty}, found array"),
445 }
446}
447
448pub(super) fn parse_json_map(map: &Map<String, Value>, ty: &DynSolType) -> Result<DynSolValue> {
449 let Some((name, fields, types)) = ty.as_custom_struct() else {
450 bail!("expected {ty}, found JSON object");
451 };
452
453 let mut values = Vec::with_capacity(fields.len());
454 for (field, ty) in fields.iter().zip(types.iter()) {
455 let Some(value) = map.get(field) else { bail!("field {field:?} not found in JSON object") };
456 values.push(parse_json_as(value, ty)?);
457 }
458
459 Ok(DynSolValue::CustomStruct {
460 name: name.to_string(),
461 prop_names: fields.to_vec(),
462 tuple: values,
463 })
464}
465
466pub(super) fn parse_json_keys(json: &str, key: &str) -> Result {
467 let json = parse_json_str(json)?;
468 let values = select(&json, key)?;
469 let [value] = values[..] else {
470 bail!("key {key:?} must return exactly one JSON object");
471 };
472 let Value::Object(object) = value else {
473 bail!("JSON value at {key:?} is not an object");
474 };
475 let keys = object.keys().collect::<Vec<_>>();
476 Ok(keys.abi_encode())
477}
478
479pub(super) fn parse_json_array_length(json: &str, key: &str) -> Result {
480 let json = parse_json_str(json)?;
481 let values = select(&json, key)?;
482 let [value] = values[..] else {
483 bail!("key {key:?} must return exactly one JSON array");
484 };
485 let Value::Array(array) = value else {
486 bail!("JSON value at {key:?} is not an array");
487 };
488 Ok(U256::from(array.len()).abi_encode())
489}
490
491fn parse_json_str(json: &str) -> Result<Value> {
492 serde_json::from_str(json).map_err(|e| fmt_err!("failed parsing JSON: {e}"))
493}
494
495fn json_to_sol(defs: Option<&StructDefinitions>, json: &[&Value]) -> Result<Vec<DynSolValue>> {
496 let mut sol = Vec::with_capacity(json.len());
497 for value in json {
498 sol.push(json_value_to_token(value, defs)?);
499 }
500 Ok(sol)
501}
502
503fn select<'a>(value: &'a Value, mut path: &str) -> Result<Vec<&'a Value>> {
504 if path == "." {
506 path = "$";
507 }
508 jsonpath_lib::select(value, &canonicalize_json_path(path))
510 .map_err(|e| fmt_err!("failed selecting from JSON: {:?}", e.to_string()))
511}
512
513fn encode(values: Vec<DynSolValue>) -> Vec<u8> {
514 let bytes = match &values[..] {
516 [] => Vec::new(),
517 [one] => one.abi_encode(),
518 _ => DynSolValue::Array(values).abi_encode(),
519 };
520 bytes.abi_encode()
521}
522
523pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> {
526 if path.starts_with('$') { path.into() } else { format!("${path}").into() }
527}
528
529#[instrument(target = "cheatcodes", level = "trace", ret)]
536pub(super) fn json_value_to_token(
537 value: &Value,
538 defs: Option<&StructDefinitions>,
539) -> Result<DynSolValue> {
540 if let Some(defs) = defs {
541 _json_value_to_token(value, defs)
542 } else {
543 _json_value_to_token(value, &StructDefinitions::default())
544 }
545}
546
547fn _json_value_to_token(value: &Value, defs: &StructDefinitions) -> Result<DynSolValue> {
548 match value {
549 Value::Null => Ok(DynSolValue::FixedBytes(B256::ZERO, 32)),
550 Value::Bool(boolean) => Ok(DynSolValue::Bool(*boolean)),
551 Value::Array(array) => array
552 .iter()
553 .map(|v| _json_value_to_token(v, defs))
554 .collect::<Result<_>>()
555 .map(DynSolValue::Array),
556 Value::Object(map) => {
557 let keys: BTreeSet<_> = map.keys().map(|s| s.as_str()).collect();
559 let matching_defs = defs
560 .values()
561 .filter(|fields| {
562 fields.len() == keys.len()
563 && fields.iter().map(|(name, _)| name.as_str()).collect::<BTreeSet<_>>()
564 == keys
565 })
566 .collect::<Vec<_>>();
567
568 if let Some(fields) = matching_defs.first() {
569 fields
571 .iter()
572 .map(|(name, type_description)| {
573 let value = map.get(name).unwrap();
575 let unambiguous = matching_defs.iter().all(|fields| {
576 fields
577 .iter()
578 .find(|(field, _)| field == name)
579 .is_some_and(|(_, ty)| ty == type_description)
580 });
581 if unambiguous
582 && let Some(parsed) = parse_fixed_array(value, type_description, defs)
583 {
584 parsed
585 } else {
586 _json_value_to_token(value, defs)
587 }
588 })
589 .collect::<Result<_>>()
590 .map(DynSolValue::Tuple)
591 } else {
592 let ordered_object: BTreeMap<_, _> =
595 map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
596 ordered_object
597 .values()
598 .map(|value| _json_value_to_token(value, defs))
599 .collect::<Result<_>>()
600 .map(DynSolValue::Tuple)
601 }
602 }
603 Value::Number(number) => {
604 if let Some(f) = number.as_f64() {
605 if f.fract() == 0.0 {
608 let s = number.to_string();
614
615 if s.contains('e') {
620 let fallback_s = f.to_string();
626 if let Ok(n) = fallback_s.parse() {
627 return Ok(DynSolValue::Uint(n, 256));
628 }
629 if let Ok(n) = I256::from_dec_str(&fallback_s) {
630 return Ok(DynSolValue::Int(n, 256));
631 }
632 }
633
634 if let Ok(n) = s.parse() {
635 return Ok(DynSolValue::Uint(n, 256));
636 }
637 if let Ok(n) = s.parse() {
638 return Ok(DynSolValue::Int(n, 256));
639 }
640 }
641 }
642
643 Err(fmt_err!("unsupported JSON number: {number}"))
644 }
645 Value::String(string) => {
646 if let Some(mut val) = string.strip_prefix("0x") {
648 let s;
649 if val.len() == 39 {
650 return Err(format!("Cannot parse \"{val}\" as an address. If you want to specify address, prepend zero to the value.").into());
651 }
652 if !val.len().is_multiple_of(2) {
653 s = format!("0{val}");
654 val = &s[..];
655 }
656 if let Ok(bytes) = hex::decode(val) {
657 return Ok(match bytes.len() {
658 20 => DynSolValue::Address(Address::from_slice(&bytes)),
659 32 => DynSolValue::FixedBytes(B256::from_slice(&bytes), 32),
660 _ => DynSolValue::Bytes(bytes),
661 });
662 }
663 }
664
665 if let Ok(n) = string.parse::<I256>()
670 && i64::try_from(n).is_err()
671 {
672 return Ok(DynSolValue::Int(n, 256));
673 } else if let Ok(n) = string.parse::<U256>()
674 && u64::try_from(n).is_err()
675 {
676 return Ok(DynSolValue::Uint(n, 256));
677 }
678
679 Ok(DynSolValue::String(string.to_owned()))
681 }
682 }
683}
684
685fn parse_fixed_array(
686 value: &Value,
687 type_description: &str,
688 defs: &StructDefinitions,
689) -> Option<Result<DynSolValue>> {
690 let root_end = type_description.find('[')?;
691 let root = &type_description[..root_end];
692 let (ty, custom) = match defs.get(root) {
693 Ok(Some(_)) => {
694 (DynSolType::parse(&format!("bool{}", &type_description[root_end..])).ok()?, true)
695 }
696 Ok(None) if root.contains('.') => return None,
697 Ok(None) => (DynSolType::parse(type_description).ok()?, false),
698 Err(_) => return None,
699 };
700 if !contains_fixed_array(&ty) {
701 return None;
702 }
703
704 Some(if custom { parse_json_custom_array(value, &ty, defs) } else { parse_json_as(value, &ty) })
705}
706
707fn parse_json_custom_array(
708 value: &Value,
709 ty: &DynSolType,
710 defs: &StructDefinitions,
711) -> Result<DynSolValue> {
712 match (value, ty) {
713 (Value::Array(values), DynSolType::Array(inner)) => values
714 .iter()
715 .map(|value| parse_json_custom_array(value, inner, defs))
716 .collect::<Result<_>>()
717 .map(DynSolValue::Array),
718 (Value::Array(values), DynSolType::FixedArray(inner, len)) => {
719 ensure!(values.len() == *len, "array length mismatch");
720 values
721 .iter()
722 .map(|value| parse_json_custom_array(value, inner, defs))
723 .collect::<Result<_>>()
724 .map(DynSolValue::FixedArray)
725 }
726 (_, DynSolType::Bool) => _json_value_to_token(value, defs),
727 _ => bail!("expected array"),
728 }
729}
730
731fn contains_fixed_array(ty: &DynSolType) -> bool {
732 match ty {
733 DynSolType::FixedArray(_, _) => true,
734 DynSolType::Array(inner) => contains_fixed_array(inner),
735 _ => false,
736 }
737}
738
739fn serialize_json<FEN: FoundryEvmNetwork>(
748 state: &mut Cheatcodes<FEN>,
749 object_key: &str,
750 value_key: &str,
751 value: DynSolValue,
752) -> Result {
753 let value = foundry_common::fmt::serialize_value_as_json(value, state.struct_defs(), false)?;
754 let map = state.serialized_jsons.entry(object_key.into()).or_default();
755 map.insert(value_key.into(), value);
756 let stringified = serde_json::to_string(map).unwrap();
757 Ok(stringified.abi_encode())
758}
759
760pub(super) fn resolve_type(
762 type_description: &str,
763 struct_defs: Option<&StructDefinitions>,
764) -> Result<DynSolType> {
765 let ordered_ty = |ty| -> Result<DynSolType> {
766 if let Some(defs) = struct_defs { reorder_type(ty, defs) } else { Ok(ty) }
767 };
768
769 if let Ok(ty) = DynSolType::parse(type_description) {
770 return ordered_ty(ty);
771 };
772
773 if let Ok(encoded) = EncodeType::parse(type_description) {
774 let main_type = encoded.types[0].type_name;
775 let mut resolver = Resolver::default();
776 for t in &encoded.types {
777 resolver.ingest(t.to_owned());
778 }
779
780 return ordered_ty(resolver.resolve(main_type)?);
782 }
783
784 bail!("type description should be a valid Solidity type or a EIP712 `encodeType` string")
785}
786
787pub(super) fn upsert_json_value(data: &mut Value, value: &str, key: &str) -> Result<()> {
800 let canonical_key = canonicalize_json_path(key);
802 let parts: Vec<&str> = canonical_key
803 .strip_prefix("$.")
804 .unwrap_or(key)
805 .split('.')
806 .filter(|s| !s.is_empty())
807 .collect();
808
809 if parts.is_empty() {
810 return Err(fmt_err!("'valueKey' cannot be empty or just '$'"));
811 }
812
813 if let Some((key_to_insert, path_to_parent)) = parts.split_last() {
816 let mut current_level = data;
817
818 for segment in path_to_parent {
819 if !current_level.is_object() {
820 return Err(fmt_err!("path segment '{segment}' does not resolve to an object."));
821 }
822 current_level = current_level
823 .as_object_mut()
824 .unwrap()
825 .entry(segment.to_string())
826 .or_insert(Value::Object(Map::new()));
827 }
828
829 if let Some(parent_obj) = current_level.as_object_mut() {
831 parent_obj.insert(
832 key_to_insert.to_string(),
833 serde_json::from_str(value).unwrap_or_else(|_| Value::String(value.to_owned())),
834 );
835 } else {
836 return Err(fmt_err!("final destination is not an object, cannot insert key."));
837 }
838 }
839
840 Ok(())
841}
842
843fn reorder_type(ty: DynSolType, struct_defs: &StructDefinitions) -> Result<DynSolType> {
849 match ty {
850 DynSolType::CustomStruct { name, prop_names, tuple } => {
851 if let Some(def) = struct_defs.get(&name)? {
852 let type_map: std::collections::HashMap<String, DynSolType> =
854 prop_names.into_iter().zip(tuple).collect();
855
856 let mut sorted_props = Vec::with_capacity(def.len());
857 let mut sorted_tuple = Vec::with_capacity(def.len());
858 for (field_name, _) in def {
859 sorted_props.push(field_name.clone());
860 if let Some(field_ty) = type_map.get(field_name) {
861 sorted_tuple.push(reorder_type(field_ty.clone(), struct_defs)?);
862 } else {
863 bail!(
864 "mismatch between struct definition and type description: field '{field_name}' not found in provided type for struct '{name}'"
865 );
866 }
867 }
868 Ok(DynSolType::CustomStruct { name, prop_names: sorted_props, tuple: sorted_tuple })
869 } else {
870 let new_tuple = tuple
873 .into_iter()
874 .map(|t| reorder_type(t, struct_defs))
875 .collect::<Result<Vec<_>>>()?;
876 Ok(DynSolType::CustomStruct { name, prop_names, tuple: new_tuple })
877 }
878 }
879 DynSolType::Array(inner) => {
880 Ok(DynSolType::Array(Box::new(reorder_type(*inner, struct_defs)?)))
881 }
882 DynSolType::FixedArray(inner, len) => {
883 Ok(DynSolType::FixedArray(Box::new(reorder_type(*inner, struct_defs)?), len))
884 }
885 DynSolType::Tuple(inner) => Ok(DynSolType::Tuple(
886 inner.into_iter().map(|t| reorder_type(t, struct_defs)).collect::<Result<Vec<_>>>()?,
887 )),
888 _ => Ok(ty),
889 }
890}
891
892#[cfg(test)]
893mod tests {
894 use super::*;
895 use alloy_primitives::FixedBytes;
896 use foundry_common::fmt::{TypeDefMap, serialize_value_as_json};
897 use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
898 use std::collections::HashSet;
899
900 fn valid_value(value: &DynSolValue) -> bool {
901 (match value {
902 DynSolValue::String(s) if s == "{}" => false,
903
904 DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => false,
905
906 DynSolValue::Array(v) | DynSolValue::FixedArray(v) => v.iter().all(valid_value),
907 _ => true,
908 }) && value.as_type().is_some()
909 }
910
911 fn fixup_guessable(value: DynSolValue) -> DynSolValue {
916 match value {
917 DynSolValue::Array(mut v) | DynSolValue::FixedArray(mut v) => {
918 if let Some(DynSolValue::Bytes(_)) = v.first() {
919 v.retain(|v| {
920 let len = v.as_bytes().unwrap().len();
921 len != 32 && len != 20
922 })
923 }
924 DynSolValue::Array(v.into_iter().map(fixup_guessable).collect())
925 }
926 DynSolValue::FixedBytes(v, _) => DynSolValue::FixedBytes(v, 32),
927 DynSolValue::Bytes(v) if v.len() == 32 => {
928 DynSolValue::FixedBytes(FixedBytes::from_slice(&v), 32)
929 }
930 DynSolValue::Bytes(v) if v.len() == 20 => DynSolValue::Address(Address::from_slice(&v)),
931 _ => value,
932 }
933 }
934
935 fn guessable_types() -> impl proptest::strategy::Strategy<Value = DynSolValue> {
936 any::<DynSolValue>().prop_map(fixup_guessable).prop_filter("invalid value", valid_value)
937 }
938
939 fn custom_struct_strategy() -> impl Strategy<Value = (StructDefinitions, DynSolValue)> {
942 let field_name_strat = "[a-z]{4,12}";
944 let field_value_strat = prop_oneof![
945 any::<bool>().prop_map(DynSolValue::Bool),
946 any::<u32>().prop_map(|v| DynSolValue::Uint(U256::from(v), 256)),
947 any::<[u8; 20]>().prop_map(Address::from).prop_map(DynSolValue::Address),
948 any::<[u8; 32]>().prop_map(B256::from).prop_map(|b| DynSolValue::FixedBytes(b, 32)),
949 ".*".prop_filter("invalid string value", |s| s != "{}").prop_map(DynSolValue::String),
950 ];
951
952 let fields_strat = proptest::collection::vec((field_name_strat, field_value_strat), 1..8)
954 .prop_map(|fields| {
955 let mut unique_fields = Vec::with_capacity(fields.len());
956 let mut seen_names = HashSet::new();
957 for (name, value) in fields {
958 if seen_names.insert(name.clone()) {
959 unique_fields.push((name, value));
960 }
961 }
962 unique_fields
963 });
964
965 ("[A-Z][a-z]{4,8}", fields_strat).prop_map(|(struct_name, fields)| {
967 let (prop_names, tuple): (Vec<String>, Vec<DynSolValue>) =
968 fields.clone().into_iter().unzip();
969 let def_fields: Vec<(String, String)> = fields
970 .iter()
971 .map(|(name, value)| (name.clone(), value.as_type().unwrap().to_string()))
972 .collect();
973 let mut defs_map = TypeDefMap::default();
974 defs_map.insert(struct_name.clone(), def_fields);
975 (defs_map.into(), DynSolValue::CustomStruct { name: struct_name, prop_names, tuple })
976 })
977 }
978
979 proptest::proptest! {
981 #[test]
982 fn test_json_roundtrip_guessed(v in guessable_types()) {
983 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
984 let value = json_value_to_token(&json, None).unwrap();
985
986 let decoded = v.as_type().unwrap().abi_decode(&value.abi_encode()).unwrap();
988 assert_eq!(decoded, v);
989 }
990
991 #[test]
992 fn test_json_roundtrip(v in any::<DynSolValue>().prop_filter("filter out values without type", |v| v.as_type().is_some())) {
993 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
994 let value = parse_json_as(&json, &v.as_type().unwrap()).unwrap();
995 assert_eq!(value, v);
996 }
997
998 #[test]
999 fn test_json_roundtrip_with_struct_defs((struct_defs, v) in custom_struct_strategy()) {
1000 let json = serialize_value_as_json(v.clone(), Some(&struct_defs), false).unwrap();
1001 let sol_type = v.as_type().unwrap();
1002 let parsed_value = parse_json_as(&json, &sol_type).unwrap();
1003 assert_eq!(parsed_value, v);
1004 }
1005 }
1006
1007 #[test]
1008 fn test_resolve_type_with_definitions() -> Result<()> {
1009 let mut struct_defs = TypeDefMap::new();
1011 struct_defs.insert(
1012 "Apple".to_string(),
1013 vec![
1014 ("color".to_string(), "string".to_string()),
1015 ("sweetness".to_string(), "uint8".to_string()),
1016 ("sourness".to_string(), "uint8".to_string()),
1017 ],
1018 );
1019 struct_defs.insert(
1020 "FruitStall".to_string(),
1021 vec![
1022 ("name".to_string(), "string".to_string()),
1023 ("apples".to_string(), "Apple[]".to_string()),
1024 ],
1025 );
1026
1027 let ty_desc = "FruitStall(Apple[] apples,string name)Apple(string color,uint8 sourness,uint8 sweetness)";
1029
1030 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1032 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1033 assert_eq!(name, "FruitStall");
1034 assert_eq!(prop_names, vec!["name", "apples"]);
1035 assert_eq!(tuple.len(), 2);
1036 assert_eq!(tuple[0], DynSolType::String);
1037
1038 if let DynSolType::Array(apple_ty_boxed) = &tuple[1]
1039 && let DynSolType::CustomStruct { name, prop_names, tuple } = &**apple_ty_boxed
1040 {
1041 assert_eq!(*name, "Apple");
1042 assert_eq!(*prop_names, vec!["color", "sweetness", "sourness"]);
1044 assert_eq!(
1045 *tuple,
1046 vec![DynSolType::String, DynSolType::Uint(8), DynSolType::Uint(8)]
1047 );
1048
1049 return Ok(());
1050 }
1051 }
1052 panic!("Expected FruitStall and Apple to be CustomStruct");
1053 }
1054
1055 #[test]
1056 fn test_resolve_type_without_definitions() -> Result<()> {
1057 let ty_desc = "Person(bool active,uint256 age,string name)";
1059
1060 let ty = resolve_type(ty_desc, None).unwrap();
1063 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1064 assert_eq!(name, "Person");
1065 assert_eq!(prop_names, vec!["active", "age", "name"]);
1066 assert_eq!(tuple.len(), 3);
1067 assert_eq!(tuple, vec![DynSolType::Bool, DynSolType::Uint(256), DynSolType::String]);
1068 return Ok(());
1069 }
1070 panic!("Expected Person to be CustomStruct");
1071 }
1072
1073 #[test]
1074 fn test_parse_fixed_array() {
1075 let mut struct_defs = TypeDefMap::new();
1076 struct_defs.insert(
1077 "Contract.Child".to_string(),
1078 vec![("value".to_string(), "uint256".to_string())],
1079 );
1080 let struct_defs = StructDefinitions::from(struct_defs);
1081
1082 let value = serde_json::json!([[1], [2]]);
1083 assert!(parse_fixed_array(&value, "uint256[1][]", &struct_defs).unwrap().is_ok());
1084 assert!(parse_fixed_array(&value, "uint256[", &struct_defs).is_none());
1085 assert!(parse_fixed_array(&value, "uint256[0]", &struct_defs).is_none());
1086 assert!(parse_fixed_array(&value, "Contract.Child[", &struct_defs).is_none());
1087 assert!(parse_fixed_array(&value, "Missing.Child[1]", &struct_defs).is_none());
1088
1089 let value = serde_json::json!([[{"value": 1}, {"value": 2}]]);
1090 let parsed =
1091 parse_fixed_array(&value, "Contract.Child[2][1]", &struct_defs).unwrap().unwrap();
1092 assert!(matches!(
1093 parsed,
1094 DynSolValue::FixedArray(outer)
1095 if matches!(&outer[..], [DynSolValue::FixedArray(inner)] if matches!(&inner[..], [DynSolValue::Tuple(_), DynSolValue::Tuple(_)]))
1096 ));
1097
1098 let value = serde_json::json!([[{"value": 1}], [{"value": 2}]]);
1099 assert!(parse_fixed_array(&value, "Contract.Child[1][]", &struct_defs).unwrap().is_ok());
1100 let value = serde_json::json!([[]]);
1101 assert!(parse_fixed_array(&value, "Contract.Child[][1]", &struct_defs).unwrap().is_ok());
1102
1103 let mut ambiguous_defs = TypeDefMap::new();
1104 ambiguous_defs
1105 .insert("A.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1106 ambiguous_defs
1107 .insert("B.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1108 let ambiguous_defs = StructDefinitions::from(ambiguous_defs);
1109 let value = serde_json::json!([{"value": 1}]);
1110 assert!(parse_fixed_array(&value, "Child[1]", &ambiguous_defs).is_none());
1111 assert!(parse_fixed_array(&value, "A.Child[1]", &ambiguous_defs).unwrap().is_ok());
1112 }
1113
1114 #[test]
1115 fn test_resolve_type_for_array_of_structs() -> Result<()> {
1116 let mut struct_defs = TypeDefMap::new();
1118 struct_defs.insert(
1119 "Item".to_string(),
1120 vec![
1121 ("name".to_string(), "string".to_string()),
1122 ("price".to_string(), "uint256".to_string()),
1123 ("id".to_string(), "uint256".to_string()),
1124 ],
1125 );
1126
1127 let ty_desc = "Item(uint256 id,string name,uint256 price)";
1129
1130 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1132 let array_ty = DynSolType::Array(Box::new(ty));
1133 if let DynSolType::Array(item_ty) = array_ty
1134 && let DynSolType::CustomStruct { name, prop_names, tuple } = *item_ty
1135 {
1136 assert_eq!(name, "Item");
1137 assert_eq!(prop_names, vec!["name", "price", "id"]);
1138 assert_eq!(
1139 tuple,
1140 vec![DynSolType::String, DynSolType::Uint(256), DynSolType::Uint(256)]
1141 );
1142 return Ok(());
1143 }
1144 panic!("Expected CustomStruct in array");
1145 }
1146
1147 #[test]
1148 fn test_parse_json_missing_field() {
1149 let mut struct_defs = TypeDefMap::new();
1151 struct_defs.insert(
1152 "Person".to_string(),
1153 vec![
1154 ("name".to_string(), "string".to_string()),
1155 ("age".to_string(), "uint256".to_string()),
1156 ],
1157 );
1158
1159 let json_str = r#"{ "name": "Alice" }"#;
1161
1162 let type_description = "Person(uint256 age,string name)";
1164 let ty = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1165
1166 let json_value: Value = serde_json::from_str(json_str).unwrap();
1168 let result = parse_json_as(&json_value, &ty);
1169
1170 assert!(result.is_err());
1172 assert!(result.unwrap_err().to_string().contains("field \"age\" not found in JSON object"));
1173 }
1174
1175 #[test]
1176 fn test_serialize_json_with_struct_def_order() {
1177 let mut struct_defs = TypeDefMap::new();
1179 struct_defs.insert(
1180 "Item".to_string(),
1181 vec![
1182 ("name".to_string(), "string".to_string()),
1183 ("id".to_string(), "uint256".to_string()),
1184 ("active".to_string(), "bool".to_string()),
1185 ],
1186 );
1187
1188 let item_struct = DynSolValue::CustomStruct {
1190 name: "Item".to_string(),
1191 prop_names: vec!["name".to_string(), "id".to_string(), "active".to_string()],
1192 tuple: vec![
1193 DynSolValue::String("Test Item".to_string()),
1194 DynSolValue::Uint(U256::from(123), 256),
1195 DynSolValue::Bool(true),
1196 ],
1197 };
1198
1199 let json_value =
1201 serialize_value_as_json(item_struct, Some(&struct_defs.into()), false).unwrap();
1202 let json_string = serde_json::to_string(&json_value).unwrap();
1203 assert_eq!(json_string, r#"{"name":"Test Item","id":123,"active":true}"#);
1204 }
1205
1206 #[test]
1207 fn test_json_full_cycle_typed_with_struct_defs() {
1208 let mut struct_defs = TypeDefMap::new();
1210 struct_defs.insert(
1211 "Wallet".to_string(),
1212 vec![
1213 ("owner".to_string(), "address".to_string()),
1214 ("balance".to_string(), "uint256".to_string()),
1215 ("id".to_string(), "bytes32".to_string()),
1216 ],
1217 );
1218
1219 let owner_address = Address::from([1; 20]);
1221 let wallet_id = B256::from([2; 32]);
1222 let original_wallet = DynSolValue::CustomStruct {
1223 name: "Wallet".to_string(),
1224 prop_names: vec!["owner".to_string(), "balance".to_string(), "id".to_string()],
1225 tuple: vec![
1226 DynSolValue::Address(owner_address),
1227 DynSolValue::Uint(U256::from(5000), 256),
1228 DynSolValue::FixedBytes(wallet_id, 32),
1229 ],
1230 };
1231
1232 let json_value = serialize_value_as_json(
1234 original_wallet.clone(),
1235 Some(&struct_defs.clone().into()),
1236 false,
1237 )
1238 .unwrap();
1239 let json_string = serde_json::to_string(&json_value).unwrap();
1240 assert_eq!(
1241 json_string,
1242 format!(r#"{{"owner":"{owner_address}","balance":5000,"id":"{wallet_id}"}}"#)
1243 );
1244
1245 let type_description = "Wallet(uint256 balance,bytes32 id,address owner)";
1247 let resolved_type = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1248
1249 let parsed_value = parse_json_as(&json_value, &resolved_type).unwrap();
1252 assert_eq!(parsed_value, original_wallet);
1253 }
1254}