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 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(
154 value: DynSolValue,
155 defs: Option<&StructDefinitions>,
156) -> Result<Value> {
157 if let Some(defs) = defs {
158 _serialize_value_as_json(value, defs)
159 } else {
160 _serialize_value_as_json(value, &StructDefinitions::default())
161 }
162}
163
164fn _serialize_value_as_json(value: DynSolValue, defs: &StructDefinitions) -> Result<Value> {
165 match value {
166 DynSolValue::Bool(b) => Ok(Value::Bool(b)),
167 DynSolValue::String(s) => {
168 if let Ok(map) = serde_json::from_str(&s) {
171 Ok(Value::Object(map))
172 } else {
173 Ok(Value::String(s))
174 }
175 }
176 DynSolValue::Bytes(b) => Ok(Value::String(hex::encode_prefixed(b))),
177 DynSolValue::FixedBytes(b, size) => Ok(Value::String(hex::encode_prefixed(&b[..size]))),
178 DynSolValue::Int(i, _) => {
179 if let Ok(n) = i64::try_from(i) {
180 Ok(Value::Number(n.into()))
182 } else {
183 Ok(Value::String(i.to_string()))
186 }
187 }
188 DynSolValue::Uint(i, _) => {
189 if let Ok(n) = u64::try_from(i) {
190 Ok(Value::Number(n.into()))
192 } else {
193 Ok(Value::String(i.to_string()))
196 }
197 }
198 DynSolValue::Address(a) => Ok(Value::String(a.to_string())),
199 DynSolValue::Array(e) | DynSolValue::FixedArray(e) => Ok(Value::Array(
200 e.into_iter().map(|v| _serialize_value_as_json(v, defs)).collect::<Result<_>>()?,
201 )),
202 DynSolValue::CustomStruct { name, prop_names, tuple } => {
203 let values = tuple
204 .into_iter()
205 .map(|v| _serialize_value_as_json(v, defs))
206 .collect::<Result<Vec<_>>>()?;
207 let mut map: HashMap<String, Value> = prop_names.into_iter().zip(values).collect();
208
209 if let Some(fields) = defs.get(&name)? {
211 let mut ordered_map = Map::with_capacity(fields.len());
212 for (field_name, _) in fields {
213 if let Some(serialized_value) = map.remove(field_name) {
214 ordered_map.insert(field_name.clone(), serialized_value);
215 }
216 }
217 return Ok(Value::Object(ordered_map));
219 }
220
221 Ok(Value::Object(map.into_iter().collect::<Map<String, Value>>()))
223 }
224 DynSolValue::Tuple(values) => Ok(Value::Array(
225 values.into_iter().map(|v| _serialize_value_as_json(v, defs)).collect::<Result<_>>()?,
226 )),
227 DynSolValue::Function(_) => eyre::bail!("cannot serialize function pointer"),
228 }
229}
230
231pub type TypeDefMap = BTreeMap<String, Vec<(String, String)>>;
234
235#[derive(Debug, Clone, Default)]
236pub struct StructDefinitions(TypeDefMap);
237
238impl From<TypeDefMap> for StructDefinitions {
239 fn from(map: TypeDefMap) -> Self {
240 Self::new(map)
241 }
242}
243
244impl StructDefinitions {
245 pub fn new(map: TypeDefMap) -> Self {
246 Self(map)
247 }
248
249 pub fn keys(&self) -> impl Iterator<Item = &String> {
250 self.0.keys()
251 }
252
253 pub fn values(&self) -> impl Iterator<Item = &[(String, String)]> {
254 self.0.values().map(|v| v.as_slice())
255 }
256
257 pub fn get(&self, key: &str) -> eyre::Result<Option<&[(String, String)]>> {
258 if let Some(value) = self.0.get(key) {
259 return Ok(Some(value));
260 }
261
262 let matches: Vec<&[(String, String)]> = self
263 .0
264 .iter()
265 .filter_map(|(k, v)| {
266 if let Some((_, struct_name)) = k.split_once('.')
267 && struct_name == key
268 {
269 return Some(v.as_slice());
270 }
271 None
272 })
273 .collect();
274
275 match matches.len() {
276 0 => Ok(None),
277 1 => Ok(Some(matches[0])),
278 _ => eyre::bail!(
279 "there are several structs with the same name. Use `<contract_name>.{key}` instead."
280 ),
281 }
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use alloy_primitives::{U256, address};
289
290 #[test]
291 fn parse_hex_uint() {
292 let ty = DynSolType::Uint(256);
293
294 let values = parse_tokens(std::iter::once((&ty, "100"))).unwrap();
295 assert_eq!(values, [DynSolValue::Uint(U256::from(100), 256)]);
296
297 let val: U256 = U256::from(100u64);
298 let hex_val = format!("0x{val:x}");
299 let values = parse_tokens(std::iter::once((&ty, hex_val.as_str()))).unwrap();
300 assert_eq!(values, [DynSolValue::Uint(U256::from(100), 256)]);
301 }
302
303 #[test]
304 fn format_addr() {
305 assert_eq!(
307 format_token(&DynSolValue::Address(address!(
308 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
309 ))),
310 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
311 );
312
313 assert_ne!(
315 format_token(&DynSolValue::Address(address!(
316 "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359"
317 ))),
318 "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359"
319 );
320 }
321}