1use super::{format_int_exp, format_uint_exp};
2use alloy_dyn_abi::{DynSolType, DynSolValue};
3use alloy_primitives::hex;
4use eyre::Result;
5use serde_json::{Map, Value};
6use std::{
7 collections::{BTreeMap, HashMap},
8 fmt,
9};
10
11struct DynValueFormatter {
13 raw: bool,
14}
15
16impl DynValueFormatter {
17 fn value(&self, value: &DynSolValue, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match value {
20 DynSolValue::Address(inner) => write!(f, "{inner}"),
21 DynSolValue::Function(inner) => write!(f, "{inner}"),
22 DynSolValue::Bytes(inner) => f.write_str(&hex::encode_prefixed(inner)),
23 DynSolValue::FixedBytes(word, size) => {
24 f.write_str(&hex::encode_prefixed(&word[..*size]))
25 }
26 DynSolValue::Uint(inner, _) => {
27 if self.raw {
28 write!(f, "{inner}")
29 } else {
30 f.write_str(&format_uint_exp(*inner))
31 }
32 }
33 DynSolValue::Int(inner, _) => {
34 if self.raw {
35 write!(f, "{inner}")
36 } else {
37 f.write_str(&format_int_exp(*inner))
38 }
39 }
40 DynSolValue::Array(values) | DynSolValue::FixedArray(values) => {
41 f.write_str("[")?;
42 self.list(values, f)?;
43 f.write_str("]")
44 }
45 DynSolValue::Tuple(values) => self.tuple(values, f),
46 DynSolValue::String(inner) => {
47 if self.raw {
48 write!(f, "{}", inner.escape_debug())
49 } else {
50 write!(f, "{inner:?}") }
52 }
53 DynSolValue::Bool(inner) => write!(f, "{inner}"),
54 DynSolValue::CustomStruct { name, prop_names, tuple } => {
55 if self.raw {
56 return self.tuple(tuple, f);
57 }
58
59 f.write_str(name)?;
60
61 if prop_names.len() == tuple.len() {
62 f.write_str("({ ")?;
63
64 for (i, (prop_name, value)) in std::iter::zip(prop_names, tuple).enumerate() {
65 if i > 0 {
66 f.write_str(", ")?;
67 }
68 f.write_str(prop_name)?;
69 f.write_str(": ")?;
70 self.value(value, f)?;
71 }
72
73 f.write_str(" })")
74 } else {
75 self.tuple(tuple, f)
76 }
77 }
78 }
79 }
80
81 fn list(&self, values: &[DynSolValue], f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 for (i, value) in values.iter().enumerate() {
84 if i > 0 {
85 f.write_str(", ")?;
86 }
87 self.value(value, f)?;
88 }
89 Ok(())
90 }
91
92 fn tuple(&self, values: &[DynSolValue], f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.write_str("(")?;
95 self.list(values, f)?;
96 f.write_str(")")
97 }
98}
99
100struct DynValueDisplay<'a> {
102 value: &'a DynSolValue,
104 formatter: DynValueFormatter,
106}
107
108impl<'a> DynValueDisplay<'a> {
109 const fn new(value: &'a DynSolValue, raw: bool) -> Self {
111 Self { value, formatter: DynValueFormatter { raw } }
112 }
113}
114
115impl fmt::Display for DynValueDisplay<'_> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 self.formatter.value(self.value, f)
118 }
119}
120
121pub fn parse_tokens<'a, I: IntoIterator<Item = (&'a DynSolType, &'a str)>>(
123 params: I,
124) -> alloy_dyn_abi::Result<Vec<DynSolValue>> {
125 params.into_iter().map(|(param, value)| DynSolType::coerce_str(param, value)).collect()
126}
127
128pub fn format_tokens(tokens: &[DynSolValue]) -> impl Iterator<Item = String> + '_ {
130 tokens.iter().map(format_token)
131}
132
133pub fn format_tokens_raw(tokens: &[DynSolValue]) -> impl Iterator<Item = String> + '_ {
135 tokens.iter().map(format_token_raw)
136}
137
138pub fn format_token(value: &DynSolValue) -> String {
140 DynValueDisplay::new(value, false).to_string()
141}
142
143pub fn format_token_raw(value: &DynSolValue) -> String {
149 DynValueDisplay::new(value, true).to_string()
150}
151
152pub fn serialize_value_as_json(
158 value: DynSolValue,
159 defs: Option<&StructDefinitions>,
160 strict: bool,
161) -> Result<Value> {
162 if let Some(defs) = defs {
163 _serialize_value_as_json(value, defs, strict)
164 } else {
165 _serialize_value_as_json(value, &StructDefinitions::default(), strict)
166 }
167}
168
169fn _serialize_value_as_json(
170 value: DynSolValue,
171 defs: &StructDefinitions,
172 strict: bool,
173) -> Result<Value> {
174 match value {
175 DynSolValue::Bool(b) => Ok(Value::Bool(b)),
176 DynSolValue::String(s) => {
177 if let Ok(map) = serde_json::from_str(&s) {
180 Ok(Value::Object(map))
181 } else {
182 Ok(Value::String(s))
183 }
184 }
185 DynSolValue::Bytes(b) => Ok(Value::String(hex::encode_prefixed(b))),
186 DynSolValue::FixedBytes(b, size) => Ok(Value::String(hex::encode_prefixed(&b[..size]))),
187 DynSolValue::Int(i, bits) => {
188 match (i64::try_from(i), strict) {
189 (Ok(n), true) if bits <= 64 => Ok(Value::Number(n.into())),
191 (Ok(n), false) => Ok(Value::Number(n.into())),
193 _ => Ok(Value::String(i.to_string())),
196 }
197 }
198 DynSolValue::Uint(i, bits) => {
199 match (u64::try_from(i), strict) {
200 (Ok(n), true) if bits <= 64 => Ok(Value::Number(n.into())),
202 (Ok(n), false) => Ok(Value::Number(n.into())),
204 _ => Ok(Value::String(i.to_string())),
207 }
208 }
209 DynSolValue::Address(a) => Ok(Value::String(a.to_string())),
210 DynSolValue::Array(e) | DynSolValue::FixedArray(e) => Ok(Value::Array(
211 e.into_iter()
212 .map(|v| _serialize_value_as_json(v, defs, strict))
213 .collect::<Result<_>>()?,
214 )),
215 DynSolValue::CustomStruct { name, prop_names, tuple } => {
216 let values = tuple
217 .into_iter()
218 .map(|v| _serialize_value_as_json(v, defs, strict))
219 .collect::<Result<Vec<_>>>()?;
220 let mut map: HashMap<String, Value> = prop_names.into_iter().zip(values).collect();
221
222 if let Some(fields) = defs.get(&name)? {
224 let mut ordered_map = Map::with_capacity(fields.len());
225 for (field_name, _) in fields {
226 if let Some(serialized_value) = map.remove(field_name) {
227 ordered_map.insert(field_name.clone(), serialized_value);
228 }
229 }
230 return Ok(Value::Object(ordered_map));
232 }
233
234 Ok(Value::Object(map.into_iter().collect::<Map<String, Value>>()))
236 }
237 DynSolValue::Tuple(values) => Ok(Value::Array(
238 values
239 .into_iter()
240 .map(|v| _serialize_value_as_json(v, defs, strict))
241 .collect::<Result<_>>()?,
242 )),
243 DynSolValue::Function(_) => {
244 eyre::bail!("cannot serialize function pointer");
245 }
246 }
247}
248
249pub type TypeDefMap = BTreeMap<String, Vec<(String, String)>>;
252
253#[derive(Debug, Clone, Default)]
254pub struct StructDefinitions(TypeDefMap);
255
256impl From<TypeDefMap> for StructDefinitions {
257 fn from(map: TypeDefMap) -> Self {
258 Self::new(map)
259 }
260}
261
262impl StructDefinitions {
263 pub const fn new(map: TypeDefMap) -> Self {
264 Self(map)
265 }
266
267 pub fn keys(&self) -> impl Iterator<Item = &String> {
268 self.0.keys()
269 }
270
271 pub fn values(&self) -> impl Iterator<Item = &[(String, String)]> {
272 self.0.values().map(|v| v.as_slice())
273 }
274
275 pub fn get(&self, key: &str) -> eyre::Result<Option<&[(String, String)]>> {
276 if let Some(value) = self.0.get(key) {
277 return Ok(Some(value));
278 }
279
280 let matches: Vec<&[(String, String)]> = self
281 .0
282 .iter()
283 .filter_map(|(k, v)| {
284 if let Some((_, struct_name)) = k.split_once('.')
285 && struct_name == key
286 {
287 return Some(v.as_slice());
288 }
289 None
290 })
291 .collect();
292
293 match matches.len() {
294 0 => Ok(None),
295 1 => Ok(Some(matches[0])),
296 _ => {
297 eyre::bail!(
298 "there are several structs with the same name. Use `<contract_name>.{key}` instead."
299 );
300 }
301 }
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use alloy_primitives::{U256, address};
309
310 #[test]
311 fn parse_hex_uint() {
312 let ty = DynSolType::Uint(256);
313
314 let values = parse_tokens(std::iter::once((&ty, "100"))).unwrap();
315 assert_eq!(values, [DynSolValue::Uint(U256::from(100), 256)]);
316
317 let val: U256 = U256::from(100u64);
318 let hex_val = format!("0x{val:x}");
319 let values = parse_tokens(std::iter::once((&ty, hex_val.as_str()))).unwrap();
320 assert_eq!(values, [DynSolValue::Uint(U256::from(100), 256)]);
321 }
322
323 #[test]
324 fn format_addr() {
325 assert_eq!(
327 format_token(&DynSolValue::Address(address!(
328 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
329 ))),
330 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
331 );
332
333 assert_ne!(
335 format_token(&DynSolValue::Address(address!(
336 "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359"
337 ))),
338 "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359"
339 );
340 }
341
342 #[test]
343 fn strict_uint256_array_is_homogeneous() {
344 let small = U256::from(1u64);
345 let big = U256::from(1u64) << 200;
346
347 let arr =
348 DynSolValue::Array(vec![DynSolValue::Uint(small, 256), DynSolValue::Uint(big, 256)]);
349
350 let json = serialize_value_as_json(arr, None, true).unwrap();
351
352 assert_eq!(
353 json,
354 serde_json::json!([
355 "1",
356 "1606938044258990275541962092341162602522202993782792835301376"
357 ])
358 );
359 }
360
361 proptest::proptest! {
362 #[test]
363 fn test_serialize_uint_as_json(l in 0u64..u64::MAX, h in ((u64::MAX as u128) + 1)..u128::MAX) {
364 let l_min_bits = (64 - l.leading_zeros()) as usize;
365 let h_min_bits = (128 - h.leading_zeros()) as usize;
366
367 assert_eq!(
369 serialize_value_as_json(DynSolValue::Uint(l.try_into().unwrap(), l_min_bits), None, false).unwrap(),
370 serde_json::json!(l)
371 );
372 assert_eq!(
374 serialize_value_as_json(DynSolValue::Uint(h.try_into().unwrap(), h_min_bits), None, false).unwrap(),
375 serde_json::json!(h.to_string())
376 );
377
378 assert_eq!(
381 serialize_value_as_json(DynSolValue::Uint(l.try_into().unwrap(), l_min_bits), None, true).unwrap(),
382 serde_json::json!(l)
383 );
384 assert_eq!(
387 serialize_value_as_json(DynSolValue::Uint(l.try_into().unwrap(), h_min_bits), None, true).unwrap(),
388 serde_json::json!(l.to_string())
389 );
390 assert_eq!(
392 serialize_value_as_json(DynSolValue::Uint(h.try_into().unwrap(), h_min_bits), None, true).unwrap(),
393 serde_json::json!(h.to_string())
394 );
395 }
396
397 #[test]
398 fn test_serialize_int_as_json(l in 0i64..=i64::MAX, h in ((i64::MAX as i128) + 1)..=i128::MAX) {
399 let l_min_bits = (64 - (l as u64).leading_zeros()) as usize + 1;
400 let h_min_bits = (128 - (h as u128).leading_zeros()) as usize + 1;
401
402 assert_eq!(
404 serialize_value_as_json(DynSolValue::Int(l.try_into().unwrap(), l_min_bits), None, false).unwrap(),
405 serde_json::json!(l)
406 );
407 assert_eq!(
409 serialize_value_as_json(DynSolValue::Int(h.try_into().unwrap(), h_min_bits), None, false).unwrap(),
410 serde_json::json!(h.to_string())
411 );
412
413 assert_eq!(
416 serialize_value_as_json(DynSolValue::Int(l.try_into().unwrap(), l_min_bits), None, true).unwrap(),
417 serde_json::json!(l)
418 );
419 assert_eq!(
422 serialize_value_as_json(DynSolValue::Int(l.try_into().unwrap(), h_min_bits), None, true).unwrap(),
423 serde_json::json!(l.to_string())
424 );
425 assert_eq!(
427 serialize_value_as_json(DynSolValue::Int(h.try_into().unwrap(), h_min_bits), None, true).unwrap(),
428 serde_json::json!(h.to_string())
429 );
430 }
431 }
432}