diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index d142cbd599b2..267e825bed7c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1841,3 +1841,94 @@ Transaction successfully executed. "#]]); }); + +// tests cast can decode external libraries traces with project cached selectors +forgetest_async!(decode_external_libraries_with_cached_selectors, |prj, cmd| { + let (api, handle) = anvil::spawn(NodeConfig::test()).await; + + foundry_test_utils::util::initialize(prj.root()); + prj.add_source( + "ExternalLib", + r#" +import "./CounterInExternalLib.sol"; +library ExternalLib { + function updateCounterInExternalLib(CounterInExternalLib.Info storage counterInfo, uint256 counter) public { + counterInfo.counter = counter + 1; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "CounterInExternalLib", + r#" +import "./ExternalLib.sol"; +contract CounterInExternalLib { + struct Info { + uint256 counter; + } + Info info; + constructor() { + ExternalLib.updateCounterInExternalLib(info, 100); + } +} + "#, + ) + .unwrap(); + prj.add_script( + "CounterInExternalLibScript", + r#" +import "forge-std/Script.sol"; +import {CounterInExternalLib} from "../src/CounterInExternalLib.sol"; +contract CounterInExternalLibScript is Script { + function run() public { + vm.startBroadcast(); + new CounterInExternalLib(); + vm.stopBroadcast(); + } +} + "#, + ) + .unwrap(); + + cmd.args([ + "script", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--rpc-url", + &handle.http_endpoint(), + "--broadcast", + "CounterInExternalLibScript", + ]) + .assert_success(); + + let tx_hash = api + .transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0)) + .await + .unwrap() + .unwrap() + .tx_hash(); + + // Cache project selectors. + cmd.forge_fuse().set_current_dir(prj.root()); + cmd.forge_fuse().args(["selectors", "cache"]).assert_success(); + + // Assert cast with local artifacts can decode external lib signature. + cmd.cast_fuse().set_current_dir(prj.root()); + cmd.cast_fuse() + .args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()]) + .assert_success() + .stdout_eq(str![[r#" +... +Traces: + [37739] → new @0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + ├─ [22411] 0xfAb06527117d29EA121998AC4fAB9Fc88bF5f979::updateCounterInExternalLib(0, 100) [delegatecall] + │ └─ ← [Stop] + └─ ← [Return] 62 bytes of code + + +Transaction successfully executed. +[GAS] + +"#]]); +}); diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 505634996de8..67aa65073ff8 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -501,6 +501,15 @@ pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf .events .insert(event.selector().to_string(), event.full_signature()); } + // External libraries doesn't have functions included in abi, but `methodIdentifiers`. + if let Some(method_identifiers) = &artifact.method_identifiers { + method_identifiers.iter().for_each(|(signature, selector)| { + cached_signatures + .functions + .entry(format!("0x{selector}")) + .or_insert(signature.to_string()); + }); + } } }); diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index b63c7f1d7209..68ecbac29c7e 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -689,10 +689,13 @@ fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec let mut unindexed = 0; let mut inputs = vec![]; for input in event.inputs.iter() { - if input.indexed { + // Prevent panic of event `Transfer(from, to)` decoded with a signature + // `Transfer(address indexed from, address indexed to, uint256 indexed tokenId)` by making + // sure the event inputs is not higher than decoded indexed / un-indexed values. + if input.indexed && indexed < decoded.indexed.len() { inputs.push(decoded.indexed[indexed].clone()); indexed += 1; - } else { + } else if unindexed < decoded.body.len() { inputs.push(decoded.body[unindexed].clone()); unindexed += 1; }