Skip to content

Commit 16a013f

Browse files
authored
feat(cast): decode external lib sigs from cached selectors (#9399)
1 parent c63aba8 commit 16a013f

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
@@ -1776,3 +1776,94 @@ Transaction successfully executed.
17761776
17771777
"#]]);
17781778
});
1779+
1780+
// tests cast can decode external libraries traces with project cached selectors
1781+
forgetest_async!(decode_external_libraries_with_cached_selectors, |prj, cmd| {
1782+
let (api, handle) = anvil::spawn(NodeConfig::test()).await;
1783+
1784+
foundry_test_utils::util::initialize(prj.root());
1785+
prj.add_source(
1786+
"ExternalLib",
1787+
r#"
1788+
import "./CounterInExternalLib.sol";
1789+
library ExternalLib {
1790+
function updateCounterInExternalLib(CounterInExternalLib.Info storage counterInfo, uint256 counter) public {
1791+
counterInfo.counter = counter + 1;
1792+
}
1793+
}
1794+
"#,
1795+
)
1796+
.unwrap();
1797+
prj.add_source(
1798+
"CounterInExternalLib",
1799+
r#"
1800+
import "./ExternalLib.sol";
1801+
contract CounterInExternalLib {
1802+
struct Info {
1803+
uint256 counter;
1804+
}
1805+
Info info;
1806+
constructor() {
1807+
ExternalLib.updateCounterInExternalLib(info, 100);
1808+
}
1809+
}
1810+
"#,
1811+
)
1812+
.unwrap();
1813+
prj.add_script(
1814+
"CounterInExternalLibScript",
1815+
r#"
1816+
import "forge-std/Script.sol";
1817+
import {CounterInExternalLib} from "../src/CounterInExternalLib.sol";
1818+
contract CounterInExternalLibScript is Script {
1819+
function run() public {
1820+
vm.startBroadcast();
1821+
new CounterInExternalLib();
1822+
vm.stopBroadcast();
1823+
}
1824+
}
1825+
"#,
1826+
)
1827+
.unwrap();
1828+
1829+
cmd.args([
1830+
"script",
1831+
"--private-key",
1832+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
1833+
"--rpc-url",
1834+
&handle.http_endpoint(),
1835+
"--broadcast",
1836+
"CounterInExternalLibScript",
1837+
])
1838+
.assert_success();
1839+
1840+
let tx_hash = api
1841+
.transaction_by_block_number_and_index(BlockNumberOrTag::Latest, Index::from(0))
1842+
.await
1843+
.unwrap()
1844+
.unwrap()
1845+
.tx_hash();
1846+
1847+
// Cache project selectors.
1848+
cmd.forge_fuse().set_current_dir(prj.root());
1849+
cmd.forge_fuse().args(["selectors", "cache"]).assert_success();
1850+
1851+
// Assert cast with local artifacts can decode external lib signature.
1852+
cmd.cast_fuse().set_current_dir(prj.root());
1853+
cmd.cast_fuse()
1854+
.args(["run", format!("{tx_hash}").as_str(), "--rpc-url", &handle.http_endpoint()])
1855+
.assert_success()
1856+
.stdout_eq(str![[r#"
1857+
...
1858+
Traces:
1859+
[37739] → new <unknown>@0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
1860+
├─ [22411] 0xfAb06527117d29EA121998AC4fAB9Fc88bF5f979::updateCounterInExternalLib(0, 100) [delegatecall]
1861+
│ └─ ← [Stop]
1862+
└─ ← [Return] 62 bytes of code
1863+
1864+
1865+
Transaction successfully executed.
1866+
[GAS]
1867+
1868+
"#]]);
1869+
});

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)