edb_common/types/
sol_value.rs

1// EDB - Ethereum Debugger
2// Copyright (C) 2024 Zhuo Zhang and Wuqi Zhang
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17use std::ops::{Deref, DerefMut};
18
19use alloy_dyn_abi::DynSolValue;
20use alloy_primitives::hex;
21use alloy_primitives::{Address, FixedBytes, I256, U256};
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23
24/// Wrapper around DynSolValue with custom serialization for EDB debugging and analysis
25#[derive(Debug, Clone)]
26pub struct EdbSolValue(pub DynSolValue);
27
28impl From<DynSolValue> for EdbSolValue {
29    fn from(value: DynSolValue) -> Self {
30        Self(value)
31    }
32}
33
34impl From<EdbSolValue> for DynSolValue {
35    fn from(value: EdbSolValue) -> Self {
36        value.0
37    }
38}
39
40impl Deref for EdbSolValue {
41    type Target = DynSolValue;
42
43    fn deref(&self) -> &Self::Target {
44        &self.0
45    }
46}
47
48impl DerefMut for EdbSolValue {
49    fn deref_mut(&mut self) -> &mut Self::Target {
50        &mut self.0
51    }
52}
53
54/// Serializable representation of DynSolValue for JSON and other formats with complete type information preservation
55#[derive(Serialize, Deserialize)]
56#[serde(tag = "type", content = "value")]
57enum SerializedDynSolValue {
58    /// Boolean value
59    Bool(bool),
60    /// Signed integer with bit size specification
61    Int { value: I256, bits: usize },
62    /// Unsigned integer with bit size specification
63    Uint { value: U256, bits: usize },
64    /// Fixed-size bytes with size specification
65    FixedBytes { value: FixedBytes<32>, size: usize },
66    /// Ethereum address (20 bytes)
67    Address(Address),
68    /// Function selector (24 bytes)
69    Function(FixedBytes<24>),
70    /// Dynamic bytes array
71    Bytes(Vec<u8>),
72    /// String value
73    String(String),
74    /// Dynamic array of values
75    Array(Vec<SerializedDynSolValue>),
76    /// Fixed-size array of values
77    FixedArray(Vec<SerializedDynSolValue>),
78    /// Tuple of multiple values
79    Tuple(Vec<SerializedDynSolValue>),
80    /// Custom struct with name, property names, and tuple data
81    CustomStruct { name: String, prop_names: Vec<String>, tuple: Vec<SerializedDynSolValue> },
82}
83
84impl From<&DynSolValue> for SerializedDynSolValue {
85    fn from(value: &DynSolValue) -> Self {
86        match value {
87            DynSolValue::Bool(b) => Self::Bool(*b),
88            DynSolValue::Int(i, bits) => Self::Int { value: *i, bits: *bits },
89            DynSolValue::Uint(u, bits) => Self::Uint { value: *u, bits: *bits },
90            DynSolValue::FixedBytes(bytes, size) => Self::FixedBytes { value: *bytes, size: *size },
91            DynSolValue::Address(addr) => Self::Address(*addr),
92            DynSolValue::Function(func) => Self::Function(func.0),
93            DynSolValue::Bytes(bytes) => Self::Bytes(bytes.clone()),
94            DynSolValue::String(s) => Self::String(s.clone()),
95            DynSolValue::Array(arr) => Self::Array(arr.iter().map(Into::into).collect()),
96            DynSolValue::FixedArray(arr) => Self::FixedArray(arr.iter().map(Into::into).collect()),
97            DynSolValue::Tuple(tuple) => Self::Tuple(tuple.iter().map(Into::into).collect()),
98            DynSolValue::CustomStruct { name, prop_names, tuple } => Self::CustomStruct {
99                name: name.clone(),
100                prop_names: prop_names.clone(),
101                tuple: tuple.iter().map(Into::into).collect(),
102            },
103        }
104    }
105}
106
107impl From<SerializedDynSolValue> for DynSolValue {
108    fn from(value: SerializedDynSolValue) -> Self {
109        match value {
110            SerializedDynSolValue::Bool(b) => Self::Bool(b),
111            SerializedDynSolValue::Int { value, bits } => Self::Int(value, bits),
112            SerializedDynSolValue::Uint { value, bits } => Self::Uint(value, bits),
113            SerializedDynSolValue::FixedBytes { value, size } => Self::FixedBytes(value, size),
114            SerializedDynSolValue::Address(addr) => Self::Address(addr),
115            SerializedDynSolValue::Function(func) => {
116                Self::Function(alloy_primitives::Function::from(func))
117            }
118            SerializedDynSolValue::Bytes(bytes) => Self::Bytes(bytes),
119            SerializedDynSolValue::String(s) => Self::String(s),
120            SerializedDynSolValue::Array(arr) => {
121                Self::Array(arr.into_iter().map(Into::into).collect())
122            }
123            SerializedDynSolValue::FixedArray(arr) => {
124                Self::FixedArray(arr.into_iter().map(Into::into).collect())
125            }
126            SerializedDynSolValue::Tuple(tuple) => {
127                Self::Tuple(tuple.into_iter().map(Into::into).collect())
128            }
129            SerializedDynSolValue::CustomStruct { name, prop_names, tuple } => Self::CustomStruct {
130                name,
131                prop_names,
132                tuple: tuple.into_iter().map(Into::into).collect(),
133            },
134        }
135    }
136}
137
138impl Serialize for EdbSolValue {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: Serializer,
142    {
143        let serialized = SerializedDynSolValue::from(&self.0);
144        serialized.serialize(serializer)
145    }
146}
147
148impl<'de> Deserialize<'de> for EdbSolValue {
149    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150    where
151        D: Deserializer<'de>,
152    {
153        let serialized = SerializedDynSolValue::deserialize(deserializer)?;
154        Ok(Self(serialized.into()))
155    }
156}
157
158/// Trait for formatting Solidity values into human-readable strings.
159pub trait SolValueFormatter {
160    /// Formats a Solidity value into a human-readable string.
161    ///
162    /// # Arguments
163    ///
164    /// * `with_ty` - If true, includes type information in the output (e.g., "uint256(123)")
165    ///
166    /// # Returns
167    ///
168    /// A formatted string representation of the value.
169    fn format_value(&self, ctx: &SolValueFormatterContext) -> String {
170        self.format_value_with_indent(ctx, 0)
171    }
172
173    /// Formats a Solidity value with specific indentation level.
174    fn format_value_with_indent(
175        &self,
176        ctx: &SolValueFormatterContext,
177        indent_level: usize,
178    ) -> String;
179
180    /// Returns the Solidity type of this value as a string.
181    ///
182    /// # Returns
183    ///
184    /// The Solidity type (e.g., "uint256", "address", "bytes32[]")
185    fn format_type(&self) -> String;
186}
187
188/// Configuration context for formatting Solidity values with various display options and address resolution
189#[derive(Default)]
190pub struct SolValueFormatterContext {
191    /// Optional function to resolve addresses to human-readable names or labels
192    pub resolve_address: Option<Box<dyn Fn(Address) -> Option<String>>>,
193    /// Whether to include type information in the formatted output
194    pub with_ty: bool,
195    /// Whether to shorten long arrays, strings, and other large data structures
196    pub shorten_long: bool,
197    /// Whether to use multi-line formatting for better readability of complex structures
198    pub multi_line: bool,
199}
200
201impl SolValueFormatterContext {
202    /// Create a new default formatter context
203    pub fn new() -> Self {
204        Self::default()
205    }
206
207    /// Configure whether to include type information in formatted output
208    pub fn with_ty(mut self, with_ty: bool) -> Self {
209        self.with_ty = with_ty;
210        self
211    }
212
213    /// Configure whether to shorten long data structures for readability
214    pub fn shorten_long(mut self, shorten_long: bool) -> Self {
215        self.shorten_long = shorten_long;
216        self
217    }
218
219    /// Configure whether to use multi-line formatting for complex structures
220    pub fn multi_line(mut self, multi_line: bool) -> Self {
221        self.multi_line = multi_line;
222        self
223    }
224}
225
226impl SolValueFormatter for DynSolValue {
227    fn format_value_with_indent(
228        &self,
229        ctx: &SolValueFormatterContext,
230        indent_level: usize,
231    ) -> String {
232        let value_str = match self {
233            Self::Bool(b) => b.to_string(),
234
235            Self::Int(n, bits) => {
236                if ctx.with_ty {
237                    format!("int{bits}({n})")
238                } else {
239                    n.to_string()
240                }
241            }
242
243            Self::Uint(n, bits) => {
244                if ctx.with_ty {
245                    format!("uint{bits}({n})")
246                } else {
247                    n.to_string()
248                }
249            }
250
251            Self::Address(addr) => {
252                if let Some(label) = ctx.resolve_address.as_ref().and_then(|f| f(*addr)) {
253                    label
254                } else {
255                    let addr_str = if !ctx.shorten_long {
256                        addr.to_checksum(None)
257                    } else if *addr == Address::ZERO {
258                        "0x0000000000000000".to_string()
259                    } else {
260                        let addr_str = addr.to_checksum(None);
261                        // Show more characters for better identification: 8 chars + ... + 6 chars
262                        format!("{}...{}", &addr_str[..8], &addr_str[addr_str.len() - 6..])
263                    };
264
265                    if ctx.with_ty {
266                        format!("address({addr_str})")
267                    } else {
268                        addr_str
269                    }
270                }
271            }
272
273            Self::Function(func) => {
274                format!("0x{}", hex::encode(func.as_slice()))
275            }
276
277            Self::FixedBytes(bytes, size) => {
278                if ctx.with_ty {
279                    format!("bytes{}(0x{})", size, hex::encode(bytes))
280                } else {
281                    format!("0x{}", hex::encode(bytes))
282                }
283            }
284
285            Self::Bytes(bytes) => {
286                if bytes.len() <= 32 || !ctx.shorten_long {
287                    format!("0x{}", hex::encode(bytes))
288                } else {
289                    format!("0x{}...[{} bytes]", hex::encode(&bytes[..16]), bytes.len())
290                }
291            }
292
293            Self::String(s) => {
294                if s.len() <= 64 || !ctx.shorten_long {
295                    format!("\"{}\"", s.replace('\"', "\\\""))
296                } else {
297                    format!("\"{}...\"[{} chars]", &s[..32].replace('\"', "\\\""), s.len())
298                }
299            }
300
301            Self::Array(arr) => format_array(arr, false, ctx, indent_level),
302
303            Self::FixedArray(arr) => format_array(arr, true, ctx, indent_level),
304
305            Self::Tuple(tuple) => format_tuple(tuple, ctx, indent_level),
306
307            Self::CustomStruct { name, prop_names, tuple } => {
308                if prop_names.is_empty() {
309                    format!("{}{}", name, format_tuple(tuple, ctx, indent_level))
310                } else {
311                    format_custom_struct(name, prop_names, tuple, ctx, indent_level)
312                }
313            }
314        };
315
316        value_str
317    }
318
319    fn format_type(&self) -> String {
320        match self {
321            Self::Bool(_) => "bool".to_string(),
322            Self::Int(_, bits) => format!("int{bits}"),
323            Self::Uint(_, bits) => format!("uint{bits}"),
324            Self::Address(_) => "address".to_string(),
325            Self::Function(_) => "function".to_string(),
326            Self::FixedBytes(_, size) => format!("bytes{size}"),
327            Self::Bytes(_) => "bytes".to_string(),
328            Self::String(_) => "string".to_string(),
329            Self::Array(arr) => {
330                if let Some(first) = arr.first() {
331                    format!("{}[]", first.format_type())
332                } else {
333                    "unknown[]".to_string()
334                }
335            }
336            Self::FixedArray(arr) => {
337                if let Some(first) = arr.first() {
338                    format!("{}[{}]", first.format_type(), arr.len())
339                } else {
340                    format!("unknown[{}]", arr.len())
341                }
342            }
343            Self::Tuple(tuple) => {
344                let types: Vec<String> = tuple.iter().map(|v| v.format_type()).collect();
345                format!("({})", types.join(","))
346            }
347            Self::CustomStruct { name, .. } => name.clone(),
348        }
349    }
350}
351
352/// Helper function to create indentation string
353fn make_indent(indent_level: usize) -> String {
354    "  ".repeat(indent_level)
355}
356
357fn format_array(
358    arr: &[DynSolValue],
359    is_fixed: bool,
360    ctx: &SolValueFormatterContext,
361    indent_level: usize,
362) -> String {
363    const MAX_DISPLAY_ITEMS: usize = 5;
364
365    if arr.is_empty() {
366        return "[]".to_string();
367    }
368
369    if ctx.multi_line && arr.len() > 1 {
370        let child_indent = make_indent(indent_level + 1);
371        let current_indent = make_indent(indent_level);
372
373        let items: Vec<String> = if ctx.shorten_long && arr.len() > MAX_DISPLAY_ITEMS {
374            let mut items = arr
375                .iter()
376                .take(3)
377                .map(|v| {
378                    format!("{}{}", child_indent, v.format_value_with_indent(ctx, indent_level + 1))
379                })
380                .collect::<Vec<_>>();
381            let suffix = if is_fixed {
382                format!("{}...[{} total]", child_indent, arr.len())
383            } else {
384                format!("{}...[{} items]", child_indent, arr.len())
385            };
386            items.push(suffix);
387            items
388        } else {
389            arr.iter()
390                .map(|v| {
391                    format!("{}{}", child_indent, v.format_value_with_indent(ctx, indent_level + 1))
392                })
393                .collect()
394        };
395        format!("[\n{}\n{}]", items.join(",\n"), current_indent)
396    } else if arr.len() <= MAX_DISPLAY_ITEMS || !ctx.shorten_long {
397        let items: Vec<String> =
398            arr.iter().map(|v| v.format_value_with_indent(ctx, indent_level)).collect();
399        format!("[{}]", items.join(", "))
400    } else {
401        let first_items: Vec<String> =
402            arr.iter().take(3).map(|v| v.format_value_with_indent(ctx, indent_level)).collect();
403
404        let suffix = if is_fixed {
405            format!(", ...[{} total]", arr.len())
406        } else {
407            format!(", ...[{} items]", arr.len())
408        };
409
410        format!("[{}{}]", first_items.join(", "), suffix)
411    }
412}
413
414fn format_tuple(
415    tuple: &[DynSolValue],
416    ctx: &SolValueFormatterContext,
417    indent_level: usize,
418) -> String {
419    if tuple.is_empty() {
420        return "()".to_string();
421    }
422
423    if tuple.len() == 1 {
424        return format!("({})", tuple[0].format_value_with_indent(ctx, indent_level));
425    }
426
427    const MAX_DISPLAY_FIELDS: usize = 4;
428
429    if ctx.multi_line && tuple.len() > 1 {
430        let child_indent = make_indent(indent_level + 1);
431        let current_indent = make_indent(indent_level);
432
433        let items: Vec<String> = if ctx.shorten_long && tuple.len() > MAX_DISPLAY_FIELDS {
434            let mut items = tuple
435                .iter()
436                .take(3)
437                .map(|v| {
438                    format!("{}{}", child_indent, v.format_value_with_indent(ctx, indent_level + 1))
439                })
440                .collect::<Vec<_>>();
441            items.push(format!("{}...[{} fields]", child_indent, tuple.len()));
442            items
443        } else {
444            tuple
445                .iter()
446                .map(|v| {
447                    format!("{}{}", child_indent, v.format_value_with_indent(ctx, indent_level + 1))
448                })
449                .collect()
450        };
451        format!("(\n{}\n{})", items.join(",\n"), current_indent)
452    } else if tuple.len() <= MAX_DISPLAY_FIELDS || !ctx.shorten_long {
453        let items: Vec<String> =
454            tuple.iter().map(|v| v.format_value_with_indent(ctx, indent_level)).collect();
455        format!("({})", items.join(", "))
456    } else {
457        let first_items: Vec<String> =
458            tuple.iter().take(3).map(|v| v.format_value_with_indent(ctx, indent_level)).collect();
459        format!("({}, ...[{} fields])", first_items.join(", "), tuple.len())
460    }
461}
462
463fn format_custom_struct(
464    name: &str,
465    prop_names: &[String],
466    tuple: &[DynSolValue],
467    ctx: &SolValueFormatterContext,
468    indent_level: usize,
469) -> String {
470    const MAX_DISPLAY_FIELDS: usize = 4;
471
472    if ctx.multi_line && tuple.len() > 1 {
473        let child_indent = make_indent(indent_level + 1);
474        let current_indent = make_indent(indent_level);
475
476        let fields: Vec<String> = if ctx.shorten_long && tuple.len() > MAX_DISPLAY_FIELDS {
477            let mut fields = tuple
478                .iter()
479                .zip(prop_names.iter())
480                .take(3)
481                .map(|(value, field_name)| {
482                    format!(
483                        "{}{}: {}",
484                        child_indent,
485                        field_name,
486                        value.format_value_with_indent(ctx, indent_level + 1)
487                    )
488                })
489                .collect::<Vec<_>>();
490            fields.push(format!("{}...[{} fields]", child_indent, tuple.len()));
491            fields
492        } else {
493            tuple
494                .iter()
495                .zip(prop_names.iter())
496                .map(|(value, field_name)| {
497                    format!(
498                        "{}{}: {}",
499                        child_indent,
500                        field_name,
501                        value.format_value_with_indent(ctx, indent_level + 1)
502                    )
503                })
504                .collect()
505        };
506
507        if ctx.with_ty {
508            format!("{}{{\n{}\n{}}}", name, fields.join(",\n"), current_indent)
509        } else {
510            format!("{{\n{}\n{}}}", fields.join(",\n"), current_indent)
511        }
512    } else {
513        let fields: Vec<String> = if ctx.shorten_long && tuple.len() > MAX_DISPLAY_FIELDS {
514            let mut fields = tuple
515                .iter()
516                .zip(prop_names.iter())
517                .take(3)
518                .map(|(value, field_name)| {
519                    format!("{}: {}", field_name, value.format_value_with_indent(ctx, indent_level))
520                })
521                .collect::<Vec<_>>();
522            fields.push(format!("...[{} fields]", tuple.len()));
523            fields
524        } else {
525            tuple
526                .iter()
527                .zip(prop_names.iter())
528                .map(|(value, field_name)| {
529                    format!("{}: {}", field_name, value.format_value_with_indent(ctx, indent_level))
530                })
531                .collect()
532        };
533
534        if ctx.with_ty {
535            format!("{}{{ {} }}", name, fields.join(", "))
536        } else {
537            format!("{{ {} }}", fields.join(", "))
538        }
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545    use alloy_primitives::{address, FixedBytes, I256, U256};
546    use serde_json;
547
548    #[test]
549    fn test_serialize_deserialize_bool() {
550        let value = EdbSolValue(DynSolValue::Bool(true));
551        let serialized = serde_json::to_string(&value).unwrap();
552        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
553
554        match deserialized.0 {
555            DynSolValue::Bool(b) => assert!(b),
556            _ => panic!("Expected Bool variant"),
557        }
558    }
559
560    #[test]
561    fn test_serialize_deserialize_uint() {
562        let value = EdbSolValue(DynSolValue::Uint(U256::from(42u64), 256));
563        let serialized = serde_json::to_string(&value).unwrap();
564        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
565
566        match deserialized.0 {
567            DynSolValue::Uint(u, bits) => {
568                assert_eq!(u, U256::from(42u64));
569                assert_eq!(bits, 256);
570            }
571            _ => panic!("Expected Uint variant"),
572        }
573    }
574
575    #[test]
576    fn test_serialize_deserialize_int() {
577        let value = EdbSolValue(DynSolValue::Int(I256::try_from(-42i64).unwrap(), 256));
578        let serialized = serde_json::to_string(&value).unwrap();
579        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
580
581        match deserialized.0 {
582            DynSolValue::Int(i, bits) => {
583                assert_eq!(i, I256::try_from(-42i64).unwrap());
584                assert_eq!(bits, 256);
585            }
586            _ => panic!("Expected Int variant"),
587        }
588    }
589
590    #[test]
591    fn test_serialize_deserialize_address() {
592        let addr = address!("0000000000000000000000000000000000000001");
593        let value = EdbSolValue(DynSolValue::Address(addr));
594        let serialized = serde_json::to_string(&value).unwrap();
595        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
596
597        match deserialized.0 {
598            DynSolValue::Address(a) => assert_eq!(a, addr),
599            _ => panic!("Expected Address variant"),
600        }
601    }
602
603    #[test]
604    fn test_serialize_deserialize_bytes() {
605        let value = EdbSolValue(DynSolValue::Bytes(vec![1, 2, 3, 4]));
606        let serialized = serde_json::to_string(&value).unwrap();
607        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
608
609        match deserialized.0 {
610            DynSolValue::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4]),
611            _ => panic!("Expected Bytes variant"),
612        }
613    }
614
615    #[test]
616    fn test_serialize_deserialize_fixed_bytes() {
617        let bytes = FixedBytes::<32>::from([1u8; 32]);
618        let value = EdbSolValue(DynSolValue::FixedBytes(bytes, 32));
619        let serialized = serde_json::to_string(&value).unwrap();
620        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
621
622        match deserialized.0 {
623            DynSolValue::FixedBytes(b, size) => {
624                assert_eq!(b, bytes);
625                assert_eq!(size, 32);
626            }
627            _ => panic!("Expected FixedBytes variant"),
628        }
629    }
630
631    #[test]
632    fn test_serialize_deserialize_string() {
633        let value = EdbSolValue(DynSolValue::String("Hello, Ethereum!".to_string()));
634        let serialized = serde_json::to_string(&value).unwrap();
635        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
636
637        match deserialized.0 {
638            DynSolValue::String(s) => assert_eq!(s, "Hello, Ethereum!"),
639            _ => panic!("Expected String variant"),
640        }
641    }
642
643    #[test]
644    fn test_serialize_deserialize_array() {
645        let value = EdbSolValue(DynSolValue::Array(vec![
646            DynSolValue::Uint(U256::from(1u64), 256),
647            DynSolValue::Uint(U256::from(2u64), 256),
648            DynSolValue::Uint(U256::from(3u64), 256),
649        ]));
650        let serialized = serde_json::to_string(&value).unwrap();
651        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
652
653        match deserialized.0 {
654            DynSolValue::Array(arr) => {
655                assert_eq!(arr.len(), 3);
656                match &arr[0] {
657                    DynSolValue::Uint(u, _) => assert_eq!(*u, U256::from(1u64)),
658                    _ => panic!("Expected Uint in array"),
659                }
660            }
661            _ => panic!("Expected Array variant"),
662        }
663    }
664
665    #[test]
666    fn test_serialize_deserialize_fixed_array() {
667        let value = EdbSolValue(DynSolValue::FixedArray(vec![
668            DynSolValue::Bool(true),
669            DynSolValue::Bool(false),
670        ]));
671        let serialized = serde_json::to_string(&value).unwrap();
672        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
673
674        match deserialized.0 {
675            DynSolValue::FixedArray(arr) => {
676                assert_eq!(arr.len(), 2);
677                match (&arr[0], &arr[1]) {
678                    (DynSolValue::Bool(b1), DynSolValue::Bool(b2)) => {
679                        assert!(*b1);
680                        assert!(!*b2);
681                    }
682                    _ => panic!("Expected Bool values in fixed array"),
683                }
684            }
685            _ => panic!("Expected FixedArray variant"),
686        }
687    }
688
689    #[test]
690    fn test_serialize_deserialize_tuple() {
691        let addr = address!("0000000000000000000000000000000000000001");
692        let value = EdbSolValue(DynSolValue::Tuple(vec![
693            DynSolValue::Address(addr),
694            DynSolValue::Uint(U256::from(100u64), 256),
695            DynSolValue::String("test".to_string()),
696        ]));
697        let serialized = serde_json::to_string(&value).unwrap();
698        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
699
700        match deserialized.0 {
701            DynSolValue::Tuple(tuple) => {
702                assert_eq!(tuple.len(), 3);
703                match (&tuple[0], &tuple[1], &tuple[2]) {
704                    (DynSolValue::Address(a), DynSolValue::Uint(u, _), DynSolValue::String(s)) => {
705                        assert_eq!(*a, addr);
706                        assert_eq!(*u, U256::from(100u64));
707                        assert_eq!(s, "test");
708                    }
709                    _ => panic!("Expected (Address, Uint, String) in tuple"),
710                }
711            }
712            _ => panic!("Expected Tuple variant"),
713        }
714    }
715
716    #[test]
717    fn test_serialize_deserialize_nested_structure() {
718        let value = EdbSolValue(DynSolValue::Array(vec![
719            DynSolValue::Tuple(vec![
720                DynSolValue::Uint(U256::from(1u64), 256),
721                DynSolValue::Array(vec![
722                    DynSolValue::String("nested1".to_string()),
723                    DynSolValue::String("nested2".to_string()),
724                ]),
725            ]),
726            DynSolValue::Tuple(vec![
727                DynSolValue::Uint(U256::from(2u64), 256),
728                DynSolValue::Array(vec![DynSolValue::String("nested3".to_string())]),
729            ]),
730        ]));
731
732        let serialized = serde_json::to_string(&value).unwrap();
733        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
734
735        match deserialized.0 {
736            DynSolValue::Array(arr) => {
737                assert_eq!(arr.len(), 2);
738                match &arr[0] {
739                    DynSolValue::Tuple(tuple) => {
740                        assert_eq!(tuple.len(), 2);
741                        match &tuple[1] {
742                            DynSolValue::Array(inner_arr) => {
743                                assert_eq!(inner_arr.len(), 2);
744                            }
745                            _ => panic!("Expected Array in tuple"),
746                        }
747                    }
748                    _ => panic!("Expected Tuple in array"),
749                }
750            }
751            _ => panic!("Expected Array variant"),
752        }
753    }
754
755    #[test]
756    fn test_serialize_deserialize_custom_struct() {
757        let value = EdbSolValue(DynSolValue::CustomStruct {
758            name: "Person".to_string(),
759            prop_names: vec!["name".to_string(), "age".to_string()],
760            tuple: vec![
761                DynSolValue::String("Alice".to_string()),
762                DynSolValue::Uint(U256::from(30u64), 256),
763            ],
764        });
765
766        let serialized = serde_json::to_string(&value).unwrap();
767        let deserialized: EdbSolValue = serde_json::from_str(&serialized).unwrap();
768
769        match deserialized.0 {
770            DynSolValue::CustomStruct { name, prop_names, tuple } => {
771                assert_eq!(name, "Person");
772                assert_eq!(prop_names, vec!["name", "age"]);
773                assert_eq!(tuple.len(), 2);
774                match (&tuple[0], &tuple[1]) {
775                    (DynSolValue::String(s), DynSolValue::Uint(u, _)) => {
776                        assert_eq!(s, "Alice");
777                        assert_eq!(*u, U256::from(30u64));
778                    }
779                    _ => panic!("Expected (String, Uint) in custom struct"),
780                }
781            }
782            _ => panic!("Expected CustomStruct variant"),
783        }
784    }
785
786    #[test]
787    fn test_json_format_readability() {
788        let value = EdbSolValue(DynSolValue::Tuple(vec![
789            DynSolValue::Bool(true),
790            DynSolValue::Uint(U256::from(42u64), 256),
791        ]));
792
793        let json = serde_json::to_string_pretty(&value).unwrap();
794        // Verify that the JSON is readable and contains expected structure
795        assert!(json.contains("\"type\""));
796        assert!(json.contains("\"value\""));
797        assert!(json.contains("\"Tuple\""));
798    }
799
800    #[test]
801    fn test_single_line_formatting() {
802        let ctx = SolValueFormatterContext::new();
803
804        // Test array single line
805        let array = DynSolValue::Array(vec![
806            DynSolValue::Uint(U256::from(1u64), 256),
807            DynSolValue::Uint(U256::from(2u64), 256),
808            DynSolValue::Uint(U256::from(3u64), 256),
809        ]);
810        let result = array.format_value(&ctx);
811        assert_eq!(result, "[1, 2, 3]");
812
813        // Test tuple single line
814        let tuple = DynSolValue::Tuple(vec![
815            DynSolValue::Bool(true),
816            DynSolValue::Uint(U256::from(42u64), 256),
817        ]);
818        let result = tuple.format_value(&ctx);
819        assert_eq!(result, "(true, 42)");
820    }
821
822    #[test]
823    fn test_multi_line_formatting() {
824        let ctx = SolValueFormatterContext::new().multi_line(true);
825
826        // Test array multi-line
827        let array = DynSolValue::Array(vec![
828            DynSolValue::Uint(U256::from(1u64), 256),
829            DynSolValue::Uint(U256::from(2u64), 256),
830            DynSolValue::Uint(U256::from(3u64), 256),
831        ]);
832        let result = array.format_value(&ctx);
833        assert_eq!(result, "[\n  1,\n  2,\n  3\n]");
834
835        // Test tuple multi-line
836        let tuple = DynSolValue::Tuple(vec![
837            DynSolValue::Bool(true),
838            DynSolValue::Uint(U256::from(42u64), 256),
839        ]);
840        let result = tuple.format_value(&ctx);
841        assert_eq!(result, "(\n  true,\n  42\n)");
842
843        // Test custom struct multi-line
844        let custom_struct = DynSolValue::CustomStruct {
845            name: "Person".to_string(),
846            prop_names: vec!["name".to_string(), "age".to_string()],
847            tuple: vec![
848                DynSolValue::String("Alice".to_string()),
849                DynSolValue::Uint(U256::from(30u64), 256),
850            ],
851        };
852        let result = custom_struct.format_value(&ctx);
853        assert_eq!(result, "{\n  name: \"Alice\",\n  age: 30\n}");
854    }
855
856    #[test]
857    fn test_multi_line_with_type_info() {
858        let ctx = SolValueFormatterContext::new().multi_line(true).with_ty(true);
859
860        let custom_struct = DynSolValue::CustomStruct {
861            name: "Person".to_string(),
862            prop_names: vec!["name".to_string(), "age".to_string()],
863            tuple: vec![
864                DynSolValue::String("Alice".to_string()),
865                DynSolValue::Uint(U256::from(30u64), 256),
866            ],
867        };
868        let result = custom_struct.format_value(&ctx);
869        assert_eq!(result, "Person{\n  name: \"Alice\",\n  age: uint256(30)\n}");
870    }
871
872    #[test]
873    fn test_single_element_formatting() {
874        let ctx = SolValueFormatterContext::new().multi_line(true);
875
876        // Single element array should not use multi-line
877        let array = DynSolValue::Array(vec![DynSolValue::Uint(U256::from(1u64), 256)]);
878        let result = array.format_value(&ctx);
879        assert_eq!(result, "[1]");
880
881        // Single element tuple should not use multi-line
882        let tuple = DynSolValue::Tuple(vec![DynSolValue::Bool(true)]);
883        let result = tuple.format_value(&ctx);
884        assert_eq!(result, "(true)");
885    }
886
887    #[test]
888    fn test_empty_collections() {
889        let ctx = SolValueFormatterContext::new().multi_line(true);
890
891        // Empty array
892        let empty_array = DynSolValue::Array(vec![]);
893        let result = empty_array.format_value(&ctx);
894        assert_eq!(result, "[]");
895
896        // Empty tuple
897        let empty_tuple = DynSolValue::Tuple(vec![]);
898        let result = empty_tuple.format_value(&ctx);
899        assert_eq!(result, "()");
900
901        // Empty fixed array
902        let empty_fixed_array = DynSolValue::FixedArray(vec![]);
903        let result = empty_fixed_array.format_value(&ctx);
904        assert_eq!(result, "[]");
905    }
906
907    #[test]
908    fn test_deeply_nested_structures() {
909        let ctx = SolValueFormatterContext::new().multi_line(true);
910
911        // Nested array within array
912        let nested_array = DynSolValue::Array(vec![
913            DynSolValue::Array(vec![
914                DynSolValue::Uint(U256::from(1u64), 256),
915                DynSolValue::Uint(U256::from(2u64), 256),
916            ]),
917            DynSolValue::Array(vec![
918                DynSolValue::Uint(U256::from(3u64), 256),
919                DynSolValue::Uint(U256::from(4u64), 256),
920            ]),
921        ]);
922        let result = nested_array.format_value(&ctx);
923        let expected = "[\n  [\n    1,\n    2\n  ],\n  [\n    3,\n    4\n  ]\n]";
924        assert_eq!(result, expected);
925
926        // Nested struct within struct
927        let nested_struct = DynSolValue::CustomStruct {
928            name: "Outer".to_string(),
929            prop_names: vec!["inner".to_string(), "value".to_string()],
930            tuple: vec![
931                DynSolValue::CustomStruct {
932                    name: "Inner".to_string(),
933                    prop_names: vec!["x".to_string(), "y".to_string()],
934                    tuple: vec![
935                        DynSolValue::Uint(U256::from(10u64), 256),
936                        DynSolValue::Uint(U256::from(20u64), 256),
937                    ],
938                },
939                DynSolValue::Uint(U256::from(100u64), 256),
940            ],
941        };
942        let result = nested_struct.format_value(&ctx);
943        let expected = "{\n  inner: {\n    x: 10,\n    y: 20\n  },\n  value: 100\n}";
944        assert_eq!(result, expected);
945    }
946
947    #[test]
948    fn test_shorten_long_with_multi_line() {
949        let ctx = SolValueFormatterContext::new().multi_line(true).shorten_long(true);
950
951        // Long array should be shortened even in multi-line mode
952        let long_array =
953            DynSolValue::Array((1..=10).map(|i| DynSolValue::Uint(U256::from(i), 256)).collect());
954        let result = long_array.format_value(&ctx);
955        let expected = "[\n  1,\n  2,\n  3,\n  ...[10 items]\n]";
956        assert_eq!(result, expected);
957
958        // Long tuple should be shortened
959        let long_tuple =
960            DynSolValue::Tuple((1..=8).map(|i| DynSolValue::Uint(U256::from(i), 256)).collect());
961        let result = long_tuple.format_value(&ctx);
962        let expected = "(\n  1,\n  2,\n  3,\n  ...[8 fields]\n)";
963        assert_eq!(result, expected);
964    }
965
966    #[test]
967    fn test_triple_nested_indentation() {
968        let ctx = SolValueFormatterContext::new().multi_line(true);
969
970        // Three levels of nesting to test proper indentation
971        let triple_nested =
972            DynSolValue::Array(vec![DynSolValue::Tuple(vec![DynSolValue::Array(vec![
973                DynSolValue::Uint(U256::from(1u64), 256),
974                DynSolValue::Uint(U256::from(2u64), 256),
975            ])])]);
976        let result = triple_nested.format_value(&ctx);
977        let expected = "[([\n  1,\n  2\n])]"; // Actual format from error
978        assert_eq!(result, expected);
979    }
980
981    #[test]
982    fn test_mixed_complex_structures() {
983        let ctx = SolValueFormatterContext::new().multi_line(true);
984
985        // Array containing tuples containing structs
986        let complex_value = DynSolValue::Array(vec![DynSolValue::Tuple(vec![
987            DynSolValue::CustomStruct {
988                name: "Point".to_string(),
989                prop_names: vec!["x".to_string(), "y".to_string()],
990                tuple: vec![
991                    DynSolValue::Uint(U256::from(1u64), 256),
992                    DynSolValue::Uint(U256::from(2u64), 256),
993                ],
994            },
995            DynSolValue::Bool(true),
996        ])]);
997        let result = complex_value.format_value(&ctx);
998        let expected = "[(\n  {\n    x: 1,\n    y: 2\n  },\n  true\n)]";
999        assert_eq!(result, expected);
1000    }
1001
1002    #[test]
1003    fn test_bytes_and_strings_multi_line() {
1004        let ctx = SolValueFormatterContext::new().multi_line(true);
1005
1006        let mixed_data = DynSolValue::Array(vec![
1007            DynSolValue::String("Hello, World!".to_string()),
1008            DynSolValue::Bytes(vec![0x01, 0x02, 0x03, 0x04]),
1009            DynSolValue::FixedBytes(FixedBytes::<32>::from([0xff; 32]), 32),
1010        ]);
1011        let result = mixed_data.format_value(&ctx);
1012        let expected = "[\n  \"Hello, World!\",\n  0x01020304,\n  0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n]";
1013        assert_eq!(result, expected);
1014    }
1015
1016    #[test]
1017    fn test_address_formatting_nested() {
1018        let ctx = SolValueFormatterContext::new().multi_line(true).shorten_long(true);
1019
1020        let struct_with_addresses = DynSolValue::CustomStruct {
1021            name: "Transfer".to_string(),
1022            prop_names: vec!["from".to_string(), "to".to_string()],
1023            tuple: vec![
1024                DynSolValue::Address("0x742d35Cc6639C0532fBb5dd9D09A0CB21234000A".parse().unwrap()),
1025                DynSolValue::Address("0x0000000000000000000000000000000000000000".parse().unwrap()),
1026            ],
1027        };
1028        let result = struct_with_addresses.format_value(&ctx);
1029        let expected = "{\n  from: 0x742d35...34000a,\n  to: 0x0000000000000000\n}";
1030        assert_eq!(result, expected);
1031    }
1032
1033    #[test]
1034    fn test_all_options_together() {
1035        let ctx = SolValueFormatterContext::new().multi_line(true).with_ty(true).shorten_long(true);
1036
1037        let complex_struct = DynSolValue::CustomStruct {
1038            name: "Transaction".to_string(),
1039            prop_names: vec!["from".to_string(), "to".to_string(), "values".to_string()],
1040            tuple: vec![
1041                DynSolValue::Address("0x742d35Cc6639C0532fBb5dd9D09A0CB21234000A".parse().unwrap()),
1042                DynSolValue::Address("0x123F681646d4A755815f9CB19e1aCc8565A0c2AC".parse().unwrap()),
1043                DynSolValue::Array(
1044                    (1..=10).map(|i| DynSolValue::Uint(U256::from(i), 256)).collect(),
1045                ),
1046            ],
1047        };
1048        let result = complex_struct.format_value(&ctx);
1049        let expected = "Transaction{\n  from: address(0x742d35...34000a),\n  to: address(0x123f68...a0c2ac),\n  values: [\n    uint256(1),\n    uint256(2),\n    uint256(3),\n    ...[10 items]\n  ]\n}";
1050        assert_eq!(result, expected);
1051    }
1052}