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 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 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 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
396pub(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 if path == "." {
486 path = "$";
487 }
488 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 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
503pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> {
506 if path.starts_with('$') { path.into() } else { format!("${path}").into() }
507}
508
509#[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 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 fields
547 .iter()
548 .map(|(name, _)| {
549 _json_value_to_token(map.get(name).unwrap(), defs)
551 })
552 .collect::<Result<_>>()
553 .map(DynSolValue::Tuple)
554 } else {
555 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 if f.fract() == 0.0 {
571 let s = number.to_string();
577
578 if s.contains('e') {
583 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 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 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 Ok(DynSolValue::String(string.to_owned()))
644 }
645 }
646}
647
648fn 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
669pub(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 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
696pub(super) fn upsert_json_value(data: &mut Value, value: &str, key: &str) -> Result<()> {
709 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 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 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
752fn 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 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 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 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 fn custom_struct_strategy() -> impl Strategy<Value = (StructDefinitions, DynSolValue)> {
851 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 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 ("[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 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 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 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 let ty_desc = "FruitStall(Apple[] apples,string name)Apple(string color,uint8 sourness,uint8 sweetness)";
938
939 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 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 let ty_desc = "Person(bool active,uint256 age,string name)";
968
969 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 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 let ty_desc = "Item(uint256 id,string name,uint256 price)";
997
998 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 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 let json_str = r#"{ "name": "Alice" }"#;
1029
1030 let type_description = "Person(uint256 age,string name)";
1032 let ty = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1033
1034 let json_value: Value = serde_json::from_str(json_str).unwrap();
1036 let result = parse_json_as(&json_value, &ty);
1037
1038 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 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 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 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 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 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 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 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 let parsed_value = parse_json_as(&json_value, &resolved_type).unwrap();
1116 assert_eq!(parsed_value, original_wallet);
1117 }
1118}