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 canonical_key = canonicalize_json_path(key);
848 let parts: Vec<&str> = canonical_key
849 .strip_prefix("$.")
850 .unwrap_or(key)
851 .split('.')
852 .filter(|s| !s.is_empty())
853 .collect();
854
855 if parts.is_empty() {
856 return Err(fmt_err!("'valueKey' cannot be empty or just '$'"));
857 }
858
859 if let Some((key_to_insert, path_to_parent)) = parts.split_last() {
862 let mut current_level = data;
863
864 for segment in path_to_parent {
865 if !current_level.is_object() {
866 return Err(fmt_err!("path segment '{segment}' does not resolve to an object."));
867 }
868 current_level = current_level
869 .as_object_mut()
870 .unwrap()
871 .entry(segment.to_string())
872 .or_insert(Value::Object(Map::new()));
873 }
874
875 if let Some(parent_obj) = current_level.as_object_mut() {
877 parent_obj.insert(
878 key_to_insert.to_string(),
879 serde_json::from_str(value).unwrap_or_else(|_| Value::String(value.to_owned())),
880 );
881 } else {
882 return Err(fmt_err!("final destination is not an object, cannot insert key."));
883 }
884 }
885
886 Ok(())
887}
888
889fn reorder_type(ty: DynSolType, struct_defs: &StructDefinitions) -> Result<DynSolType> {
895 match ty {
896 DynSolType::CustomStruct { name, prop_names, tuple } => {
897 if let Some(def) = struct_defs.get(&name)? {
898 let type_map: std::collections::HashMap<String, DynSolType> =
900 prop_names.into_iter().zip(tuple).collect();
901
902 let mut sorted_props = Vec::with_capacity(def.len());
903 let mut sorted_tuple = Vec::with_capacity(def.len());
904 for (field_name, _) in def {
905 sorted_props.push(field_name.clone());
906 if let Some(field_ty) = type_map.get(field_name) {
907 sorted_tuple.push(reorder_type(field_ty.clone(), struct_defs)?);
908 } else {
909 bail!(
910 "mismatch between struct definition and type description: field '{field_name}' not found in provided type for struct '{name}'"
911 );
912 }
913 }
914 Ok(DynSolType::CustomStruct { name, prop_names: sorted_props, tuple: sorted_tuple })
915 } else {
916 let new_tuple = tuple
919 .into_iter()
920 .map(|t| reorder_type(t, struct_defs))
921 .collect::<Result<Vec<_>>>()?;
922 Ok(DynSolType::CustomStruct { name, prop_names, tuple: new_tuple })
923 }
924 }
925 DynSolType::Array(inner) => {
926 Ok(DynSolType::Array(Box::new(reorder_type(*inner, struct_defs)?)))
927 }
928 DynSolType::FixedArray(inner, len) => {
929 Ok(DynSolType::FixedArray(Box::new(reorder_type(*inner, struct_defs)?), len))
930 }
931 DynSolType::Tuple(inner) => Ok(DynSolType::Tuple(
932 inner.into_iter().map(|t| reorder_type(t, struct_defs)).collect::<Result<Vec<_>>>()?,
933 )),
934 _ => Ok(ty),
935 }
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use alloy_primitives::FixedBytes;
942 use foundry_common::fmt::{TypeDefMap, serialize_value_as_json};
943 use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
944 use std::collections::HashSet;
945
946 #[test]
947 fn test_parse_json_comments() {
948 let value = parse_json_str(
949 r#"{
950 // A line comment.
951 "value": 42,
952 /* A block comment. */
953 "url": "https://example.com/path/*literal*/"
954 }"#,
955 )
956 .unwrap();
957
958 assert_eq!(value["value"], 42);
959 assert_eq!(value["url"], "https://example.com/path/*literal*/");
960
961 let error = parse_json_str(r#"{"value": 42} /* unterminated"#).unwrap_err();
962 assert!(error.to_string().contains("unterminated block comment"));
963 }
964
965 fn valid_value(value: &DynSolValue) -> bool {
966 (match value {
967 DynSolValue::String(s) if s == "{}" => false,
968
969 DynSolValue::Tuple(_) | DynSolValue::CustomStruct { .. } => false,
970
971 DynSolValue::Array(v) | DynSolValue::FixedArray(v) => v.iter().all(valid_value),
972 _ => true,
973 }) && value.as_type().is_some()
974 }
975
976 fn fixup_guessable(value: DynSolValue) -> DynSolValue {
981 match value {
982 DynSolValue::Array(mut v) | DynSolValue::FixedArray(mut v) => {
983 if let Some(DynSolValue::Bytes(_)) = v.first() {
984 v.retain(|v| {
985 let len = v.as_bytes().unwrap().len();
986 len != 32 && len != 20
987 })
988 }
989 DynSolValue::Array(v.into_iter().map(fixup_guessable).collect())
990 }
991 DynSolValue::FixedBytes(v, _) => DynSolValue::FixedBytes(v, 32),
992 DynSolValue::Bytes(v) if v.len() == 32 => {
993 DynSolValue::FixedBytes(FixedBytes::from_slice(&v), 32)
994 }
995 DynSolValue::Bytes(v) if v.len() == 20 => DynSolValue::Address(Address::from_slice(&v)),
996 _ => value,
997 }
998 }
999
1000 fn guessable_types() -> impl proptest::strategy::Strategy<Value = DynSolValue> {
1001 any::<DynSolValue>().prop_map(fixup_guessable).prop_filter("invalid value", valid_value)
1002 }
1003
1004 fn custom_struct_strategy() -> impl Strategy<Value = (StructDefinitions, DynSolValue)> {
1007 let field_name_strat = "[a-z]{4,12}";
1009 let field_value_strat = prop_oneof![
1010 any::<bool>().prop_map(DynSolValue::Bool),
1011 any::<u32>().prop_map(|v| DynSolValue::Uint(U256::from(v), 256)),
1012 any::<[u8; 20]>().prop_map(Address::from).prop_map(DynSolValue::Address),
1013 any::<[u8; 32]>().prop_map(B256::from).prop_map(|b| DynSolValue::FixedBytes(b, 32)),
1014 ".*".prop_filter("invalid string value", |s| s != "{}").prop_map(DynSolValue::String),
1015 ];
1016
1017 let fields_strat = proptest::collection::vec((field_name_strat, field_value_strat), 1..8)
1019 .prop_map(|fields| {
1020 let mut unique_fields = Vec::with_capacity(fields.len());
1021 let mut seen_names = HashSet::new();
1022 for (name, value) in fields {
1023 if seen_names.insert(name.clone()) {
1024 unique_fields.push((name, value));
1025 }
1026 }
1027 unique_fields
1028 });
1029
1030 ("[A-Z][a-z]{4,8}", fields_strat).prop_map(|(struct_name, fields)| {
1032 let (prop_names, tuple): (Vec<String>, Vec<DynSolValue>) =
1033 fields.clone().into_iter().unzip();
1034 let def_fields: Vec<(String, String)> = fields
1035 .iter()
1036 .map(|(name, value)| (name.clone(), value.as_type().unwrap().to_string()))
1037 .collect();
1038 let mut defs_map = TypeDefMap::default();
1039 defs_map.insert(struct_name.clone(), def_fields);
1040 (defs_map.into(), DynSolValue::CustomStruct { name: struct_name, prop_names, tuple })
1041 })
1042 }
1043
1044 proptest::proptest! {
1046 #[test]
1047 fn test_json_roundtrip_guessed(v in guessable_types()) {
1048 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
1049 let value = json_value_to_token(&json, None).unwrap();
1050
1051 let decoded = v.as_type().unwrap().abi_decode(&value.abi_encode()).unwrap();
1053 assert_eq!(decoded, v);
1054 }
1055
1056 #[test]
1057 fn test_json_roundtrip(v in any::<DynSolValue>().prop_filter("filter out values without type", |v| v.as_type().is_some())) {
1058 let json = serialize_value_as_json(v.clone(), None, false).unwrap();
1059 let value = parse_json_as(&json, &v.as_type().unwrap()).unwrap();
1060 assert_eq!(value, v);
1061 }
1062
1063 #[test]
1064 fn test_json_roundtrip_with_struct_defs((struct_defs, v) in custom_struct_strategy()) {
1065 let json = serialize_value_as_json(v.clone(), Some(&struct_defs), false).unwrap();
1066 let sol_type = v.as_type().unwrap();
1067 let parsed_value = parse_json_as(&json, &sol_type).unwrap();
1068 assert_eq!(parsed_value, v);
1069 }
1070 }
1071
1072 #[test]
1073 fn test_resolve_type_with_definitions() -> Result<()> {
1074 let mut struct_defs = TypeDefMap::new();
1076 struct_defs.insert(
1077 "Apple".to_string(),
1078 vec![
1079 ("color".to_string(), "string".to_string()),
1080 ("sweetness".to_string(), "uint8".to_string()),
1081 ("sourness".to_string(), "uint8".to_string()),
1082 ],
1083 );
1084 struct_defs.insert(
1085 "FruitStall".to_string(),
1086 vec![
1087 ("name".to_string(), "string".to_string()),
1088 ("apples".to_string(), "Apple[]".to_string()),
1089 ],
1090 );
1091
1092 let ty_desc = "FruitStall(Apple[] apples,string name)Apple(string color,uint8 sourness,uint8 sweetness)";
1094
1095 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1097 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1098 assert_eq!(name, "FruitStall");
1099 assert_eq!(prop_names, vec!["name", "apples"]);
1100 assert_eq!(tuple.len(), 2);
1101 assert_eq!(tuple[0], DynSolType::String);
1102
1103 if let DynSolType::Array(apple_ty_boxed) = &tuple[1]
1104 && let DynSolType::CustomStruct { name, prop_names, tuple } = &**apple_ty_boxed
1105 {
1106 assert_eq!(*name, "Apple");
1107 assert_eq!(*prop_names, vec!["color", "sweetness", "sourness"]);
1109 assert_eq!(
1110 *tuple,
1111 vec![DynSolType::String, DynSolType::Uint(8), DynSolType::Uint(8)]
1112 );
1113
1114 return Ok(());
1115 }
1116 }
1117 panic!("Expected FruitStall and Apple to be CustomStruct");
1118 }
1119
1120 #[test]
1121 fn test_resolve_type_without_definitions() -> Result<()> {
1122 let ty_desc = "Person(bool active,uint256 age,string name)";
1124
1125 let ty = resolve_type(ty_desc, None).unwrap();
1128 if let DynSolType::CustomStruct { name, prop_names, tuple } = ty {
1129 assert_eq!(name, "Person");
1130 assert_eq!(prop_names, vec!["active", "age", "name"]);
1131 assert_eq!(tuple.len(), 3);
1132 assert_eq!(tuple, vec![DynSolType::Bool, DynSolType::Uint(256), DynSolType::String]);
1133 return Ok(());
1134 }
1135 panic!("Expected Person to be CustomStruct");
1136 }
1137
1138 #[test]
1139 fn test_resolve_type_bare_struct_name_errors_instead_of_panicking() {
1140 let err = resolve_type("Foo", None).unwrap_err();
1142 assert!(
1143 err.to_string().contains("valid Solidity type or a EIP712 `encodeType` string"),
1144 "unexpected error: {err}"
1145 );
1146 }
1147
1148 #[test]
1149 fn test_parse_fixed_array() {
1150 let mut struct_defs = TypeDefMap::new();
1151 struct_defs.insert(
1152 "Contract.Child".to_string(),
1153 vec![("value".to_string(), "uint256".to_string())],
1154 );
1155 let struct_defs = StructDefinitions::from(struct_defs);
1156
1157 let value = serde_json::json!([[1], [2]]);
1158 assert!(parse_fixed_array(&value, "uint256[1][]", &struct_defs).unwrap().is_ok());
1159 assert!(parse_fixed_array(&value, "uint256[", &struct_defs).is_none());
1160 assert!(parse_fixed_array(&value, "uint256[0]", &struct_defs).is_none());
1161 assert!(parse_fixed_array(&value, "Contract.Child[", &struct_defs).is_none());
1162 assert!(parse_fixed_array(&value, "Missing.Child[1]", &struct_defs).is_none());
1163
1164 let value = serde_json::json!([[{"value": 1}, {"value": 2}]]);
1165 let parsed =
1166 parse_fixed_array(&value, "Contract.Child[2][1]", &struct_defs).unwrap().unwrap();
1167 assert!(matches!(
1168 parsed,
1169 DynSolValue::FixedArray(outer)
1170 if matches!(&outer[..], [DynSolValue::FixedArray(inner)] if matches!(&inner[..], [DynSolValue::Tuple(_), DynSolValue::Tuple(_)]))
1171 ));
1172
1173 let value = serde_json::json!([[{"value": 1}], [{"value": 2}]]);
1174 assert!(parse_fixed_array(&value, "Contract.Child[1][]", &struct_defs).unwrap().is_ok());
1175 let value = serde_json::json!([[]]);
1176 assert!(parse_fixed_array(&value, "Contract.Child[][1]", &struct_defs).unwrap().is_ok());
1177
1178 let mut ambiguous_defs = TypeDefMap::new();
1179 ambiguous_defs
1180 .insert("A.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1181 ambiguous_defs
1182 .insert("B.Child".to_string(), vec![("value".to_string(), "uint256".to_string())]);
1183 let ambiguous_defs = StructDefinitions::from(ambiguous_defs);
1184 let value = serde_json::json!([{"value": 1}]);
1185 assert!(parse_fixed_array(&value, "Child[1]", &ambiguous_defs).is_none());
1186 assert!(parse_fixed_array(&value, "A.Child[1]", &ambiguous_defs).unwrap().is_ok());
1187 }
1188
1189 #[test]
1190 fn test_resolve_type_for_array_of_structs() -> Result<()> {
1191 let mut struct_defs = TypeDefMap::new();
1193 struct_defs.insert(
1194 "Item".to_string(),
1195 vec![
1196 ("name".to_string(), "string".to_string()),
1197 ("price".to_string(), "uint256".to_string()),
1198 ("id".to_string(), "uint256".to_string()),
1199 ],
1200 );
1201
1202 let ty_desc = "Item(uint256 id,string name,uint256 price)";
1204
1205 let ty = resolve_type(ty_desc, Some(&struct_defs.into())).unwrap();
1207 let array_ty = DynSolType::Array(Box::new(ty));
1208 if let DynSolType::Array(item_ty) = array_ty
1209 && let DynSolType::CustomStruct { name, prop_names, tuple } = *item_ty
1210 {
1211 assert_eq!(name, "Item");
1212 assert_eq!(prop_names, vec!["name", "price", "id"]);
1213 assert_eq!(
1214 tuple,
1215 vec![DynSolType::String, DynSolType::Uint(256), DynSolType::Uint(256)]
1216 );
1217 return Ok(());
1218 }
1219 panic!("Expected CustomStruct in array");
1220 }
1221
1222 #[test]
1223 fn test_parse_json_missing_field() {
1224 let mut struct_defs = TypeDefMap::new();
1226 struct_defs.insert(
1227 "Person".to_string(),
1228 vec![
1229 ("name".to_string(), "string".to_string()),
1230 ("age".to_string(), "uint256".to_string()),
1231 ],
1232 );
1233
1234 let json_str = r#"{ "name": "Alice" }"#;
1236
1237 let type_description = "Person(uint256 age,string name)";
1239 let ty = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1240
1241 let json_value: Value = serde_json::from_str(json_str).unwrap();
1243 let result = parse_json_as(&json_value, &ty);
1244
1245 assert!(result.is_err());
1247 assert!(result.unwrap_err().to_string().contains("field \"age\" not found in JSON object"));
1248 }
1249
1250 #[test]
1251 fn test_serialize_json_with_struct_def_order() {
1252 let mut struct_defs = TypeDefMap::new();
1254 struct_defs.insert(
1255 "Item".to_string(),
1256 vec![
1257 ("name".to_string(), "string".to_string()),
1258 ("id".to_string(), "uint256".to_string()),
1259 ("active".to_string(), "bool".to_string()),
1260 ],
1261 );
1262
1263 let item_struct = DynSolValue::CustomStruct {
1265 name: "Item".to_string(),
1266 prop_names: vec!["name".to_string(), "id".to_string(), "active".to_string()],
1267 tuple: vec![
1268 DynSolValue::String("Test Item".to_string()),
1269 DynSolValue::Uint(U256::from(123), 256),
1270 DynSolValue::Bool(true),
1271 ],
1272 };
1273
1274 let json_value =
1276 serialize_value_as_json(item_struct, Some(&struct_defs.into()), false).unwrap();
1277 let json_string = serde_json::to_string(&json_value).unwrap();
1278 assert_eq!(json_string, r#"{"name":"Test Item","id":123,"active":true}"#);
1279 }
1280
1281 #[test]
1282 fn test_json_full_cycle_typed_with_struct_defs() {
1283 let mut struct_defs = TypeDefMap::new();
1285 struct_defs.insert(
1286 "Wallet".to_string(),
1287 vec![
1288 ("owner".to_string(), "address".to_string()),
1289 ("balance".to_string(), "uint256".to_string()),
1290 ("id".to_string(), "bytes32".to_string()),
1291 ],
1292 );
1293
1294 let owner_address = Address::from([1; 20]);
1296 let wallet_id = B256::from([2; 32]);
1297 let original_wallet = DynSolValue::CustomStruct {
1298 name: "Wallet".to_string(),
1299 prop_names: vec!["owner".to_string(), "balance".to_string(), "id".to_string()],
1300 tuple: vec![
1301 DynSolValue::Address(owner_address),
1302 DynSolValue::Uint(U256::from(5000), 256),
1303 DynSolValue::FixedBytes(wallet_id, 32),
1304 ],
1305 };
1306
1307 let json_value = serialize_value_as_json(
1309 original_wallet.clone(),
1310 Some(&struct_defs.clone().into()),
1311 false,
1312 )
1313 .unwrap();
1314 let json_string = serde_json::to_string(&json_value).unwrap();
1315 assert_eq!(
1316 json_string,
1317 format!(r#"{{"owner":"{owner_address}","balance":5000,"id":"{wallet_id}"}}"#)
1318 );
1319
1320 let type_description = "Wallet(uint256 balance,bytes32 id,address owner)";
1322 let resolved_type = resolve_type(type_description, Some(&struct_defs.into())).unwrap();
1323
1324 let parsed_value = parse_json_as(&json_value, &resolved_type).unwrap();
1327 assert_eq!(parsed_value, original_wallet);
1328 }
1329}