foundry
foundry copied to clipboard
Document `InstructionResult`
Component
Forge
Have you ensured that all of these are up to date?
- [X] Foundry
- [X] Foundryup
What version of Foundry are you on?
forge 0.2.0 (1a4960d 2024-03-20T00:28:07.727577000Z)
What command(s) is the bug in?
forge test
Operating System
macOS (Intel)
Describe the bug
I hate that I'm doing this, because the reproducibility is... weird. Here is my test contract, you can copy paste the code directly into a blank foundry template created from forge init
.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
contract CounterTest is Test {
function setUp() public {}
function testWeirdness() public {
address addr = deployStuff();
bool response = sendCalldata(addr);
assert(response);
}
function deployStuff() public returns (address addr) {
bytes memory bytecode =
hex"60a88060093d393df3602035335f540114156100a4575f3560e01c8063e18d4afd1461003e57806390949f111461005757806308949a76146100715780637aa9a7f91461008b575b5f545f1461004a575f5ffd5b60015f5560075f5260205ff35b5f54600114610064575f5ffd5b60025f5560075f5260205ff35b5f5460021461007e575f5ffd5b60035f5560075f5260205ff35b5f54600314610098575f5ffd5b5f5f5560075f5260205ff35b5f5ffd";
assembly {
// s11Helper := create(0, add(bytecode, 0x20), mload(bytecode))
addr := create(callvalue(), add(bytecode, 0x20), mload(bytecode))
}
require(addr != address(0), "Failed to deploy S11Helper");
}
function sendCalldata(address addr) public returns (bool) {
// Data doesn't really matter
bytes memory data = abi.encode(uint256(0), uint256(1), uint256(2));
(bool success,) = addr.call(data);
if (!success) {
return false;
}
return true;
}
}
Running the following command gets an expected fail:
forge test --mt testWeirdness
However why it fails is a mystery to me, it seems foundry just "cuts out" the call. Here is an image of calling the bytecode deployed contract using the debug flag:
This line: (bool success,) = addr.call(data);
so inside the addr
contract.
forge test --debug testWeirdness
We see the call ends with a PUSH0
opcode. However, looking at the deployed bytecode, there should at LEAST be another SLOAD
opcode after the call. the RETURN
opcode from the contract deployment code is f3
so we can remove60a88060093d393df3
from the bytecode, and we are left with the runtime code. The first few opcodes there are at least:
PUSH1 0x20
CALLDATALOAD
CALLER
PUSH0
SLOAD
ADD
So why is the call showing kicking the bucket after PUSH0
?
What I've tried
According to the EVM, there are a few reasons why a CALL
opcode will fail:
1. Not enough gas.
2. Not enough values on the stack.
3. The current execution context is from a [STATICCALL](https://www.evm.codes/#FA) and the [value](https://www.evm.codes/#34) (stack index 2) is not 0 (since Byzantium fork).
I tried running with {gas: 100}
on the call, and saw no difference, but running with {gas: 6}
seemed to correctly limit the opcodes to just:
PUSH1 0x20
CALLDATALOAD
CALLER
So adding more gas probably isn't the issue, since removing gas works fine.
2. Not enough values on the stack.
: This doesn't make sense since we are in a new call context
And 3
doesn't make sense since we are not in a staticcall.
So I'm not sure what's going on.