From 0f7268f46d2db7502cd0a75c8cfba34f06f8fd6e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:38:40 +0200 Subject: [PATCH] feat(`cast`): `decode-event` with local and openchain API (#9431) --- crates/cast/bin/args.rs | 5 ++-- crates/cast/bin/main.rs | 25 +++++++++++++++++-- crates/cast/tests/cli/main.rs | 47 +++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index e4cf32639d4b..fb7fb0757890 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -533,8 +533,9 @@ pub enum CastSubcommand { /// Decode event data. #[command(visible_aliases = &["event-decode", "--event-decode", "ed"])] DecodeEvent { - /// The event signature. - sig: String, + /// The event signature. If none provided then tries to decode from local cache or `https://api.openchain.xyz`. + #[arg(long, visible_alias = "event-sig")] + sig: Option, /// The event data to decode. data: String, }, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index cacddb8344f5..fcb5a20eb1ad 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -213,8 +213,29 @@ async fn main_args(args: CastArgs) -> Result<()> { print_tokens(&tokens); } CastSubcommand::DecodeEvent { sig, data } => { - let event = get_event(sig.as_str())?; - let decoded_event = event.decode_log_parts(None, &hex::decode(data)?, false)?; + let decoded_event = if let Some(event_sig) = sig { + get_event(event_sig.as_str())?.decode_log_parts(None, &hex::decode(data)?, false)? + } else { + let data = data.strip_prefix("0x").unwrap_or(data.as_str()); + let selector = data.get(..64).unwrap_or_default(); + let identified_event = + SignaturesIdentifier::new(Config::foundry_cache_dir(), false)? + .write() + .await + .identify_event(&hex::decode(selector)?) + .await; + if let Some(event) = identified_event { + let _ = sh_println!("{}", event.signature()); + let data = data.get(64..).unwrap_or_default(); + get_event(event.signature().as_str())?.decode_log_parts( + None, + &hex::decode(data)?, + false, + )? + } else { + eyre::bail!("No matching event signature found for selector `{selector}`") + } + }; print_tokens(&decoded_event.body); } CastSubcommand::DecodeError { sig, data } => { diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 0cd43766e3e1..f3d04b09456b 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1474,15 +1474,35 @@ casttest!(string_decode, |_prj, cmd| { "#]]); }); -casttest!(event_decode, |_prj, cmd| { - cmd.args(["decode-event", "MyEvent(uint256,address)", "0x000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000d0004f"]).assert_success().stdout_eq(str![[r#" +// tests cast can decode event with provided signature +casttest!(event_decode_with_sig, |_prj, cmd| { + cmd.args(["decode-event", "--sig", "MyEvent(uint256,address)", "0x000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000d0004f"]).assert_success().stdout_eq(str![[r#" 78 0x0000000000000000000000000000000000D0004F +"#]]); + + cmd.args(["--json"]).assert_success().stdout_eq(str![[r#" +[ + "78", + "0x0000000000000000000000000000000000D0004F" +] + "#]]); }); -// tests cast can decode traces with provided signature +// tests cast can decode event with Openchain API +casttest!(event_decode_with_openchain, |prj, cmd| { + prj.clear_cache(); + cmd.args(["decode-event", "0xe27c4c1372396a3d15a9922f74f9dfc7c72b1ad6d63868470787249c356454c1000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000dd00000004e"]).assert_success().stdout_eq(str![[r#" +BaseCurrencySet(address,uint256) +0x000000000000000000000000000000000000004e +15187004358734 [1.518e13] + +"#]]); +}); + +// tests cast can decode error with provided signature casttest!(error_decode_with_sig, |_prj, cmd| { cmd.args(["decode-error", "--sig", "AnotherValueTooHigh(uint256,address)", "0x7191bc6200000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000D0004F"]).assert_success().stdout_eq(str![[r#" 101 @@ -1499,8 +1519,9 @@ casttest!(error_decode_with_sig, |_prj, cmd| { "#]]); }); -// tests cast can decode traces with Openchain API -casttest!(error_decode_with_openchain, |_prj, cmd| { +// tests cast can decode error with Openchain API +casttest!(error_decode_with_openchain, |prj, cmd| { + prj.clear_cache(); cmd.args(["decode-error", "0x7a0e198500000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064"]).assert_success().stdout_eq(str![[r#" ValueTooHigh(uint256,uint256) 101 @@ -1509,14 +1530,16 @@ ValueTooHigh(uint256,uint256) "#]]); }); -// tests cast can decode traces when using local sig identifiers cache -forgetest!(error_decode_with_cache, |prj, cmd| { +// tests cast can decode error and event when using local sig identifiers cache +forgetest!(error_event_decode_with_cache, |prj, cmd| { + prj.clear_cache(); foundry_test_utils::util::initialize(prj.root()); prj.add_source( "LocalProjectContract", r#" contract ContractWithCustomError { error AnotherValueTooHigh(uint256, address); + event MyUniqueEventWithinLocalProject(uint256 a, address b); } "#, ) @@ -1533,6 +1556,16 @@ AnotherValueTooHigh(uint256,address) 101 0x0000000000000000000000000000000000D0004F +"#]]); + // Assert cast can decode event with local cache. + cmd.cast_fuse() + .args(["decode-event", "0xbd3699995dcc867b64dbb607be2c33be38df9134bef1178df13bfb9446e73104000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000dd00000004e"]) + .assert_success() + .stdout_eq(str![[r#" +MyUniqueEventWithinLocalProject(uint256,address) +78 +0x00000000000000000000000000000DD00000004e + "#]]); });