Skip to content

Commit 182a184

Browse files
grandizzyrplusq
authored andcommitted
feat(cast): decode external lib sigs from cached selectors (foundry-rs#9399)
1 parent a51bbe4 commit 182a184

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

crates/cast/tests/cli/main.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,3 +1841,94 @@ Transaction successfully executed.
18411841
18421842
"#]]);
18431843
});
1844+
1845+
// tests cast can decode external libraries traces with project cached selectors
1846+
forgetest_async!(decode_external_libraries_with_cached_selectors, |prj, cmd| {
1847+
let (api, handle) = anvil::spawn(NodeConfig::test()).await;
1848+
1849+
foundry_test_utils::util::initialize(prj.root());
1850+
prj.add_source(
1851+
"ExternalLib",
1852+
r#"
1853+
import "./CounterInExternalLib.sol";
1854+
library ExternalLib {
1855+
function updateCounterInExternalLib(CounterInExternalLib.Info storage counterInfo, uint256 counter) public {
1856+
counterInfo.counter = counter + 1;
1857+
}
1858+
}
1859+
"#,
1860+
)
1861+
.unwrap();
1862+
prj.add_source(
1863+
"CounterInExternalLib",
1864+
r#"
1865+
import "./ExternalLib.sol";
1866+
contract CounterInExternalLib {
1867+
struct Info {
1868+
uint256 counter;
1869+
}
1870+
Info info;
1871+
constructor() {
1872+
ExternalLib.updateCounterInExternalLib(info, 100);
1873+
}
1874+
}
1875+
"#,
1876+
)
1877+
.unwrap();
1878+
prj.add_script(
1879+
"CounterInExternalLibScript",
1880+
r#"
1881+
import "forge-std/Script.sol";
1882+
import {CounterInExternalLib} from "../src/CounterInExternalLib.sol";
1883+
contract CounterInExternalLibScript is Script {
1884+
function run() public {
1885+
vm.startBroadcast();
1886+
new CounterInExternalLib();
1887+
vm.stopBroadcast();
1888+
}
1889+
}
1890+
"#,
1891+
)
1892+
.unwrap();
1893+
1894+
cmd.args([
1895+
"script",
1896+
"--private-key",
1897+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
1898+
"--rpc-url",
1899+
&handle.http_endpoint(),
1900+
"--broadcast",
1901+
"CounterInExternalLibScript",
1902+
])
1903+
.assert_success();
1904+
1905+
let tx_hash = api
1906+
.transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0))
1907+
.await
1908+
.unwrap()
1909+
.unwrap()
1910+
.tx_hash();
1911+
1912+
// Cache project selectors.
1913+
cmd.forge_fuse().set_current_dir(prj.root());
1914+
cmd.forge_fuse().args(["selectors", "cache"]).assert_success();
1915+
1916+
// Assert cast with local artifacts can decode external lib signature.
1917+
cmd.cast_fuse().set_current_dir(prj.root());
1918+
cmd.cast_fuse()
1919+
.args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()])
1920+
.assert_success()
1921+
.stdout_eq(str![[r#"
1922+
...
1923+
Traces:
1924+
[37739] → new <unknown>@0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
1925+
├─ [22411] 0xfAb06527117d29EA121998AC4fAB9Fc88bF5f979::updateCounterInExternalLib(0, 100) [delegatecall]
1926+
│ └─ ← [Stop]
1927+
└─ ← [Return] 62 bytes of code
1928+
1929+
1930+
Transaction successfully executed.
1931+
[GAS]
1932+
1933+
"#]]);
1934+
});

crates/cli/src/utils/cmd.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,15 @@ pub fn cache_local_signatures(output: &ProjectCompileOutput, cache_path: PathBuf
501501
.events
502502
.insert(event.selector().to_string(), event.full_signature());
503503
}
504+
// External libraries doesn't have functions included in abi, but `methodIdentifiers`.
505+
if let Some(method_identifiers) = &artifact.method_identifiers {
506+
method_identifiers.iter().for_each(|(signature, selector)| {
507+
cached_signatures
508+
.functions
509+
.entry(format!("0x{selector}"))
510+
.or_insert(signature.to_string());
511+
});
512+
}
504513
}
505514
});
506515

crates/evm/traces/src/decoder/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,10 +689,13 @@ fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue>
689689
let mut unindexed = 0;
690690
let mut inputs = vec![];
691691
for input in event.inputs.iter() {
692-
if input.indexed {
692+
// Prevent panic of event `Transfer(from, to)` decoded with a signature
693+
// `Transfer(address indexed from, address indexed to, uint256 indexed tokenId)` by making
694+
// sure the event inputs is not higher than decoded indexed / un-indexed values.
695+
if input.indexed && indexed < decoded.indexed.len() {
693696
inputs.push(decoded.indexed[indexed].clone());
694697
indexed += 1;
695-
} else {
698+
} else if unindexed < decoded.body.len() {
696699
inputs.push(decoded.body[unindexed].clone());
697700
unindexed += 1;
698701
}

0 commit comments

Comments
 (0)