Skip to content

Latest commit

 

History

History
660 lines (472 loc) · 18.2 KB

cheatcodes.md

File metadata and controls

660 lines (472 loc) · 18.2 KB

Cheatcodes Reference

Cheatcodes give you the ability to alter the state of the EVM, mock data, assert on reverts, and more.

To enable a cheatcode you call designated functions on the cheatcode address: 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. This address can be accessed through the HEVM_ADDRESS constant in DSTest.

Cheatcodes Interface

This is a complete overview of all the available cheatcodes. For detailed descriptions and example usage, see below.

interface CheatCodes {

    function warp(uint256) external;
    // Set block.timestamp

    function roll(uint256) external;
    // Set block.number

    function fee(uint256) external;
    // Set block.basefee

    function load(address account, bytes32 slot) external returns (bytes32);
    // Loads a storage slot from an address

    function store(address account, bytes32 slot, bytes32 value) external;
    // Stores a value to an address' storage slot

    function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);
    // Signs data

    function addr(uint256 privateKey) external returns (address);
    // Computes address for a given private key

    function ffi(string[] calldata) external returns (bytes memory);
    // Performs a foreign function call via terminal

    function prank(address) external;
    // Sets the *next* call's msg.sender to be the input address

    function startPrank(address) external;
    // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called

    function prank(address, address) external;
    // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input

    function startPrank(address, address) external;
    // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input

    function stopPrank() external;
    // Resets subsequent calls' msg.sender to be `address(this)`

    function deal(address who, uint256 newBalance) external;
    // Sets an address' balance

    function etch(address who, bytes calldata code) external;
    // Sets an address' code

    function expectRevert(bytes calldata) external;
    function expectRevert(bytes4) external;
    // Expects an error on next call

    function record() external;
    // Record all storage reads and writes

    function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes);
    // Gets all accessed reads and write slot from a recording session, for a given address

    function expectEmit(bool, bool, bool, bool) external;
    // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData).
    // Call this function, then emit an event, then call a function. Internally after the call, we check if
    // logs were emitted in the expected order with the expected topics and data (as specified by the booleans)

    function mockCall(address, bytes calldata, bytes calldata) external;
    // Mocks a call to an address, returning specified data.
    // Calldata can either be strict or a partial match, e.g. if you only
    // pass a Solidity selector to the expected calldata, then the entire Solidity
    // function will be mocked.

    function clearMockedCalls() external;
    // Clears all mocked calls

    function expectCall(address, bytes calldata) external;
    // Expect a call to an address with the specified calldata.
    // Calldata can either be strict or a partial match

    function getCode(string calldata) external returns (bytes memory);
    // Gets the bytecode for a contract in the project given the path to the contract.

    function label(address addr, string calldata label) external;
    // Label an address in test traces

    function assume(bool) external;
    // When fuzzing, generate new inputs if conditional not met
}

Cheatcodes

This section documents all cheat codes, gotchas, and provides usage examples.

warp

function warp(uint256) external;

Sets block.timestamp.

Example
cheats.warp(1641070800);
emit log_uint(block.timestamp); // 1641070800


roll

function roll(uint256) external;

Sets block.number.

Example
cheats.roll(100);
emit log_uint(block.number); // 100


fee

function fee(uint256) external;

Sets block.basefee.

Example
cheats.fee(25 gwei);
emit log_uint(block.basefee); // 25000000000


load

function load(address account, bytes32 slot) external returns (bytes32);

Loads the value from storage slot slot on account account.

Example
/// contract LeetContract {
///     uint256 private leet = 1337; // slot 0
/// }

bytes32 leet = cheats.load(address(leetContract), bytes32(uint256(0)));
emit log_uint(uint256(leet)); // 1337


store

function store(address account, bytes32 slot, bytes32 value) external;

Stores the value value in storage slot slot on account account.

Example
/// contract LeetContract {
///     uint256 private leet = 1337; // slot 0
/// }

cheats.store(address(leetContract), bytes32(uint256(0)), bytes32(uint256(31337)));
bytes32 leet = cheats.load(address(leetContract), bytes32(uint256(0)));
emit log_uint(uint256(leet)); // 31337


sign

function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);

Signs a digest digest with private key privateKey, returning (v, r, s).

This is useful for testing functions that take signed data and perform an ecrecover to verify the signer.

Example
address alice = cheats.addr(1);
bytes32 hash = keccak256("Signed by Alice");
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(1, hash);
address signer = ecrecover(hash, v, r, s);
assertEq(alice, signer); // [PASS]


addr

function addr(uint256 privateKey) external returns (address);

Computes the address for a given private key.

