Skip to content

Commit

Permalink
fix: Exclude from empty code error case where there is no calldata an…
Browse files Browse the repository at this point in the history
…d value (#804)

* Check empty code only inside mock block

* Put back comments

* Change approach, skip empty code error when dealing with empty calldata with value

* Change test names and remove comments

---------

Co-authored-by: Nisheeth Barthwal <[email protected]>
  • Loading branch information
Jrigada and nbaztec authored Jan 9, 2025
1 parent 31d3744 commit 1cd2c56
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 2 deletions.
52 changes: 52 additions & 0 deletions crates/forge/tests/cli/zk_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,55 @@ contract CallEmptyCode is Test {
.stdout_lossy()
.contains("call may fail or behave unexpectedly due to empty code");
});

forgetest_async!(test_zk_can_send_eth_to_eoa_without_warnings, |prj, cmd| {
foundry_test_utils::util::initialize(prj.root());
prj.add_test(
"SendEthToEOA.t.sol",
r#"
import "forge-std/Test.sol";
contract SendEthToEOA is Test {
function testSendEthToEOA() external {
address eoa = makeAddr("Juan's Account");
vm.deal(address(this), 1 ether);
(bool success,) = eoa.call{value: 1 ether}("");
assertTrue(success, "ETH transfer failed");
}
}
"#,
)
.unwrap();

cmd.args(["test", "--zksync", "--match-test", "testSendEthToEOA"]);
let output = cmd.assert_success().get_output().stdout_lossy();

assert!(!output.contains("call may fail or behave unexpectedly due to empty code"));
});

forgetest_async!(test_zk_calling_empty_code_with_zero_value_issues_warning, |prj, cmd| {
foundry_test_utils::util::initialize(prj.root());
prj.add_test(
"CallEmptyCodeWithZeroValue.t.sol",
r#"
import "forge-std/Test.sol";
contract CallEmptyCodeWithZeroValue is Test {
function testCallEmptyCodeWithZeroValue() external {
address eoa = makeAddr("Juan's Account");
vm.deal(address(this), 1 ether);
(bool success,) = eoa.call("");
assertTrue(success, "call failed");
}
}
"#,
)
.unwrap();

cmd.args(["test", "--zksync", "--match-test", "testCallEmptyCodeWithZeroValue"]);
let output = cmd.assert_success().get_output().stdout_lossy();

assert!(output.contains("call may fail or behave unexpectedly due to empty code"));
});
15 changes: 13 additions & 2 deletions crates/zksync/core/src/vm/tracers/cheatcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,22 @@ impl CheatcodeTracer {
}

/// Check if the given address's code is empty
fn has_empty_code<S: ReadStorage>(&self, storage: StoragePtr<S>, target: Address) -> bool {
fn has_empty_code<S: ReadStorage>(
&self,
storage: StoragePtr<S>,
target: Address,
calldata: &[u8],
value: rU256,
) -> bool {
// The following addresses are expected to have empty bytecode
let ignored_known_addresses =
[foundry_evm_abi::HARDHAT_CONSOLE_ADDRESS, self.call_context.tx_caller];

// Skip empty code check for empty calldata with non-zero value (Transfers)
if calldata.is_empty() && !value.is_zero() {
return false;
}

let contract_code = storage.borrow_mut().read_value(&get_code_key(&target.to_h160()));

!ignored_known_addresses.contains(&target) &&
Expand Down Expand Up @@ -295,7 +306,7 @@ impl<S: ReadStorage, H: HistoryMode> DynTracer<S, SimpleMemory<H>> for Cheatcode

// if we get here there was no matching mock call,
// so we check if there's no code at the mocked address
if self.has_empty_code(storage, call_contract) {
if self.has_empty_code(storage, call_contract, &call_input, call_value) {
// issue a more targeted
// error if we already had some mocks there
let had_mocks_message =
Expand Down

0 comments on commit 1cd2c56

Please sign in to comment.