1use 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#[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#[derive(Serialize, Deserialize)]
56#[serde(tag = "type", content = "value")]
57enum SerializedDynSolValue {
58 Bool(bool),
60 Int { value: I256, bits: usize },
62 Uint { value: U256, bits: usize },
64 FixedBytes { value: FixedBytes<32>, size: usize },
66 Address(Address),
68 Function(FixedBytes<24>),
70 Bytes(Vec<u8>),
72 String(String),
74 Array(Vec<SerializedDynSolValue>),
76 FixedArray(Vec<SerializedDynSolValue>),
78 Tuple(Vec<SerializedDynSolValue>),
80 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
158pub trait SolValueFormatter {
160 fn format_value(&self, ctx: &SolValueFormatterContext) -> String {
170 self.format_value_with_indent(ctx, 0)
171 }
172
173 fn format_value_with_indent(
175 &self,
176 ctx: &SolValueFormatterContext,
177 indent_level: usize,
178 ) -> String;
179
180 fn format_type(&self) -> String;
186}
187
188#[derive(Default)]
190pub struct SolValueFormatterContext {
191 pub resolve_address: Option<Box<dyn Fn(Address) -> Option<String>>>,
193 pub with_ty: bool,
195 pub shorten_long: bool,
197 pub multi_line: bool,
199}
200
201impl SolValueFormatterContext {
202 pub fn new() -> Self {
204 Self::default()
205 }
206
207 pub fn with_ty(mut self, with_ty: bool) -> Self {
209 self.with_ty = with_ty;
210 self
211 }
212
213 pub fn shorten_long(mut self, shorten_long: bool) -> Self {
215 self.shorten_long = shorten_long;
216 self
217 }
218
219 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 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
352fn 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 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 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 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 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 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 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 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 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 let empty_array = DynSolValue::Array(vec![]);
893 let result = empty_array.format_value(&ctx);
894 assert_eq!(result, "[]");
895
896 let empty_tuple = DynSolValue::Tuple(vec![]);
898 let result = empty_tuple.format_value(&ctx);
899 assert_eq!(result, "()");
900
901 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 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 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 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 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 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])]"; assert_eq!(result, expected);
979 }
980
981 #[test]
982 fn test_mixed_complex_structures() {
983 let ctx = SolValueFormatterContext::new().multi_line(true);
984
985 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}