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
44macro_rules! impl_parse_json {
45 ($call:ident, $call_with_default:ident, $ty:expr) => {
46 impl Cheatcode for $call {
47 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
48 let Self { json, key } = self;
49 parse_json_coerce(json, key, &$ty)
50 }
51 }
52
53 impl Cheatcode for $call_with_default {
54 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
55 let Self { json, key, defaultValue } = self;
56 parse_json_coerce_default(json, key, &$ty, defaultValue)
57 }
58 }
59 };
60}
61
62impl_parse_json!(parseJsonUint_0Call, parseJsonUint_1Call, DynSolType::Uint(256));
63impl_parse_json!(
64 parseJsonUintArray_0Call,
65 parseJsonUintArray_1Call,
66 DynSolType::Array(Box::new(DynSolType::Uint(256)))
67);
68impl_parse_json!(parseJsonInt_0Call, parseJsonInt_1Call, DynSolType::Int(256));
69impl_parse_json!(
70 parseJsonIntArray_0Call,
71 parseJsonIntArray_1Call,
72 DynSolType::Array(Box::new(DynSolType::Int(256)))
73);
74impl_parse_json!(parseJsonBool_0Call, parseJsonBool_1Call, DynSolType::Bool);
75impl_parse_json!(
76 parseJsonBoolArray_0Call,
77 parseJsonBoolArray_1Call,
78 DynSolType::Array(Box::new(DynSolType::Bool))
79);
80impl_parse_json!(parseJsonAddress_0Call, parseJsonAddress_1Call, DynSolType::Address);
81impl_parse_json!(
82 parseJsonAddressArray_0Call,
83 parseJsonAddressArray_1Call,
84 DynSolType::Array(Box::new(DynSolType::Address))
85);
86impl_parse_json!(parseJsonString_0Call, parseJsonString_1Call, DynSolType::String);
87impl_parse_json!(
88 parseJsonStringArray_0Call,
89 parseJsonStringArray_1Call,
90 DynSolType::Array(Box::new(DynSolType::String))
91);
92
93impl Cheatcode for parseJsonArrayLengthCall {
94 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
95 let Self { json, key } = self;
96 parse_json_array_length(json, key)
97 }
98}
99
100impl_parse_json!(parseJsonBytes_0Call, parseJsonBytes_1Call, DynSolType::Bytes);
101impl_parse_json!(
102 parseJsonBytesArray_0Call,
103 parseJsonBytesArray_1Call,
104 DynSolType::Array(Box::new(DynSolType::Bytes))
105);
106impl_parse_json!(parseJsonBytes32_0Call, parseJsonBytes32_1Call, DynSolType::FixedBytes(32));
107impl_parse_json!(
108 parseJsonBytes32Array_0Call,
109 parseJsonBytes32Array_1Call,
110 DynSolType::Array(Box::new(DynSolType::FixedBytes(32)))
111);
112
113impl Cheatcode for parseJsonType_0Call {
114 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
115 let Self { json, typeDescription } = self;
116 parse_json_coerce(json, "$", &resolve_type(typeDescription, state.struct_defs())?)
117 .map(|v| v.abi_encode())
118 }
119}
120
121impl Cheatcode for parseJsonType_1Call {
122 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
123 let Self { json, key, typeDescription } = self;
124 parse_json_coerce(json, key, &resolve_type(typeDescription, state.struct_defs())?)
125 .map(|v| v.abi_encode())
126 }
127}
128
129impl Cheatcode for parseJsonTypeArrayCall {
130 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
131 let Self { json, key, typeDescription } = self;
132 let ty = resolve_type(typeDescription, state.struct_defs())?;
133 parse_json_coerce(json, key, &DynSolType::Array(Box::new(ty))).map(|v| v.abi_encode())
134 }
135}
136
137impl Cheatcode for parseJsonKeysCall {
138 fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
139 let Self { json, key } = self;
140 parse_json_keys(json, key)
141 }
142}
143
144impl Cheatcode for serializeJsonCall {
145 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
146 let Self { objectKey, value } = self;
147 *state.serialized_jsons.entry(objectKey.into()).or_default() = serde_json::from_str(value)?;
148 Ok(value.abi_encode())
149 }
150}
151
152impl Cheatcode for serializeBool_0Call {
153 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
154 let Self { objectKey, valueKey, value } = self;
155 serialize_json(state, objectKey, valueKey, (*value).into())
156 }
157}
158
159impl Cheatcode for serializeUint_0Call {
160 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
161 let Self { objectKey, valueKey, value } = self;
162 serialize_json(state, objectKey, valueKey, (*value).into())
163 }
164}
165
166impl Cheatcode for serializeInt_0Call {
167 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
168 let Self { objectKey, valueKey, value } = self;
169 serialize_json(state, objectKey, valueKey, (*value).into())
170 }
171}
172
173impl Cheatcode for serializeAddress_0Call {
174 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
175 let Self { objectKey, valueKey, value } = self;
176 serialize_json(state, objectKey, valueKey, (*value).into())
177 }
178}
179
180impl Cheatcode for serializeBytes32_0Call {
181 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
182 let Self { objectKey, valueKey, value } = self;
183 serialize_json(state, objectKey, valueKey, DynSolValue::FixedBytes(*value, 32))
184 }
185}
186
187impl Cheatcode for serializeString_0Call {
188 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
189 let Self { objectKey, valueKey, value } = self;
190 serialize_json(state, objectKey, valueKey, value.clone().into())
191 }
192}
193
194impl Cheatcode for serializeBytes_0Call {
195 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
196 let Self { objectKey, valueKey, value } = self;
197 serialize_json(state, objectKey, valueKey, value.to_vec().into())
198 }
199}
200
201impl Cheatcode for serializeBool_1Call {
202 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
203 let Self { objectKey, valueKey, values } = self;
204 serialize_json(
205 state,
206 objectKey,
207 valueKey,
208 DynSolValue::Array(values.iter().copied().map(DynSolValue::Bool).collect()),
209 )
210 }
211}
212
213impl Cheatcode for serializeUint_1Call {
214 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
215 let Self { objectKey, valueKey, values } = self;
216 serialize_json(
217 state,
218 objectKey,
219 valueKey,
220 DynSolValue::Array(values.iter().map(|v| DynSolValue::Uint(*v, 256)).collect()),
221 )
222 }
223}
224
225impl Cheatcode for serializeInt_1Call {
226 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
227 let Self { objectKey, valueKey, values } = self;
228 serialize_json(
229 state,
230 objectKey,
231 valueKey,
232 DynSolValue::Array(values.iter().map(|v| DynSolValue::Int(*v, 256)).collect()),
233 )
234 }
235}
236
237impl Cheatcode for serializeAddress_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::Address).collect()),
245 )
246 }
247}
248
249impl Cheatcode for serializeBytes32_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::FixedBytes(*v, 32)).collect()),
257 )
258 }
259}
260
261impl Cheatcode for serializeString_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().cloned().map(DynSolValue::String).collect()),
269 )
270 }
271}
272
273impl Cheatcode for serializeBytes_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(
281 values.iter().cloned().map(Into::into).map(DynSolValue::Bytes).collect(),
282 ),
283 )
284 }
285}
286
287impl Cheatcode for serializeJsonType_0Call {
288 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
289 let Self { typeDescription, value } = self;
290 let ty = resolve_type(typeDescription, state.struct_defs())?;
291 let value = ty.abi_decode(value)?;
292 let value =
293 foundry_common::fmt::serialize_value_as_json(value, state.struct_defs(), false)?;
294 Ok(value.to_string().abi_encode())
295 }
296}
297
298impl Cheatcode for serializeJsonType_1Call {
299 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
300 let Self { objectKey, valueKey, typeDescription, value } = self;
301 let ty = resolve_type(typeDescription, state.struct_defs())?;
302 let value = ty.abi_decode(value)?;
303 serialize_json(state, objectKey, valueKey, value)
304 }
305}
306
307impl Cheatcode for serializeUintToHexCall {
308 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
309 let Self { objectKey, valueKey, value } = self;
310 let hex = format!("0x{value:x}");
311 serialize_json(state, objectKey, valueKey, hex.into())
312 }
313}
314
315impl Cheatcode for writeJson_0Call {
316 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
317 let Self { json, path } = self;
318 let json = serde_json::from_str(json).unwrap_or_else(|_| Value::String(json.to_owned()));
319 let json_string = serde_json::to_string_pretty(&json)?;
320 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
321 }
322}
323
324impl Cheatcode for writeJson_1Call {
325 fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
326 let Self { json: value, path, valueKey } = self;
327
328 let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?;
331 let mut data = if data_path.exists() {
332 let data_string = fs::locked_read_to_string(&data_path)?;
333 serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string))
334 } else {
335 Value::Object(Default::default())
336 };
337 upsert_json_value(&mut data, value, valueKey)?;
338
339 let json_string = serde_json::to_string_pretty(&data)?;
341 super::fs::write_file(state, path.as_ref(), json_string.as_bytes())
342 }
343}
344
345pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result {
346 let json = parse_json_str(json)?;
347 let values = select(&json, key)?;
348 let exists = !values.is_empty();
349 Ok(exists.abi_encode())
350}
351
352pub(super) fn parse_json(json: &str, path: &str, defs: Option<&StructDefinitions>) -> Result {
353 let value = parse_json_str(json)?;
354 let selected = select(&value, path)?;
355 let sol = json_to_sol(defs, &selected)?;
356 Ok(encode(sol))
357}
358
359pub(super) fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result {
360 let json = parse_json_str(json)?;
361 let [value] = select(&json, path)?[..] else {
362 bail!("path {path:?} must return exactly one JSON value");
363 };
364
365 parse_json_as(value, ty).map(|v| v.abi_encode())
366}
367
368pub(super) fn parse_json_coerce_default<T: SolValue>(
369 json: &str,
370 path: &str,
371 ty: &DynSolType,
372 default: &T,
373) -> Result {
374 let json = parse_json_str(json)?;
375 match select(&json, path)?.as_slice() {
376 [] => Ok(default.abi_encode()),
377 [value] => parse_json_as(value, ty).map(|value| value.abi_encode()),
378 _ => bail!("path {path:?} must return exactly one JSON value"),
379 }
380}
381
382pub(super) fn parse_json_as(value: &Value, ty: &DynSolType) -> Result<DynSolValue> {
384 let to_string = |v: &Value| {
385 let mut s = v.to_string();
386 s.retain(|c: char| c != '"');
387 s
388 };
389
390 match (value, ty) {
391 (Value::Array(array), ty) => parse_json_array(array, ty),
392 (Value::Object(object), ty) => parse_json_map(object, ty),
393 (Value::String(s), DynSolType::String) => Ok(DynSolValue::String(s.clone())),
394 (Value::String(s), DynSolType::Uint(_) | DynSolType::Int(_)) => string::parse_value(s, ty),
395 _ => string::parse_value(&to_string(value), ty),
396 }
397}
398
399pub(super) fn parse_json_array(array: &[Value], ty: &DynSolType) -> Result<DynSolValue> {
400 match ty {
401 DynSolType::Tuple(types) => {
402 ensure!(array.len() == types.len(), "array length mismatch");
403 let values = array
404 .iter()
405 .zip(types)
406 .map(|(e, ty)| parse_json_as(e, ty))
407 .collect::<Result<Vec<_>>>()?;
408
409 Ok(DynSolValue::Tuple(values))
410 }
411 DynSolType::Array(inner) => {
412 let values =
413 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
414 Ok(DynSolValue::Array(values))
415 }
416 DynSolType::FixedArray(inner, len) => {
417 ensure!(array.len() == *len, "array length mismatch");
418 let values =
419 array.iter().map(|e| parse_json_as(e, inner)).collect::<Result<Vec<_>>>()?;
420 Ok(DynSolValue::FixedArray(values))
421 }
422 _ => bail!("expected {ty}, found array"),
423 }
424}
425
426pub(super) fn parse_json_map(map: &Map<String, Value>, ty: &DynSolType) -> Result<DynSolValue> {
427 let Some((name, fields, types)) = ty.as_custom_struct() else {
428 bail!("expected {ty}, found JSON object");
429 };
430
431 let mut values = Vec::with_capacity(fields.len());
432 for (field, ty) in fields.iter().zip(types.iter()) {
433 let Some(value) = map.get(field) else { bail!("field {field:?} not found in JSON object") };
434 values.push(parse_json_as(value, ty)?);
435 }
436
437 Ok(DynSolValue::CustomStruct {
438 name: name.to_string(),
439 prop_names: fields.to_vec(),
440 tuple: values,
441 })
442}
443
444pub(super) fn parse_json_keys(json: &str, key: &str) -> Result {
445 let json = parse_json_str(json)?;
446 let values = select(&json, key)?;
447 let [value] = values[..] else {
448 bail!("key {key:?} must return exactly one JSON object");
449 };
450 let Value::Object(object) = value else {
451 bail!("JSON value at {key:?} is not an object");
452 };
453 let keys = object.keys().collect::<Vec<_>>();
454 Ok(keys.abi_encode())
455}
456
457pub(super) fn parse_json_array_length(json: &str, key: &str) -> Result {
458 let json = parse_json_str(json)?;
459 let values = select(&json, key)?;
460 let [value] = values[..] else {
461 bail!("key {key:?} must return exactly one JSON array");
462 };
463 let Value::Array(array) = value else {
464 bail!("JSON value at {key:?} is not an array");
465 };
466 Ok(U256::from(array.len()).abi_encode())
467}
468
469fn parse_json_str(json: &str) -> Result<Value> {
470 let json = strip_json_comments(json)?;
471 serde_json::from_str(&json).map_err(|e| fmt_err!("failed parsing JSON: {e}"))
472}
473
474fn strip_json_comments(json: &str) -> Result<Cow<'_, str>> {
475 let bytes = json.as_bytes();
476 let mut stripped = None;
477 let mut index = 0;
478 let mut in_string = false;
479
480 while index < bytes.len() {
481 if in_string {
482 match bytes[index] {
483 b'\\' => index += 2,
484 b'"' => {
485 in_string = false;
486 index += 1;
487 }
488 _ => index += 1,
489 }
490 continue;
491 }
492
493 match (bytes[index], bytes.get(index + 1)) {
494 (b'"', _) => {
495 in_string = true;
496 index += 1;
497 }
498 (b'/', Some(b'/')) => {
499 let stripped = stripped.get_or_insert_with(|| bytes.to_vec());
500 while index < bytes.len() && !matches!(bytes[index], b'\r' | b'\n') {
501 stripped[index] = b' ';
502 index += 1;
503 }
504 }
505 (b'/', Some(b'*')) => {
506 let stripped = stripped.get_or_insert_with(|| bytes.to_vec());
507 stripped[index] = b' ';
508 stripped[index + 1] = b' ';
509 index += 2;
510
511 let mut closed = false;
512 while index < bytes.len() {
513 if bytes[index] == b'*' && bytes.get(index + 1) == Some(&b'/') {
514 stripped[index] = b' ';
515 stripped[index + 1] = b' ';
516 index += 2;
517 closed = true;
518 break;
519 }
520 if !matches!(bytes[index], b'\r' | b'\n') {
521 stripped[index] = b' ';
522 }
523 index += 1;
524 }
525 ensure!(closed, "failed parsing JSON: unterminated block comment");
526 }
527 _ => index += 1,
528 }
529 }
530
531 Ok(match stripped {
532 Some(stripped) => {
533 String::from_utf8(stripped).expect("comment stripping preserves valid UTF-8").into()
534 }
535 None => json.into(),
536 })
537}
538
539fn json_to_sol(defs: Option<&StructDefinitions>, json: &[&Value]) -> Result<Vec<DynSolValue>> {
540 let mut sol = Vec::with_capacity(json.len());
541 for value in json {
542 sol.push(json_value_to_token(value, defs)?);
543 }
544 Ok(sol)
545}
546
547fn select<'a>(value: &'a Value, mut path: &str) -> Result<Vec<&'a Value>> {
548 if path == "." {
550 path = "$";
551 }
552 jsonpath_lib::select(value, &canonicalize_json_path(path))
554 .map_err(|e| fmt_err!("failed selecting from JSON: {:?}", e.to_string()))
555}
556
557fn encode(values: Vec<DynSolValue>) -> Vec<u8> {
558 let bytes = match &values[..] {
560 [] => Vec::new(),
561 [one] => one.abi_encode(),
562 _ => DynSolValue::Array(values).abi_encode(),
563 };
564 bytes.abi_encode()
565}
566
567pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> {
570 if path.starts_with('$') { path.into() } else { format!("${path}").into() }
571}
572
573#[instrument(target = "cheatcodes", level = "trace", ret)]
580pub(super) fn json_value_to_token(
581 value: &Value,
582 defs: Option<&StructDefinitions>,
583) -> Result<DynSolValue> {
584 if let Some(defs) = defs {
585 _json_value_to_token(value, defs)
586 } else {
587 _json_value_to_token(value, &StructDefinitions::default())
588 }
589}
590
591fn _json_value_to_token(value: &Value, defs: &StructDefinitions) -> Result<DynSolValue> {
592 match value {
593 Value::Null => Ok(DynSolValue::FixedBytes(B256::ZERO, 32)),
594 Value::Bool(boolean) => Ok(DynSolValue::Bool(*boolean)),
595 Value::Array(array) => array
596 .iter()
597 .map(|v| _json_value_to_token(v, defs))
598 .collect::<Result<_>>()
599 .map(DynSolValue::Array),
600 Value::Object(map) => {
601 let keys: BTreeSet<_> = map.keys().map(|s| s.as_str()).collect();
603 let matching_defs = defs
604 .values()
605 .filter(|fields| {
606 fields.len() == keys.len()
607 && fields.iter().map(|(name, _)| name.as_str()).collect::<BTreeSet<_>>()
608 == keys
609 })
610 .collect::<Vec<_>>();
611
612 if let Some(fields) = matching_defs.first() {
613 fields
615 .iter()
616 .map(|(name, type_description)| {
617 let value = map.get(name).unwrap();
619 let unambiguous = matching_defs.iter().all(|fields| {
620 fields
621 .iter()
622 .find(|(field, _)| field == name)
623 .is_some_and(|(_, ty)| ty == type_description)
624 });
625 if unambiguous
626 && let Some(parsed) = parse_fixed_array(value, type_description, defs)
627 {
628 parsed
629 } else {
630 _json_value_to_token(value, defs)
631 }
632 })
633 .collect::<Result<_>>()
634 .map(DynSolValue::Tuple)
635 } else {
636 let ordered_object: BTreeMap<_, _> =
639 map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
640 ordered_object
641 .values()
642 .map(|value| _json_value_to_token(value, defs))
643 .collect::<Result<_>>()
644 .map(DynSolValue::Tuple)
645 }
646 }
647 Value::Number(number) => {
648 if let Some(f) = number.as_f64() {
649 if f.fract() == 0.0 {
652 let s = number.to_string();
658
659 if s.contains('e') {
664 let fallback_s = f.to_string();
670 if let Ok(n) = fallback_s.parse() {
671 return Ok(DynSolValue::Uint(n, 256));
672 }
673 if let Ok(n) = I256::from_dec_str(&fallback_s) {
674 return Ok(DynSolValue::Int(n, 256));
675 }
676 }
677
678 if let Ok(n) = s.parse() {
679 return Ok(DynSolValue::Uint(n, 256));
680 }
681 if let Ok(n) = s.parse() {
682 return Ok(DynSolValue::Int(n, 256));
683 }
684 }
685 }
686
687 Err(fmt_err!("unsupported JSON number: {number}"))
688 }
689 Value::String(string) => {
690 if let Some(mut val) = string.strip_prefix("0x") {
692 let s;
693 if val.len() == 39 {
694 return Err(format!("Cannot parse \"{val}\" as an address. If you want to specify address, prepend zero to the value.").into());
695 }
696 if !val.len().is_multiple_of(2) {
697 s = format!("0{val}");
698 val = &s[..];
699 }
700 if let Ok(bytes) = hex::decode(val) {
701 return Ok(match bytes.len() {
702 20 => DynSolValue::Address(Address::from_slice(&bytes)),
703 32 => DynSolValue::FixedBytes(B256::from_slice(&bytes), 32),
704 _ => DynSolValue::Bytes(bytes),
705 });
706 }
707 }
708
709 if let Ok(n) = string.parse::<I256>()
714 && i64::try_from(n).is_err()
715 {
716 return Ok(DynSolValue::Int(n, 256));
717 } else if let Ok(n) = string.parse::<U256>()
718 && u64::try_from(n).is_err()
719 {
720 return Ok(DynSolValue::Uint(n, 256));
721 }
722
723 Ok(DynSolValue::String(string.to_owned()))
725 }
726 }
727}
728
729fn parse_fixed_array(
730 value: &Value,
731 type_description: &str,
732 defs: &StructDefinitions,
733) -> Option<Result<DynSolValue>> {
734 let root_end = type_description.find('[')?;
735 let root = &type_description[..root_end];
736 let (ty, custom) = match defs.get(root) {
737 Ok(Some(_)) => {
738 (DynSolType::parse(&format!("bool{}", &type_description[root_end..])).ok()?, true)
739 }
740 Ok(None) if root.contains('.') => return None,
741 Ok(None) => (DynSolType::parse(type_description).ok()?, false),
742 Err(_) => return None,
743 };
744 if !contains_fixed_array(&ty) {
745 return None;
746 }
747
748 Some(if custom { parse_json_custom_array(value, &ty, defs) } else { parse_json_as(value, &ty) })
749}
750
751fn parse_json_custom_array(
752 value: &Value,
753 ty: &DynSolType,
754 defs: &StructDefinitions,
755) -> Result<DynSolValue> {
756 match (value, ty) {
757 (Value::Array(values), DynSolType::Array(inner)) => values
758 .iter()
759 .map(|value| parse_json_custom_array(value, inner, defs))
760 .collect::<Result<_>>()
761 .map(DynSolValue::Array),
762 (Value::Array(values), DynSolType::FixedArray(inner, len)) => {
763 ensure!(values.len() == *len, "array length mismatch");
764 values
765 .iter()
766 .map(|value| parse_json_custom_array(value, inner, defs))
767 .collect::<Result<_>>()
768 .map(DynSolValue::FixedArray)
769 }
770 (_, DynSolType::Bool) => _json_value_to_token(value, defs),
771 _ => bail!("expected array"),
772 }
773}
774
775fn contains_fixed_array(ty: &DynSolType) -> bool {
776 match ty {
777 DynSolType::FixedArray(_, _) => true,
778 DynSolType::Array(inner) => contains_fixed_array(inner),
779 _ => false,
780 }
781}
782
783fn serialize_json<FEN: FoundryEvmNetwork>(
792 state: &mut Cheatcodes<FEN>,
793 object_key: &str,
794 value_key: &str,
795 value: DynSolValue,
796) -> Result {
797 let value = foundry_common::fmt::serialize_value_as_json(value, state.struct_defs(), false)?;
798 let map = state.serialized_jsons.entry(object_key.into()).or_default();
799 map.insert(value_key.into(), value);
800 let stringified = serde_json::to_string(map).unwrap();
801 Ok(stringified.abi_encode())
802}
803
804pub(super) fn resolve_type(
806 type_description: &str,
807 struct_defs: Option<&StructDefinitions>,
808) -> Result<DynSolType> {
809 let ordered_ty = |ty| -> Result<DynSolType> {
810 if let Some(defs) = struct_defs { reorder_type(ty, defs) } else { Ok(ty) }
811 };
812
813 if let Ok(ty) = DynSolType::parse(type_description) {
814 return ordered_ty(ty);
815 };
816
817 if let Ok(encoded) = EncodeType::parse(type_description)
818 && let Some(main) = encoded.types.first()
819 {
820 let main_type = main.type_name;
821 let mut resolver = Resolver::default();
822 for t in &encoded.types {
823 resolver.ingest(t.to_owned());
824 }
825
826 return ordered_ty(resolver.resolve(main_type)?);
828 }
829
830 bail!("type description should be a valid Solidity type or a EIP712 `encodeType` string")
831}
832
833pub(super) fn upsert_json_value(data: &mut Value, value: &str, key: &str) -> Result<()> {
846 let parts = split_value_key(key)?;
847
848 if let Some((key_to_insert, path_to_parent)) = parts.split_last() {
851 let mut current_level = data;
852
853 for segment in path_to_parent {
854 if !current_level.is_object() {
855 return Err(fmt_err!("path segment '{segment}' does not resolve to an object."));
856 }
857 current_level = current_level
858 .as_object_mut()
859 .unwrap()
860 .entry(segment.clone())
861 .or_insert(Value::Object(Map::new()));
862 }
863
864 if let Some(parent_obj) = current_level.as_object_mut() {
866 parent_obj.insert(
867 key_to_insert.clone(),
868 serde_json::from_str(value).unwrap_or_else(|_| Value::String(value.to_owned())),
869 );
870 } else {
871 return Err(fmt_err!("final destination is not an object, cannot insert key."));
872 }
873 }
874
875 Ok(())
876}
877
878pub(super) fn split_value_key(key: &str) -> Result<Vec<String>> {
880 let canonical_key = canonicalize_json_path(key);
882 let parts = canonical_key
883 .strip_prefix("$.")
884 .unwrap_or(key)
885 .split('.')
886 .filter(|s| !s.is_empty())
887 .map(str::to_string)
888 .collect::<Vec<_>>();
889
890 if parts.is_empty() {
891 return Err(fmt_err!("'valueKey' cannot be empty or just '$'"));
892 }
893
894 Ok(parts)
895}
896
897fn reorder_type(ty: DynSolType, struct_defs: &StructDefinitions) -> Result<DynSolType> {
903 match ty {
904 DynSolType::CustomStruct { name, prop_names, tuple } => {
905 if let Some(def) = struct_defs.get(&name)? {
906 let type_map: std::collections::HashMap<String, DynSolType> =
908 prop_names.into_iter().zip(tuple).collect();
909
910 let mut sorted_props = Vec::with_capacity(def.len());
911 let mut sorted_tuple = Vec::with_capacity(def.len());
912 for (field_name, _) in def {
913 sorted_props.push(field_name.clone());
914 if let Some(field_ty) = type_map.get(field_name) {
915 sorted_tuple.push(reorder_type(field_ty.clone(), struct_defs)?);
916 } else {
917 bail!(
918 "mismatch between struct definition and type description: field '{field_name}' not found in provided type for struct '{name}'"
919 );
920 }
921 }
922 Ok(DynSolType::CustomStruct { name, prop_names: sorted_props, tuple: sorted_tuple })
923 } else {
924 let new_tuple = tuple
927 .into_iter()
928 .map(|t| reorder_type(t, struct_defs))
929 .collect::<Result<Vec<_>>>()?;
930 Ok(DynSolType::CustomStruct { name, prop_names, tuple: new_tuple })
931 }
932 }
933 DynSolType::Array(inner) => {
934 Ok(DynSolType::Array(Box::new(reorder_type(*inner, struct_defs)?)))
935 }
936 DynSolType::FixedArray(inner, len) => {
937 Ok(DynSolType::FixedArray(Box::new(reorder_type(*inner, struct_defs)?), len))
938 }
939 DynSolType::Tuple(inner) => Ok(DynSolType::Tuple(
940 inner.into_iter().map(|t| reorder_type(t, struct_defs)).collect::<Result<Vec<_>>>()?,
941 )),
942 _ => Ok(ty),
943 }
944}
945
946#[cfg(test)]
947mod tests {
948 use super::*;
949 use alloy_primitives::FixedBytes;
950 use foundry_common::fmt::{TypeDefMap, serialize_value_as_json};
951 use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
952 use std::collections::HashSet;
953
954 #[test]
955 fn test_parse_json_comments() {
956 let value = parse_json_str(
957 r#"{
958 // A line comment.
959 "value": 42,
960 /* A block comment. */
961 "url": "https://example.com/path/*literal*/"
962 }"#,
963 )
964 .unwrap();
965
966 assert_eq!(value["value"], 42);
967 assert_eq!(value["url"], "https://example.com/path/*literal*/");
968
969 let error = parse_json_str(r#"{"value": 42} /* unterminated"#).unwrap_err();
970 assert!(error.to_string().contains("unterminated block comment"));
971 }
972
973 fn valid_value(value: &DynSolValue) -> bool {
974 (match value {
975 DynSolValue::String(s) if s == "{}" => false,
976
977 DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => false,
978
979 DynSolValue::Array(v) | DynSolValue::FixedArray(v) => v.iter().all(valid_value),
980 _ => true,
981 }) && value.as_type().is_some()
982 }
983
984 fn fixup_guessable(value: DynSolValue) -> DynSolValue {
989 match value {
990 DynSolValue::Array(mut v) | DynSolValue::FixedArray(mut v) => {
991 if let Some(DynSolValue::Bytes(_)) = v.first() {
992 v.retain(|v| {
993 let len = v.as_bytes().unwrap().len();
994 len != 32 && len != 20
995 })
996 }
997 DynSolValue::Array(v.into_iter().map(fixup_guessable).collect())
998 }
999 DynSolValue::FixedBytes(v, _) => DynSolValue::FixedBytes(v, 32),
1000 DynSolValue::Bytes(v) if v.len() == 32 => {
1001 DynSolValue::FixedBytes(FixedBytes::from_slice(&v), 32)
1002 }
1003 DynSolValue::Bytes(v) if v.len() == 20 => DynSolValue::Address(Address::from_slice(&v)),
1004 _ => value,
1005 }
1006 }
1007
1008 fn guessable_types() -> impl proptest::strategy::Strategy<Value = DynSolValue> {
1009 any::<DynSolValue>().prop_map(fixup_guessable).prop_filter("invalid value", valid_value)
1010 }
1011
1012 fn custom_struct_strategy() -> impl Strategy<Value = (StructDefinitions, DynSolValue)> {
1015 let field_name_strat = "[a-z]{4,12}";
1017 let field_value_strat = prop_oneof![
1018 any::<bool>().prop_map(DynSolValue::Bool),
1019 any::<u32>().prop_map(|v| DynSolValue::Uint(U256::from(v), 256)),
1020 any::<[u8; 20]>().prop_map(Address::from).prop_map(DynSolValue::Address),
1021 any::<[u8; 32]>().prop_map(B256::from).prop_map(|b| DynSolValue::FixedBytes(b, 32)),
1022 ".*".prop_filter("invalid string value", |s| s != "{}").prop_map(DynSolValue::String),
1023 ];
1024
1025 let fields_strat = proptest::collection::vec((field_name_strat, field_value_strat), 1..8)
1027 .prop_map(|fields| {
1028 let mut unique_fields = Vec::with_capacity(fields.len());
1029 let mut seen_names = HashSet::new();
1030 for (name, value) in fields {
1031 if seen_names.insert(name.clone()) {
1032 unique_fields.push((name, value));
1033 }
1034 }
1035 unique_fields
1036 });
1037
1038 ("[A-Z][a-z]{4,8}", fields_strat).prop_map(|(struct_name, fields)| {
1040 let (prop_names, tuple): (Vec<String>, Vec<DynSolValue>) =
1041 fields.clone().into_iter().unzip();
1042 let def_fields: Vec<(String, String)> = fields
1043 .iter()
1044 .map(|(name, value)| (name.clone(), value.as_type().unwrap().to_string()))
1045 .collect();
1046 let mut defs_map = TypeDefMap::default();
1047 defs_map.insert(struct_name.clone(), def_fields);
1048 (defs_map.into(), DynSolValue::CustomStruct { name: struct_name, prop_names, tuple })
1049 })
1050 }
1051
1052 proptest::proptest! {
1054 #[test]
1055 fn test_json_roundtrip_guessed(v in guessable_types()) {
1056 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
1057 let value = json_value_to_token(&json, None).unwrap();
1058
1059 let decoded = v.as_type().unwrap().abi_decode(&value.abi_encode()).unwrap();
1061 assert_eq!(decoded, v);
1062 }
1063
1064 #[test]
1065 fn test_json_roundtrip(v in any::<DynSolValue>().prop_filter("filter out values without type", |v| v.as_type().is_some())) {
1066 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
1067 let value = parse_json_as(&json, &v.as_type().unwrap()).unwrap();
1068 assert_eq!(value, v);
1069 }
1070
1071 #[test]
1072 fn test_json_roundtrip_with_struct_defs((struct_defs, v) in custom_struct_strategy()) {
1073 let json = serialize_value_as_json(v.clone(), Some(&struct_defs), false).unwrap();
1074 let sol_type = v.as_type().unwrap();
1075 let parsed_value = parse_json_as(&json, &sol_type).unwrap();
1076 assert_eq!(parsed_value, v);
1077 }
1078 }
1079
1080 #[test]
1081 fn test_resolve_type_with_definitions() -> Result<()> {
1082 let mut struct_defs = TypeDefMap::new();
1084 struct_defs.insert(
1085 "Apple".to_string(),
1086 vec![
1087 ("color".to_string(), "string".to_string()),
1088 ("sweetness".to_string(), "uint8".to_string()),
1089 ("sourness".to_string(), "uint8".to_string()),
1090 ],
1091 );
1092 struct_defs.insert(
1093 "FruitStall".to_string(),
1094 vec![
1095 ("name".to_string(), "string".to_string()),
1096 ("apples".to_string(), "Apple[]".to_string()),
1097 ],
1098 );
1099
1100 let ty_desc = "FruitStall(Apple[] apples,string name)Apple(string color,uint8 sourness,uint8 sweetness)";
1102
1103 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1105 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1106 assert_eq!(name, "FruitStall");
1107 assert_eq!(prop_names, vec!["name", "apples"]);
1108 assert_eq!(tuple.len(), 2);
1109 assert_eq!(tuple[0], DynSolType::String);
1110
1111 if let DynSolType::Array(apple_ty_boxed) = &tuple[1]
1112 && let DynSolType::CustomStruct { name, prop_names, tuple } = &**apple_ty_boxed
1113 {
1114 assert_eq!(*name, "Apple");
1115 assert_eq!(*prop_names, vec!["color", "sweetness", "sourness"]);
1117 assert_eq!(
1118 *tuple,
1119 vec![DynSolType::String, DynSolType::Uint(8), DynSolType::Uint(8)]
1120 );
1121
1122 return Ok(());
1123 }
1124 }
1125 panic!("Expected FruitStall and Apple to be CustomStruct");
1126 }
1127
1128 #[test]
1129 fn test_resolve_type_without_definitions() -> Result<()> {
1130 let ty_desc = "Person(bool active,uint256 age,string name)";
1132
1133 let ty = resolve_type(ty_desc, None).unwrap();
1136 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1137 assert_eq!(name, "Person");
1138 assert_eq!(prop_names, vec!["active", "age", "name"]);
1139 assert_eq!(tuple.len(), 3);
1140 assert_eq!(tuple, vec![DynSolType::Bool, DynSolType::Uint(256), DynSolType::String]);
1141 return Ok(());
1142 }
1143 panic!("Expected Person to be CustomStruct");
1144 }
1145
1146 #[test]
1147 fn test_resolve_type_bare_struct_name_errors_instead_of_panicking() {
1148 let err = resolve_type("Foo", None).unwrap_err();
1150 assert!(
1151 err.to_string().contains("valid Solidity type or a EIP712 `encodeType` string"),
1152 "unexpected error: {err}"
1153 );
1154 }
1155
1156 #[test]
1157 fn test_parse_fixed_array() {
1158 let mut struct_defs = TypeDefMap::new();
1159 struct_defs.insert(
1160 "Contract.Child".to_string(),
1161 vec![("value".to_string(), "uint256".to_string())],
1162 );
1163 let struct_defs = StructDefinitions::from(struct_defs);
1164
1165 let value = serde_json::json!([[1], [2]]);
1166 assert!(parse_fixed_array(&value, "uint256[1][]", &struct_defs).unwrap().is_ok());
1167 assert!(parse_fixed_array(&value, "uint256[", &struct_defs).is_none());
1168 assert!(parse_fixed_array(&value, "uint256[0]", &struct_defs).is_none());
1169 assert!(parse_fixed_array(&value, "Contract.Child[", &struct_defs).is_none());
1170 assert!(parse_fixed_array(&value, "Missing.Child[1]", &struct_defs).is_none());
1171
1172 let value = serde_json::json!([[{"value": 1}, {"value": 2}]]);
1173 let parsed =
1174 parse_fixed_array(&value, "Contract.Child[2][1]", &struct_defs).unwrap().unwrap();
1175 assert!(matches!(
1176 parsed,
1177 DynSolValue::FixedArray(outer)
1178 if matches!(&outer[..], [DynSolValue::FixedArray(inner)] if matches!(&inner[..], [DynSolValue::Tuple(_), DynSolValue::Tuple(_)]))
1179 ));
1180
1181 let value = serde_json::json!([[{"value": 1}], [{"value": 2}]]);
1182 assert!(parse_fixed_array(&value, "Contract.Child[1][]", &struct_defs).unwrap().is_ok());
1183 let value = serde_json::json!([[]]);
1184 assert!(parse_fixed_array(&value, "Contract.Child[][1]", &struct_defs).unwrap().is_ok());
1185
1186 let mut ambiguous_defs = TypeDefMap::new();
1187 ambiguous_defs
1188 .insert("A.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1189 ambiguous_defs
1190 .insert("B.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1191 let ambiguous_defs = StructDefinitions::from(ambiguous_defs);
1192 let value = serde_json::json!([{"value": 1}]);
1193 assert!(parse_fixed_array(&value, "Child[1]", &ambiguous_defs).is_none());
1194 assert!(parse_fixed_array(&value, "A.Child[1]", &ambiguous_defs).unwrap().is_ok());
1195 }
1196
1197 #[test]
1198 fn test_resolve_type_for_array_of_structs() -> Result<()> {
1199 let mut struct_defs = TypeDefMap::new();
1201 struct_defs.insert(
1202 "Item".to_string(),
1203 vec![
1204 ("name".to_string(), "string".to_string()),
1205 ("price".to_string(), "uint256".to_string()),
1206 ("id".to_string(), "uint256".to_string()),
1207 ],
1208 );
1209
1210 let ty_desc = "Item(uint256 id,string name,uint256 price)";
1212
1213 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1215 let array_ty = DynSolType::Array(Box::new(ty));
1216 if let DynSolType::Array(item_ty) = array_ty
1217 && let DynSolType::CustomStruct { name, prop_names, tuple } = *item_ty
1218 {
1219 assert_eq!(name, "Item");
1220 assert_eq!(prop_names, vec!["name", "price", "id"]);
1221 assert_eq!(
1222 tuple,
1223 vec![DynSolType::String, DynSolType::Uint(256), DynSolType::Uint(256)]
1224 );
1225 return Ok(());
1226 }
1227 panic!("Expected CustomStruct in array");
1228 }
1229
1230 #[test]
1231 fn test_parse_json_missing_field() {
1232 let mut struct_defs = TypeDefMap::new();
1234 struct_defs.insert(
1235 "Person".to_string(),
1236 vec![
1237 ("name".to_string(), "string".to_string()),
1238 ("age".to_string(), "uint256".to_string()),
1239 ],
1240 );
1241
1242 let json_str = r#"{ "name": "Alice" }"#;
1244
1245 let type_description = "Person(uint256 age,string name)";
1247 let ty = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1248
1249 let json_value: Value = serde_json::from_str(json_str).unwrap();
1251 let result = parse_json_as(&json_value, &ty);
1252
1253 assert!(result.is_err());
1255 assert!(result.unwrap_err().to_string().contains("field \"age\" not found in JSON object"));
1256 }
1257
1258 #[test]
1259 fn test_serialize_json_with_struct_def_order() {
1260 let mut struct_defs = TypeDefMap::new();
1262 struct_defs.insert(
1263 "Item".to_string(),
1264 vec![
1265 ("name".to_string(), "string".to_string()),
1266 ("id".to_string(), "uint256".to_string()),
1267 ("active".to_string(), "bool".to_string()),
1268 ],
1269 );
1270
1271 let item_struct = DynSolValue::CustomStruct {
1273 name: "Item".to_string(),
1274 prop_names: vec!["name".to_string(), "id".to_string(), "active".to_string()],
1275 tuple: vec![
1276 DynSolValue::String("Test Item".to_string()),
1277 DynSolValue::Uint(U256::from(123), 256),
1278 DynSolValue::Bool(true),
1279 ],
1280 };
1281
1282 let json_value =
1284 serialize_value_as_json(item_struct, Some(&struct_defs.into()), false).unwrap();
1285 let json_string = serde_json::to_string(&json_value).unwrap();
1286 assert_eq!(json_string, r#"{"name":"Test Item","id":123,"active":true}"#);
1287 }
1288
1289 #[test]
1290 fn test_json_full_cycle_typed_with_struct_defs() {
1291 let mut struct_defs = TypeDefMap::new();
1293 struct_defs.insert(
1294 "Wallet".to_string(),
1295 vec![
1296 ("owner".to_string(), "address".to_string()),
1297 ("balance".to_string(), "uint256".to_string()),
1298 ("id".to_string(), "bytes32".to_string()),
1299 ],
1300 );
1301
1302 let owner_address = Address::repeat_byte(1);
1304 let wallet_id = B256::repeat_byte(2);
1305 let original_wallet = DynSolValue::CustomStruct {
1306 name: "Wallet".to_string(),
1307 prop_names: vec!["owner".to_string(), "balance".to_string(), "id".to_string()],
1308 tuple: vec![
1309 DynSolValue::Address(owner_address),
1310 DynSolValue::Uint(U256::from(5000), 256),
1311 DynSolValue::FixedBytes(wallet_id, 32),
1312 ],
1313 };
1314
1315 let json_value = serialize_value_as_json(
1317 original_wallet.clone(),
1318 Some(&struct_defs.clone().into()),
1319 false,
1320 )
1321 .unwrap();
1322 let json_string = serde_json::to_string(&json_value).unwrap();
1323 assert_eq!(
1324 json_string,
1325 format!(r#"{{"owner":"{owner_address}","balance":5000,"id":"{wallet_id}"}}"#)
1326 );
1327
1328 let type_description = "Wallet(uint256 balance,bytes32 id,address owner)";
1330 let resolved_type = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1331
1332 let parsed_value = parse_json_as(&json_value, &resolved_type).unwrap();
1335 assert_eq!(parsed_value, original_wallet);
1336 }
1337}