Eridian
eridian.xyzx.comGitHub
  • ๐Ÿ“–Eridian Docs
  • Ethereum Dev
    • โœ๏ธEthereum Notes
      • ๐ŸŽ›๏ธTechnical Basics
      • ๐ŸชงEthereum Addresses
      • ๐Ÿ“šEthereum State Explained
      • โ›ฝGas Fees Explained
    • ๐Ÿ”งUseful Tools
      • โ˜๏ธEthers
      • *๏ธโƒฃEthernal
    • ๐Ÿ“Solidity Notes
      • โ”Interview Questions
        • ๐ŸŸข1. Easy - Interview Questions
        • ๐ŸŸ 2. Medium - Interview Questions
        • ๐ŸŸก3. Hard - Interview Questions
        • ๐Ÿ”ด4. Advanced - Interview Questions
      • ๐Ÿ’กNote Ideas
      • ABI
      • abi.encodePacked
      • Abstract Contracts
      • Arrays
      • Casting
      • CEI - Checks, Effects, Interactions
      • Comments (NATSPEC)
      • Constructor
      • Contract Structure & Versions
      • Data - Storage vs Memory
      • Data - Storage Layout
      • Enum
      • Errors (require & revert)
      • Events
      • EVM Opcodes
      • External Contract Interaction
      • ๐Ÿ—๏ธExternal Dependencies
      • Functions
      • Function Modifiers
      • If / Else / For / While Loops
      • Inheritance
      • Interfaces
      • Keccak256
      • Library
      • Mappings
      • msg.sender
      • Objects & Types
      • OpenZeppelin
      • Payable
      • Public State Variable vs Function
      • Receive & Fallback
      • Security
      • Self Destruct
      • Send ETH (transfer, send, call)
      • Stack Too Deep
      • Structs
      • Style Guide
      • Time Units
      • Try / Catch
      • Typecasting
      • Using Directive
      • Variables, Consts & Immutable
      • Withdraws
    • โš’๏ธFoundry Notes
      • ๐Ÿ“–Docs & GitHub Pages
      • ๐ŸคUseful Commands
        • ๐Ÿ”จAnvil
        • ๐Ÿช„Cast
        • ๐Ÿ”ฅForge
      • ๐ŸงชTests
        • Cheatcodes
      • ๐Ÿ“Useful Scripts
        • Deploy Contract Using Hex
    • ๐Ÿ‘พDeFi Challenges
      • ๐Ÿ‘จโ€๐Ÿš€Ethernaut
        • Ethernaut - Template
        • Level 1 - Fallback โบ
        • Level 2 - Fallout โบ
        • Level 3 - Coin Flip โบโบ
        • Level 4 - Telephone โบ
        • Level 5 - Token โบโบ
        • Level 6 - Delegation โบโบ
        • Level 7 - Force โบโบโบ
        • Level 8 - Vault โบโบ
        • Level 9 - King โบโบโบ
        • Level 10 - Re-entrancy โบโบโบ
        • Level 11 - Elevator โบโบ
        • Level 12 - Privacy โบโบโบ
        • Level 13 - Gatekeeper 1 โบโบโบโบ
        • Level 14 - Gatekeeper 2 โบโบโบ
        • Level 15 - Naught Coin โบโบโบ
        • Level 16 - Preservation โบโบโบโบ
        • Level 17 - Recovery โบโบโบ
        • Level 18 - Magic Number โบโบโบ
        • Level 19 - Alien Codex โบโบโบโบ
        • Level 20 - Denial โบโบโบ
        • Level 21 - Shop โบโบ
        • Level 22 - Dex โบโบ
        • Level 23 - Dex Two โบโบ
        • Level 24 - Puzzle Wallet โบโบโบโบ
        • Level 25 - Motorbike โบโบโบ
        • Level 26 - DoubleEntryPoint โบโบ
        • Level 27 - Good Samaritan โบโบโบ
        • Level 28 - Gatekeeper 3 โบโบโบ
        • Level 29 - Switch โบโบโบโบ
        • Level 30 - Higher Order โบโบโบโบ
        • Level 31 - Stake โบโบโบ
      • ๐Ÿ’ธDamn Vulnerable DeFi
    • ๐Ÿ”Auditing
      • ๐Ÿ—ž๏ธExploit Resources
      • ๐Ÿ”งAudit Tools
    • ๐Ÿค–MEV
  • Infrastructure Docs
    • ๐Ÿ’ปHardware
    • ๐ŸงLinux
      • ๐Ÿ“–Linux Glossary
      • โŒจ๏ธLinux Commands
      • ๐Ÿ’พInstallation
      • ๐Ÿ—๏ธMaintenance
      • ๐Ÿ–ฅ๏ธUbuntu Desktop
      • ๐Ÿ›œZeroTier
      • ๐ŸŽž๏ธTMUX
      • ๐Ÿ”ตBluetooth
    • โ›“๏ธEthereum Clients
      • โš™๏ธExecution Clients
        • โ›๏ธGeth
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
        • ๐ŸปErigon
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
        • ๐ŸงถBesu
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
      • ๐ŸคBeacon Clients
        • ๐Ÿ’กLighthouse
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
        • ๐Ÿช…Teku
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
      • ๐Ÿ’ŽValidator Clients
        • ๐Ÿ’กLighthouse
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
      • โž•L2 Clients
        • ๐Ÿ”ตBase
          • ๐Ÿ’พInstallation
          • โŒจ๏ธUseful Commands
          • ๐Ÿ—๏ธMaintenance
      • ๐Ÿ’ฐMEV Boost
        • ๐Ÿ’พInstallation
        • ๐Ÿ—๏ธMaintenance
    • ๐ŸšจAlerting and Monitoring
      • ๐Ÿ”ฅPrometheus
      • ๐ŸŒก๏ธHealthChecks.io
      • ๐Ÿ“ŸPagerDuty
  • General Dev
    • ๐Ÿ’พGit Notes
      • Repos
      • Committing changes
      • Branches
      • Merging & Rebasing
      • PRs
