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
Install dependencies in an existing project:
forge install
To add a new dependency:
forge install <dependency>
# Example
forge install openzeppelin/openzeppelin-contracts
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
To run tests:
forge test
Verbosity
-vv
showsconsole.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.
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>
Refer to Cheatcodes Reference for all available cheatcodes.
// 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)
// 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)
// 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)
// 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)
// 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));
}
// 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)
// 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)
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);
...
}