Skip to content

Commit a2b46c7

Browse files
authored
gas metering cheatcodes + modifier (#248)
1 parent d666309 commit a2b46c7

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

src/StdCheats.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ abstract contract StdCheats is StdCheatsSafe {
431431
StdStorage private stdstore;
432432
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
433433

434+
bool private gasMeteringOff;
435+
434436
// Skip forward or rewind time by the specified number of seconds
435437
function skip(uint256 time) internal virtual {
436438
vm.warp(block.timestamp + time);
@@ -528,6 +530,27 @@ abstract contract StdCheats is StdCheatsSafe {
528530
} catch (bytes memory) {}
529531
}
530532

533+
modifier noGasMetering() {
534+
vm.pauseGasMetering();
535+
// To prevent turning gas monitoring back on with nested functions that use this modifier,
536+
// we check if gasMetering started in the off position. If it did, we don't want to turn
537+
// it back on until we exit the top level function that used the modifier
538+
//
539+
// i.e. funcA() noGasMetering { funcB() }, where funcB has noGasMetering as well.
540+
// funcA will have `gasStartedOff` as false, funcB will have it as true,
541+
// so we only turn metering back on at the end of the funcA
542+
bool gasStartedOff = gasMeteringOff;
543+
gasMeteringOff = true;
544+
545+
_;
546+
547+
// if gas metering was on when this modifier was called, turn it back on at the end
548+
if (!gasStartedOff) {
549+
gasMeteringOff = false;
550+
vm.resumeGasMetering();
551+
}
552+
}
553+
531554
modifier skipWhenForking() {
532555
if (!isFork()) {
533556
_;

src/Vm.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,11 @@ interface VmSafe {
200200
function rpcUrlStructs() external view returns (Rpc[] memory);
201201
// If the condition is false, discard this run's fuzz inputs and generate new ones.
202202
function assume(bool) external pure;
203+
204+
// Pauses gas metering (i.e. gas usage is not counted). Noop if already paused.
205+
function pauseGasMetering() external;
206+
// Resumes gas metering (i.e. gas usage is counted again). Noop if already on.
207+
function resumeGasMetering() external;
203208
}
204209

205210
interface Vm is VmSafe {

test/StdCheats.t.sol

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,41 @@ contract StdCheatsTest is Test {
219219
receipts;
220220
}
221221

222+
function testGasMeteringModifier() public {
223+
uint256 gas_start_normal = gasleft();
224+
addInLoop();
225+
uint256 gas_used_normal = gas_start_normal - gasleft();
226+
227+
uint256 gas_start_single = gasleft();
228+
addInLoopNoGas();
229+
uint256 gas_used_single = gas_start_single - gasleft();
230+
231+
uint256 gas_start_double = gasleft();
232+
addInLoopNoGasNoGas();
233+
uint256 gas_used_double = gas_start_double - gasleft();
234+
235+
emit log_named_uint("Normal gas", gas_used_normal);
236+
emit log_named_uint("Single modifier gas", gas_used_single);
237+
emit log_named_uint("Double modifier gas", gas_used_double);
238+
assertTrue(gas_used_double + gas_used_single < gas_used_normal);
239+
}
240+
241+
function addInLoop() internal returns (uint256) {
242+
uint256 b;
243+
for (uint256 i; i < 10000; i++) {
244+
b += i;
245+
}
246+
return b;
247+
}
248+
249+
function addInLoopNoGas() internal noGasMetering returns (uint256) {
250+
return addInLoop();
251+
}
252+
253+
function addInLoopNoGasNoGas() internal noGasMetering returns (uint256) {
254+
return addInLoopNoGas();
255+
}
256+
222257
function bytesToUint_test(bytes memory b) private pure returns (uint256) {
223258
uint256 number;
224259
for (uint256 i = 0; i < b.length; i++) {

0 commit comments

Comments
 (0)