Powered by GitBook
On this page
  • prank
  • startPrank
  • stopPrank
  • deal
  • hoax
  • txGasPrice
  • warp
  • roll
  • recordLogs
  • expectEmit
  • expectEmit Examples
  • envUint
  • load
Edit on GitHub
  1. Ethereum Dev
  2. Foundry Notes
  3. Tests

Cheatcodes

Last updated 9 months ago

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.

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

vm.prank(owner);
myContract.withdraw(); // [PASS]

startPrank

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

If the alternative signature of startPrank is used, then tx.origin is set as well for all subsequent calls.

startPrank example
function startPrank(address) external;
function startPrank(address sender, address origin) external;

stopPrank

startPrank example
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.

txGasPrice example
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;
  1. 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.

  2. Emit the event we are supposed to see during the next call.

  3. 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.

Matching sequences

In functions that emit a lot of events, it's possible to "skip" events and only match a specific sequence, but this sequence must always be in order. As an example, let's say a function emits events: A, B, C, D, E, F, F, G.

expectEmit will be able to match ranges with and without skipping events in between:

  • [A, B, C] is valid.

  • [B, D, F] is valid.

  • [G] or any other single event combination is valid.

  • [B, A] or similar out-of-order combinations are invalid (events must be in order).

  • [C, F, F] is valid.

  • [F, F, C] is invalid (out of order).

expectEmit Examples

Just check that an event happens but no details about it
// 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
This does not check the emitting address.
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);
}
This does check the emitting address.
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);
}
We can also assert that multiple events are emitted in a single call.
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);
}
This example fails, as the expected event is not emitted on the next call.
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)));

Stops an active prank started by , resetting msg.sender and tx.origin to the values before startPrank was called.

โš’๏ธ
๐Ÿงช
stopPrank
startPrank