Example
address alice = cheats.addr(1);
emit log_address(alice); // 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf


ffi

function ffi(string[] calldata) external returns (bytes memory);

Calls an arbitrary command if ffi is enabled.

It is generally advised to use this cheat code as a last resort, and to not enable it by default, as anyone who can change the tests of a project will be able to execute arbitrary commands on devices that run the tests.

Example
string[] memory inputs = new string[](3);
inputs[0] = "echo";
inputs[1] = "-n";
// ABI encoded "gm", as a string
inputs[2] = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000";

bytes memory res = cheats.ffi(inputs);
string memory output = abi.decode(res, (string));
assertEq(output, "gm");


prank

function prank(address) external;

Sets msg.sender to the specified address for the next call. "The next call" includes static calls as well, but not calls to the cheat code address.

Example
/// function withdraw() public {
///     require(msg.sender == owner);

cheats.prank(owner);
myContract.withdraw(); // [PASS]
Alternative Signature
function prank(address sender, address origin) external;

Sets msg.sender to sender and tx.origin to origin for the next call.

startPrank

function startPrank(address) external;

Sets msg.sender for all subsequent calls until stopPrank is called.

Alternative Signature
function startPrank(address sender, address origin) external;

Sets msg.sender to sender and tx.origin to origin for all subsequent calls until stopPrank is called.

stopPrank

function stopPrank() external;

Resets msg.sender to FOUNDRY_SENDER. Always used in conjunction with startPrank.



deal

function deal(address who, uint256 newBalance) external;

Sets the balance of an address who to newBalance.

Example
address alice = address(1);
cheats.deal(alice, 1 ether);
log_uint256(alice.balance); // 1000000000000000000


etch

function etch(address who, bytes calldata code) external;

Sets the bytecode of an address who to code.

Example
address code = address(awesomeContract).code;
address targetAddr = address(1);
cheats.etch(targetAddr, code);
log_bytes(address(targetAddr).code); // 0x6080604052348015610010...


expectRevert

function expectRevert(bytes calldata msg) external;

If the next call does not revert with message msg, then expectRevert will.

To use expectRevert with a string, convert it to bytes.

cheats.expectRevert(bytes("error message"));

To use expectRevert with a custom error type with parameters, ABI encode the error type.

cheats.expectRevert(
  abi.encodeWithSelector(MyContract.CustomError.selector, 1, 2)
);

After calling cheats.expectRevert(), calls to other cheat codes before the reverting call are ignored. This means, for example, we can call cheats.prank(user) immediately before the reverting call.

Alternative Signature
function expectRevert(bytes4) external;

An alternative version of expectRevert that only takes an error type signature. Useful for custom error types that do not take parameters.

cheats.expectRevert(MyContract.CustomError.selector)


record

function record() external;

Tell the VM to start recording all storage reads and writes. To access the reads and writes, use accesses.

accesses

function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes);

Gets all storage slots that have been read (reads) or written to (writes) on an address.

Note that record must be called first.

Example
/// contract NumsContract {
///     uint256 public num1 = 100; // slot 0
///     uint256 public num2 = 200; // slot 1
/// }

cheats.record();
numsContract.num2();
(bytes32[] memory reads, bytes32[] memory writes) = cheats.accesses(
  address(numsContract)
);
emit log_uint(uint256(reads[0])); // 1


expectEmit

function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external;

This cheat code is used to assert that certain logs are emitted on the next call.

  1. Call the cheat code, specifying whether we should check the first, second or third topic, and the log data. Topic 0 is always checked.
  2. Emit the event we are supposed to see after the next call.
  3. Perform the call.

If the event is not available in the current scope (e.g because we are using an interface, or an external smart contract), we can define the event ourselves with an identical event signature.

The cheatcode does not check the origin of the event, but simply that it was emitted during that call.

For example:

event Transfer(address indexed from, address indexed to, uint256 amount);

function testERC20EmitsTransfer() public {
  // Only `src` and `dst` are indexed in ERC20's `Transfer` event,
  // so we specifically check topics 1 and 2 (topic 0 is always checked by default),
  // as well as the data (`amount`).
  cheats.expectEmit(true, true, false, true);

  // We emit the event we expect to see.
  emit MyToken.Transfer(address(this), address(1), 10);

  // We perform the call.
  myToken.transfer(address(1), 10);
}

Calls to other cheat codes before the final call are ignored, meaning we can also do something like this:

function testERC20EmitsTransfer() public {
  // The first two lines are the same as above.
  cheats.expectEmit(true, true, false, true);
  emit MyToken.Transfer(address(alice), address(1), 10);

  // Use the `prank` cheat code to impersonate a user.
  cheats.prank(address(alice));

  // We perform the call.
  myToken.transfer(address(1), 10);
}

