🎓 A1Learner
🔍
0 of 48 lessons completed

Solidity Cheatcodes in Depth

A cheatcode is a special helper the test runner offers that changes the test chain itself. A cheatcode can change the caller, fund an account, move the clock, or demand that the next call reverts.

Prank makes the next call come from another account

Without a prank, the caller is the test contract itself. With a prank, the next call comes from the chosen account instead.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract CallerLog {
    address public lastCaller;

    function record() public {
        lastCaller = msg.sender;
    }
}

Deal funds an account and deltas prove the movement

Deal gives the chosen account five ether. The deposit then moves exactly two ether, shown as a change from the balance before.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract DepositVault {
    function deposit() public payable {}
}

Warp moves time and roll moves block height

Warp sets the block timestamp to one thousand. Roll sets the block number to five hundred, and each read matches the chosen value.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Clock {
    function nowIs() public view returns (uint256) {
        return block.timestamp;
    }

    function heightIs() public view returns (uint256) {
        return block.number;
    }
}

Expect revert demands the next call fails

The counter is at zero, so decreasing it must fail with the reason below zero. Expect revert states that demand before the call.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Counter {
    uint256 public count;

    function increment() public {
        count += 1;
    }

    function decrement() public {
        require(count > 0, "below zero");
        count -= 1;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract TimedVault {
    uint256 public unlockTime;
    uint256 public totalDeposited;

    constructor(uint256 _unlockTime) {
        unlockTime = _unlockTime;
    }

    function deposit() public payable {
        totalDeposited += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(block.timestamp >= unlockTime, "still locked");
        require(amount <= address(this).balance, "insufficient vault balance");
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "send failed");
    }
}

contract OriginRecorder {
    function who() public view returns (address sender, address origin) {
        return (msg.sender, tx.origin);
    }
}

Key points

  • Cheatcode: a cheatcode is a test-only helper that changes the sender, balance, time, or block height of the test chain.
  • Prank: a prank makes the next call come from a chosen address.
  • Deal: a deal sets an account's ether balance to a chosen amount.
  • Warp: a warp moves the block timestamp forward to a chosen time.
  • Roll: a roll moves the block number to a chosen height.
  • Expect revert: an expect revert statement demands that the very next call fails with a given reason.

In this example, a vault locks deposits until a chosen time. An early withdrawal is demanded to revert, a warp moves the clock to the unlock time, and the later withdrawal pays out exactly the amount claimed as a balance change. A separate recorder shows the two-argument prank setting both the sender and the origin.

Note

cheatcodes run only in tests, and they never appear in the contract code itself.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

Which cheatcode sets an account's ether balance?