Skip to content

Commit

Permalink
fix(trace): check fn sigs for contract with fallbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
grandizzy committed Nov 8, 2024
1 parent f3376a6 commit a40b59c
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 1 deletion.
25 changes: 24 additions & 1 deletion crates/evm/traces/src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ pub struct CallTraceDecoder {
pub labels: HashMap<Address, String>,
/// Contract addresses that have a receive function.
pub receive_contracts: Vec<Address>,
/// Contract addresses that have fallback functions, mapped to function sigs.
pub fallback_contracts: HashMap<Address, Vec<String>>,

/// All known functions.
pub functions: HashMap<Selector, Vec<Function>>,
Expand Down Expand Up @@ -188,6 +190,7 @@ impl CallTraceDecoder {
(POINT_EVALUATION, "PointEvaluation".to_string()),
]),
receive_contracts: Default::default(),
fallback_contracts: Default::default(),

functions: hh_funcs()
.chain(
Expand Down Expand Up @@ -222,6 +225,7 @@ impl CallTraceDecoder {
}

self.receive_contracts.clear();
self.fallback_contracts.clear();
}

/// Identify unknown addresses in the specified call trace using the specified identifier.
Expand Down Expand Up @@ -317,6 +321,14 @@ impl CallTraceDecoder {
if abi.receive.is_some() {
self.receive_contracts.push(*address);
}

if abi.fallback.is_some() {
let mut functions_sig = vec![];
for function in abi.functions() {
functions_sig.push(function.signature());
}
self.fallback_contracts.insert(*address, functions_sig);
}
}
}

Expand Down Expand Up @@ -379,9 +391,20 @@ impl CallTraceDecoder {
};
};

// If traced contract is a fallback contract, then check if it has the decoded
// functions. If not, then replace call data signature with `fallback`.
let mut call_data = self.decode_function_input(trace, func);
if let Some(fallback_functions) = self.fallback_contracts.get(&trace.address) {
if functions.iter().any(|decoded_function| {
!fallback_functions.contains(&decoded_function.signature())
}) {
call_data.signature = "fallback()".into();
}
}

DecodedCallTrace {
label,
call_data: Some(self.decode_function_input(trace, func)),
call_data: Some(call_data),
return_data: self.decode_function_output(trace, functions),
}
} else {
Expand Down
80 changes: 80 additions & 0 deletions crates/forge/tests/cli/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2818,3 +2818,83 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| {
.is_json(),
);
});

// <https://github.com/foundry-rs/foundry/issues/9115>
forgetest_init!(gas_report_with_fallback, |prj, cmd| {
prj.add_test(
"DelegateProxyTest.sol",
r#"
import {Test} from "forge-std/Test.sol";
contract ProxiedContract {
uint256 public amount;
function deposit(uint256 aba) external {
amount = amount * 2;
}
function deposit() external {
}
}
contract DelegateProxy {
address internal implementation;
constructor(address counter) {
implementation = counter;
}
function deposit() external {
}
fallback() external payable {
address addr = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), addr, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
contract GasReportFallbackTest is Test {
function test_fallback_gas_report() public {
ProxiedContract proxied = ProxiedContract(address(new DelegateProxy(address(new ProxiedContract()))));
proxied.deposit(100);
proxied.deposit();
}
}
"#,
)
.unwrap();

cmd.args(["test", "--mt", "test_fallback_gas_report", "--gas-report"])
.assert_success()
.stdout_eq(str![[r#"
...
Ran 1 test for test/DelegateProxyTest.sol:GasReportFallbackTest
[PASS] test_fallback_gas_report() ([GAS])
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
| test/DelegateProxyTest.sol:DelegateProxy contract | | | | | |
|---------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 108698 | 315 | | | | |
| Function Name | min | avg | median | max | # calls |
| deposit | 21160 | 21160 | 21160 | 21160 | 1 |
| fallback | 29396 | 29396 | 29396 | 29396 | 1 |
| test/DelegateProxyTest.sol:ProxiedContract contract | | | | | |
|-----------------------------------------------------|-----------------|------|--------|------|---------|
| Deployment Cost | Deployment Size | | | | |
| 106511 | 276 | | | | |
| Function Name | min | avg | median | max | # calls |
| deposit | 3320 | 3320 | 3320 | 3320 | 1 |
...
"#]]);
});

0 comments on commit a40b59c

Please sign in to comment.