We can also assert that multiple events are emitted in a single call. For example:

function testERC20EmitsBatchTransfer() public {
  // We declare multiple expected transfer events
  for (uint256 i = 0; i < users.length; i++) {
    cheats.expectEmit(true, true, true, true);
    emit Transfer(address(this), users[i], 10);
  }

  // We also expect a custom `BatchTransfer(uint256 numberOfTransfers)` event.
  cheats.expectEmit(false, false, false, false);
  emit BatchTransfer(users.length);

  // We perform the call.
  myToken.batchTransfer(users, 10);
}


mockCall

function mockCall(address where, bytes calldata data, bytes calldata retdata) external;

Mocks all calls to an address where if the call data either strictly or loosely matches data and returns retdata.

When a call is made to where the call data is first checked to see if it matches in its entirety with data. If not, the call data is checked to see if there is a partial match, with the match starting at the first byte of the call data.

If a match is found, then retdata is returned from the call.

Mocked calls are in effect until clearMockedCalls is called.

💬 Note

Calls to mocked addresses may revert if there is no code on the address. This is because Solidity inserts an extcodesize check before some contract calls.

To circumvent this, use the etch cheatcode if the mocked address has no code.

Mocking an exact call
function testMockCall() public {
  cheats.mockCall(
    address(0),
    abi.encodeWithSelector(MyToken.balanceOf.selector, address(1)),
    abi.encode(10)
  );
  assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
}
Mocking an entire function
function testMockCall() public {
  cheats.mockCall(
    address(0),
    abi.encodeWithSelector(MyToken.balanceOf.selector),
    abi.encode(10)
  );
  assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
  assertEq(IERC20(address(0)).balanceOf(address(2)), 10);

  // and so on..
}

clearMockedCalls

function clearMockedCalls() external;

Clears all mocked calls.



expectCall

function expectCall(address where, bytes calldata data) external;

Expects at least one call to address where where the call data either strictly or loosely matches data.

When a call is made to where the call data is first checked to see if it matches in its entirety with data. If not, the call data is checked to see if there is a partial match, with the match starting at the first byte of the call data.

If the test terminates without the call being made, the test fails.

Example
bytes memory expectedData = abi.encodeWithSignature("fulfillRandomness(bytes32,uint256)", requestId, randomness);
cheats.expectCall(address(awesomeContract), expectedData);
vrfCoordinator.callBackWithRandomness(requestId, randomness, address(awesomeContract));
// [PASS]


getCode

function getCode(string calldata) external returns (bytes memory);

Returns the bytecode for a contract in the project given the path to the contract.

The calldata parameter can either be in the form ContractFile.sol (if the filename and contract name are the same), ContractFile.sol:ContractName, or ./path/to/artifact.json

Example
MyContract myContract = new MyContract(arg1, arg2);

// Let's do the same thing with `getCode`
bytes memory args = abi.encode(arg1, arg2);
bytes memory bytecode = abi.encodePacked(cheats.getCode("MyContract.sol:MyContract"), args);
address anotherAddress;
assembly {
    anotherAddress := create(0, add(bytecode, 0x20), mload(bytecode))
}

assertEq0(address(myContract).code, anotherAddress.code); // [PASS]

If you'd like to use getCode to deploy a contract's bytecode, you can also use Forge Std's deployCode helper. In your test file:

    function testDeployCode() public {
        // deployCode takes a string argument for the contract to deploy
        // and optionally a bytes argument for any arguments that should
        // be passed to your contract's constructor
        address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", bytes(""));
        ...
    }


label

function label(address addr, string label) external;

Sets a label label for addr in test traces.

If an address is labelled, the label will show up in test traces instead of the address.



assume

function assume(bool) external;

If the boolean expression evaluates to false, discard the current fuzz inputs and start a new fuzz run.

The assume cheatcode should mainly be used for very narrow checks. Broad checks will slow down tests as it will take a while to find valid values, and the test may fail if you hit the max number of rejects. You can configure the rejection thresholds by setting fuzz_max_local_rejects and fuzz_max_global_rejects in your foundry.toml file. More information on filtering via assume can be found here.

For broad checks, such as ensuring a uint256 falls within a certain range, you can bound your input with the modulo operator or Solmate's bound method.

Example
// Good example of using assume
function testX(uint256 a) public {
    cheats.assume(a != 1);
    require(a != 1);
    // [PASS]
}

// In this case assume is not a great fit, so you should bound inputs manually
function testY(uint256 a) public {
    a = bound(a, 100, 1e36);
    require(a >= 100 && a <= 1e36);
    // [PASS]
}