Cheatcodes
To manipulate the state of the blockchain, as well as test for specific reverts and events, Foundry is shipped with a set of cheatcodes.
Cheatcodes are accessed using the vm
instance available in Forge Standard Library's Test
contract.
prank
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.
If the alternative signature of prank
is used, then tx.origin
is set as well for the next call.
/// function withdraw() public {
/// require(msg.sender == owner);
vm.prank(owner);
myContract.withdraw(); // [PASS]
startPrank
Sets msg.sender
for all subsequent calls until stopPrank
is called.
If the alternative signature of startPrank
is used, then tx.origin
is set as well for all subsequent calls.
function startPrank(address) external;
function startPrank(address sender, address origin) external;
stopPrank
Stops an active prank started by startPrank
, resetting msg.sender
and tx.origin
to the values before startPrank
was called.
function stopPrank() external;
deal
Sets the balance of an address who
to newBalance
.
function deal(address who, uint256 newBalance) external;
vm.deal(alice, 1 ether);
hoax
Similar to prank
+ deal
except that it adds to the existing user's balance where deal
resets the balance to the specified amount.
function hoax(address who, uint256 give) public;
txGasPrice
Sets tx.gasprice
for the rest of the transaction.
function txGasPrice(uint256) external;
function txGasPrice(uint256 newGasPrice) external;
// Example usage
uint256 gasStart = gasleft();
vm.txGasPrice(GAS_PRICE);
// ...Do things...
uint256 gasEnd = gasleft();
uint256 gasUsed = (gasStart - gasEnd) * GAS_PRICE;
console.log("Gas used: ", gasUsed);
warp
Sets block.timestamp
vm.warp(1641070800);
emit log_uint(block.timestamp); // 1641070800
roll
Sets block.number
.
vm.roll(100);
emit log_uint(block.number); // 100
recordLogs
Tells the VM to start recording all the emitted events. To access them, use getRecordedLogs
.
vm.recordLogs(); // Start recording logs
raffle.performUpkeep(""); // Call function that emits event log
Vm.Log[] memory entries = vm.getRecordedLogs(); // Store emitted log
// Access stored log by knowing exactly which logs are emitted
// .topics[0] would refer to the entire event
// .topics[1] is for the first actual topic
// All logs are recorded as bytes32 in foundry
bytes32 requestId = entries[1].topics[1];
expectEmit
Assert a specific log is emitted during the next call.
function expectEmit() external;
function expectEmit(
bool checkTopic1,
bool checkTopic2,
bool checkTopic3,
bool checkData
) external;
function expectEmit(address emitter) external;
function expectEmit(
bool checkTopic1,
bool checkTopic2,
bool checkTopic3,
bool checkData,
address emitter
) external;
Call the cheat code, specifying whether we should check the first, second or third topic, and the log data (
expectEmit
()
checks them all). Topic 0 is always checked.Emit the event we are supposed to see during the next call.
Perform the call.
You can perform steps 1 and 2 multiple times to match a sequence of events in the next call.
If the event is not available in the current scope (e.g. if we are using an interface, or an external smart contract), we can define the event ourselves with an identical event signature.
There are 2 varieties of expectEmit
:
Without checking the emitter address: Asserts the topics match without checking the emitting address.
With
address
: Asserts the topics match and that the emitting address matches.
expectEmit Examples
// The first parameter: Whether to check the event signature.
// The second parameter: Whether to check the indexed parameters (topics) of the event.
// The third parameter: Whether to check the unindexed parameters (data) of the event.
// The fourth parameter: Whether to check the event data's values.
vm.expectEmit(true, true, true, false); // `false` for data means we don't care about the value
emit IAavePM.Rebalanced(0); // The data is a placeholder and not checked
event Transfer(address indexed from, address indexed to, uint256 amount);
function testERC20EmitsTransfer() public {
vm.expectEmit();
// 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);
}
event Transfer(address indexed from, address indexed to, uint256 amount);
function testERC20EmitsTransfer() public {
// We check that the token is the event emitter by passing the address.
vm.expectEmit(address(myToken));
emit MyToken.Transfer(address(this), address(1), 10);
// We perform the call.
myToken.transfer(address(1), 10);
}
function testERC20EmitsBatchTransfer() public {
// We declare multiple expected transfer events
for (uint256 i = 0; i < users.length; i++) {
// Here we use the longer signature for demonstration purposes. This call checks
// topic0 (always checked), topic1 (true), topic2 (true), NOT topic3 (false), and data (true).
vm.expectEmit(true, true, false, true);
emit Transfer(address(this), users[i], 10);
}
// We also expect a custom `BatchTransfer(uint256 numberOfTransfers)` event.
vm.expectEmit(false, false, false, true);
emit BatchTransfer(users.length);
// We perform the call.
myToken.batchTransfer(users, 10);
}
event Transfer(address indexed from, address indexed to, uint256 amount);
function testERC20EmitsTransfer() public {
// We check that the token is the event emitter by passing the address as the fifth argument.
vm.expectEmit(true, true, false, true, address(myToken));
emit MyToken.Transfer(address(this), address(1), 10);
// We perform an unrelated call that won't emit the intended event,
// making the cheatcode fail.
myToken.approve(address(this), 1e18);
// We perform the call, but it will have no effect as the cheatcode has already failed.
myToken.transfer(address(1), 10);
}
envUint
Read an environment variable as uint256
or uint256[]
.
vm.envUint("PRIVATE_KEY")
load
Read a storage value directly.
vm.load(targetContractAddress, bytes32(uint256(1)));
Last updated