foundry
foundry copied to clipboard
test cannot construct a proxy object while gas metering is on
Component
Forge
Have you ensured that all of these are up to date?
- [ ] Foundry
- [ ] Foundryup
What version of Foundry are you on?
forge 0.2.0 (34f684d 2023-11-01T22:19:24.989266000Z)
What command(s) is the bug in?
forge test
Operating System
None
Describe the bug
The following code reverts in setup. It does run OK gas metering is NOT disabled (which means, we can't filter out meter gas only on inner call)
pragma solidity ^0.8.17;
import {Test} from "forge-std/Test.sol";
import "forge-std/Vm.sol";
import {console} from "hardhat/console.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
contract MyObject is Ownable, UUPSUpgradeable, Initializable {
constructor() {
_disableInitializers();
}
function initialize(address anOwner) public virtual initializer {
_transferOwnership(anOwner);
}
function _authorizeUpgrade(address newImplementation) internal override {}
}
contract TestProxy is Test {
MyObject singleton = new MyObject();
function setUp() public {
//with this line enabled, the test revert. comment this line, and the test will pass
vm.pauseGasMetering();
}
function newObj(address owner, uint salt) internal returns (MyObject) {
return MyObject(address(new ERC1967Proxy{salt: bytes32(salt)}(
address(singleton),
abi.encodeCall(MyObject.initialize, (owner))
)));
}
function test_send1() public {
MyObject obj = newObj(msg.sender, 0);
vm.resumeGasMetering();
//this is the method we want to test (and meter for gas)
obj.owner();
}
}
This is the test result when running with forge test -vvv
[FAIL. Reason: EvmError: Revert] test_send1() (gas: 9223372036854754743)
Traces:
[0] TestProxy::test_send1()
├─ [0] → new <Unknown>@0x3E6e1b3d0069A017c50Cc1d758207728ABb19fdc
│ ├─ emit Upgraded(implementation: MyObject: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f])
│ ├─ [0] MyObject::initialize(DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38]) [delegatecall]
│ │ ├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38])
│ │ ├─ emit Initialized(version: 1)
│ │ └─ ← ()
│ └─ ← 0 bytes of code <<<< this is a constructor call that returns ZERO BYTES
└─ ← "EvmError: Revert"
I just ran into this trying to deploy a contract that deploys a (non-proxy) implementation contract for clones
I suspect that this is an issue with nested-creates in general
The pauseGasMetering
cheatcode has a special case for temporarily re-enabling during contract creates – I suspect that it does not handle nested creates correctly
See code here: https://github.com/foundry-rs/foundry/blob/ff391b5fbef93a5dc6d28055501d5bdc39aee1c8/crates/cheatcodes/src/inspector.rs#L204
This should probably be an Option<Vec<Option<Gas>>>
for each level of CREATE