Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/cast/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use eyre::Result;
use foundry_cli::{handler, utils, utils::LoadConfig};
use foundry_common::{
abi::{get_error, get_event},
fmt::{format_tokens, format_tokens_raw, format_uint_exp},
fmt::{format_tokens, format_uint_exp, token_to_json},
fs,
selectors::{
ParsedSignatures, SelectorImportData, SelectorKind, decode_calldata, decode_event_topic,
Expand Down Expand Up @@ -757,7 +757,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
/// This is included here to avoid a cyclic dependency between `fmt` and `common`.
fn print_tokens(tokens: &[DynSolValue]) {
if shell::is_json() {
let tokens: Vec<String> = format_tokens_raw(tokens).collect();
let tokens: Vec<serde_json::Value> = tokens.iter().map(token_to_json).collect();
let _ = sh_println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
} else {
let tokens = format_tokens(tokens);
Expand Down
43 changes: 42 additions & 1 deletion crates/cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2313,7 +2313,7 @@ fn explorer_client(

#[cfg(test)]
mod tests {
use super::SimpleCast as Cast;
use super::{DynSolValue, SimpleCast as Cast, token_to_json};
use alloy_primitives::hex;

#[test]
Expand Down Expand Up @@ -2431,6 +2431,47 @@ mod tests {
);
}

#[test]
fn calldata_decode_nested_json() {
let calldata = "0xdb5b0ed700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006772bf190000000000000000000000000000000000000000000000000000000000020716000000000000000000000000af9d27ffe4d51ed54ac8eec78f2785d7e11e5ab100000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000404366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb73188600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5";
let sig = "sequenceBatchesValidium((bytes32,bytes32,uint64,bytes32)[],uint64,uint64,address,bytes)";
let decoded = Cast::calldata_decode(sig, calldata, true).unwrap();
let json_value = token_to_json(&DynSolValue::Array(decoded));
let expected = serde_json::json!([
[
[
"0x04366a6dc4b2f348a85e0066e46f0cc206fca6512e0ed7f17ca7afb88e9a4c27",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
[
"0x093922dee6e380c28a50c008ab167b7800bb24c2026cd1b22f1c6fb884ceed74",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
[
"0x60f85e59ecad6c1a6be343a945abedb7d5b5bfad7817c4d8cc668da7d391faf7",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
[
"0x93dfbf04395fbec1f1aed4ad0f9d3ba880ff58a60485df5d33f8f5e0fb731886",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
],
"1735573273",
"132886",
"0xAF9d27ffe4d51eD54AC8eEc78f2785D7E11E5ab1",
"0x334a426ea9e21d5f84eb2d4723ca56b92382b9260ab2b6769b7c23d437b6b512322a25cecc954127e60cf91ef056ac1da25f90b73be81c3ff1872fa48d10c7ef1ccb4087bbeedb54b1417a24abbb76f6cd57010a65bb03c7b6602b1eaf0e32c67c54168232d4edc0bfa1b815b2af2a2d0a5c109d675a4f2de684e51df9abb324ab1b19a81bac80f9ce3a45095f3df3a7cf69ef18fc08e94ac3cbc1c7effeacca68e3bfe5d81e26a659b5"
]);
assert_eq!(json_value, expected);
}

#[test]
fn concat_hex() {
assert_eq!(Cast::concat_hex(["0x00", "0x01"]), "0x0001");
Expand Down
27 changes: 27 additions & 0 deletions crates/common/fmt/src/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{format_int_exp, format_uint_exp};
use alloy_dyn_abi::{DynSolType, DynSolValue};
use alloy_primitives::hex;
use serde_json::{Value, json};
use std::fmt;

/// [`DynSolValue`] formatter.
Expand Down Expand Up @@ -146,6 +147,32 @@ pub fn format_token_raw(value: &DynSolValue) -> String {
DynValueDisplay::new(value, true).to_string()
}

/// Recursively converts a `DynSolValue` into a serde_json::Value.
pub fn token_to_json(value: &DynSolValue) -> Value {
match value {
DynSolValue::Array(values)
| DynSolValue::FixedArray(values)
| DynSolValue::Tuple(values) => {
let tokens: Vec<Value> = values.iter().map(token_to_json).collect();
Value::Array(tokens)
}
DynSolValue::CustomStruct { name, prop_names, tuple } => {
if prop_names.len() == tuple.len() {
let obj = prop_names
.iter()
.zip(tuple)
.map(|(k, v)| (k.clone(), token_to_json(v)))
.collect();
Value::Object(obj)
} else {
let tokens: Vec<Value> = tuple.iter().map(token_to_json).collect();
json!({ name: tokens })
}
}
_ => json!(format_token_raw(value)),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 3 additions & 1 deletion crates/common/fmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ mod console;
pub use console::{ConsoleFmt, FormatSpec, console_format};

mod dynamic;
pub use dynamic::{format_token, format_token_raw, format_tokens, format_tokens_raw, parse_tokens};
pub use dynamic::{
format_token, format_token_raw, format_tokens, format_tokens_raw, parse_tokens, token_to_json,
};

mod exp;
pub use exp::{format_int_exp, format_uint_exp, to_exp_notation};
Expand Down
Loading