Skip to content

Instantly share code, notes, and snippets.

@SpiralOutDotEu
Forked from MiloTruck/Foundry Cheatsheet.md
Created August 2, 2023 12:21
Show Gist options
  • Save SpiralOutDotEu/4e53ee6f4d5c573963fba028231d8211 to your computer and use it in GitHub Desktop.
Save SpiralOutDotEu/4e53ee6f4d5c573963fba028231d8211 to your computer and use it in GitHub Desktop.

Foundry Cheatsheet

Setup

Create a new project:

forge init <project_name>

Create a new project using a template:

forge init --template <template> <project_name>

# Example
forge init --template https://github.com/zobront/paradigm-ctf paradigm_ctf

Dependencies

Adding dependencies

Install dependencies in an existing project:

forge install

To add a new dependency:

forge install <dependency>

# Example
forge install openzeppelin/openzeppelin-contracts

Remappings

Forge can automatically deduce remappings:

forge remappings > remappings.txt

To customize a remapping, simply add it to remappings.txt:

echo "@openzeppelin/=lib/openzeppelin-contracts/" > remappings.txt

Testing

To run tests:

forge test

Verbosity

  • -vv shows console.log output.
  • -vvv shows execution traces for failing tests.
  • -vvvv shows execution traces for all tests, and setup traces for failing tests.
  • -vvvvv shows execution and setup traces for all tests.

To run specific tests:

  • --match-test runs tests matching the specified regex.
  • --match-contract runs tests in contracts matching the specified regex.
  • --match-path runs tests in source files matching the specified path.

Fork testing

To fork a network:

forge test --fork-url <rpc_url>

To identify contracts in a forked environment, pass your Etherscan API key using --etherscan-api-key:

forge test --fork-url <rpc_url> --etherscan-api-key <etherscan_api_key>

Useful Cheatcodes

Refer to Cheatcodes Reference for all available cheatcodes.

Global values

// Set block.timestamp
vm.warp(uint256 timestamp)

// Increase block.timestamp by specified seconds
skip(uint256 time)

// Decrease block.timestamp by specified seconds
rewind(uint256 time) 

// Set block.number
vm.roll(uint256 blockNumber)

Storage and memory

// Load a storage slot from an address
vm.load(address account, bytes32 slot) 

// Store a value to an address' storage slot
vm.store(address account, bytes32 slot)

// Set code at address
vm.etch(address addr, bytes calldata code)

Caller address

// Set msg.sender for the next call
vm.prank(address msgSender)

// Set msg.sender for subsequent calls
vm.startPrank(address msgSender)

// Reset msg.sender for subsequent calls
vm.stopPrank()

// Change msg.sender for subsequent calls
changePrank(address msgSender) 

Balances

// Set ether balance for an address
deal(address to, uint256 balance)

// Set ERC20 token balance for an address
deal(address token, address to, uint256 balance)

// Set ERC20 token balance for an address and increase totalSupply if `adjust` is true
deal(address token, address to, uint256 balance, bool adjust)

// Give ERC721 token with `id` to an address
dealERC721(address token, address to, uint256 id)

// Set ERC1155 token balance for an address
dealERC1155(address token, address to, uint256 id, uint256 balance)

// Set ERC1155 token balance for an address and adjust totalSupply
dealERC1155(address token, address to, uint256 id, uint256 balance, bool adjust)

Calls

// Mock calls to an address `where`. If the call data `data` matches, return `retdata`  
vm.mockCall(address where, bytes calldata data, bytes calldata retdata);

// Same as the above, but msg.value also has to match `value`
vm.mockCall(address where, uint256 value, bytes calldata data, bytes calldata retdata);

Example usage:

function testMockCall() public {
    // Without value
    vm.mockCall(
        address(token),
        abi.encodeWithSelector(token.balanceOf.selector, ALICE),
        abi.encode(10)
    );
    assertEq(token.balanceOf(ALICE), 10);

    // With value
    vm.mockCall(
        address(market),
        10 ether,
        abi.encodeWithSignature("pay(address,uint256)", ALICE, 10 ether),
        abi.encode(true)
    );
    assertTrue(market.pay{value: 10 ether}(ALICE, 10 ether));
}

Testing reverts

// Expect the next call to revert
vm.expectRevert()

// Expect the next call to revert with `message`
vm.expectRevert(bytes calldata message)

// Expect the next call to revert with `bytes4 data` (used for custom error selectors)
vm.expectRevert(bytes4 data)

Others

// Create a labelled address
address addr = makeAddr(string memory name)

// Create a labelled address with private key
(address addr, uint256 privateKey) = makeAddrAndKey(string memory name)

// Sign data
(uint8 v, bytes32 r, bytes32 s) = vm.sign(uint256 privateKey, bytes32 digest)

Fuzzing

Use vm.assume() to specify conditions for inputs. It should only be used for narrow checks:

function testSomething(uint256 v) public {
    vm.assume(v != 0);
    require(v != 0);
    ... 
}

Use bound() to restrict inputs to a certain range:

function testSomething(uint256 v) public {
    v = bound(v, 100, 500);
    require(v >= 100 && v <= 500);
    ... 
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment