Tutoriel·8 min de lecture·Par Solingo

Foundry Cheatcodes You're Probably Not Using

Beyond vm.prank. Powerful cheatcodes for fuzzing, forking, and simulating real-world scenarios.

# Foundry Cheatcodes You're Probably Not Using

Most Foundry users know vm.prank and vm.expectRevert. But Forge ships with 100+ cheatcodes, many of which are incredibly powerful for edge case testing, fuzzing, and mainnet fork simulations.

Here are 10 underused cheatcodes that will level up your testing.

1. vm.assume — Constrain Fuzz Inputs

When fuzzing, you often need to exclude invalid inputs. Instead of require, use vm.assume:

Before

function testDeposit(uint256 amount) public {

require(amount > 0 && amount < 1e30); // BAD — wastes fuzz runs

vault.deposit(amount);

}

After

function testDeposit(uint256 amount) public {

vm.assume(amount > 0 && amount < 1e30);

vault.deposit(amount);

}

vm.assume tells the fuzzer to discard invalid inputs instead of counting them as passing tests. This gives you better coverage.

Advanced: Multiple Constraints

function testSwap(uint256 amountIn, uint256 reserveA, uint256 reserveB) public {

vm.assume(amountIn > 1000);

vm.assume(reserveA > 1e18 && reserveA < 1e24);

vm.assume(reserveB > 1e18 && reserveB < 1e24);

vm.assume(reserveA != reserveB); // Avoid edge case

// Test swap logic

}

2. vm.store — Manipulate Storage Directly

Need to set up complex state without going through external calls? Use vm.store:

function testLiquidation() public {

// Manually set user balance in storage slot 3

bytes32 slot = keccak256(abi.encode(address(user), 3));

vm.store(address(token), slot, bytes32(uint256(1000e18)));

assertEq(token.balanceOf(user), 1000e18);

}

This is useful for:

  • Setting balances without minting
  • Manipulating internal state for edge case tests
  • Bypassing access control in test setup

3. vm.mockCall — Mock External Calls

Want to test behavior when an external call returns a specific value? Use vm.mockCall:

function testOracleFailure() public {

// Mock oracle to return stale price

vm.mockCall(

address(oracle),

abi.encodeWithSelector(IOracle.getPrice.selector),

abi.encode(0) // Return 0

);

vm.expectRevert("Stale price");

vault.liquidate(user);

}

This lets you simulate external failures without deploying mock contracts.

Advanced: Conditional Mocks

function testConditionalMock() public {

// Mock returns different values based on input

vm.mockCall(

address(oracle),

abi.encodeWithSelector(IOracle.getPrice.selector, tokenA),

abi.encode(1000e18)

);

vm.mockCall(

address(oracle),

abi.encodeWithSelector(IOracle.getPrice.selector, tokenB),

abi.encode(2000e18)

);

assertEq(oracle.getPrice(tokenA), 1000e18);

assertEq(oracle.getPrice(tokenB), 2000e18);

}

4. vm.expectEmit — Verify Event Data

Most people use vm.expectEmit to check that an event was emitted. But you can also verify event data:

function testTransfer() public {

vm.expectEmit(true, true, false, true);

emit Transfer(alice, bob, 100); // Expected event

token.transfer(bob, 100); // Should emit matching event

}

The four booleans control what to check:

  • Check topic 1 (indexed param 1)
  • Check topic 2 (indexed param 2)
  • Check topic 3 (indexed param 3)
  • Check data (non-indexed params)
  • Advanced: Multiple Events

    function testMultipleEvents() public {
    

    vm.expectEmit(true, true, false, true);

    emit Approval(alice, bob, 100);

    vm.expectEmit(true, true, false, true);

    emit Transfer(alice, bob, 100);

    token.transferFrom(alice, bob, 100);

    }

    5. vm.recordLogs — Capture Events for Inspection

    Need to inspect event data programmatically? Use vm.recordLogs:

    function testEventData() public {
    

    vm.recordLogs();

    vault.deposit(100);

    Vm.Log[] memory logs = vm.getRecordedLogs();

    assertEq(logs.length, 1);

    assertEq(logs[0].topics[0], keccak256("Deposit(address,uint256)"));

    }

    This is useful for testing event-driven systems like indexers.

    6. vm.snapshot and vm.revertTo — Save and Restore State

    Testing multiple scenarios from the same starting state? Use snapshots:

    function testScenarios() public {
    

    // Setup

    vault.deposit(1000);

    uint256 snapshot = vm.snapshot();

    // Scenario 1: withdraw succeeds

    vault.withdraw(500);

    assertEq(vault.balanceOf(address(this)), 500);

    vm.revertTo(snapshot);

    // Scenario 2: withdraw fails

    vm.expectRevert("Insufficient balance");

    vault.withdraw(2000);

    }

    This is much faster than re-running setup code.

    7. vm.warp and vm.roll — Time Travel

    Need to test time-dependent logic? Use vm.warp and vm.roll:

    function testVesting() public {
    

    vault.startVesting(1000, 365 days);

    // Fast forward 180 days

    vm.warp(block.timestamp + 180 days);

    uint256 vested = vault.vestedAmount();

    assertApproxEqRel(vested, 500, 0.01e18); // ~500, 1% tolerance

    }

    • vm.warp(timestamp) — set block.timestamp
    • vm.roll(blockNumber) — set block.number

    8. vm.chainId — Test Multi-Chain Logic

    Testing cross-chain signatures or chain-specific logic? Use vm.chainId:

    function testChainIdValidation() public {
    

    vm.chainId(1); // Mainnet

    assertTrue(bridge.isChainSupported());

    vm.chainId(999); // Unsupported chain

    assertFalse(bridge.isChainSupported());

    }

    9. vm.sign and vm.signCompact — Generate Signatures

    Need to test EIP-712 signatures? Use vm.sign:

    function testPermit() public {
    

    uint256 privateKey = 0x1234;

    address signer = vm.addr(privateKey);

    bytes32 digest = keccak256(abi.encodePacked(

    "\x19\x01",

    token.DOMAIN_SEPARATOR(),

    keccak256(abi.encode(

    token.PERMIT_TYPEHASH(),

    signer,

    spender,

    amount,

    nonce,

    deadline

    ))

    ));

    (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);

    token.permit(signer, spender, amount, deadline, v, r, s);

    }

    For compact signatures (EIP-2098), use vm.signCompact.

    10. vm.createSelectFork — Fork Multiple Chains

    Testing cross-chain interactions? Fork multiple chains in the same test:

    function testCrossChain() public {
    

    // Fork Ethereum mainnet

    uint256 ethFork = vm.createFork("https://eth-mainnet.g.alchemy.com/v2/...");

    // Fork Arbitrum

    uint256 arbFork = vm.createFork("https://arb-mainnet.g.alchemy.com/v2/...");

    // Switch to Ethereum

    vm.selectFork(ethFork);

    uint256 ethBalance = USDC.balanceOf(user);

    // Switch to Arbitrum

    vm.selectFork(arbFork);

    uint256 arbBalance = USDC.balanceOf(user);

    assertGt(ethBalance, arbBalance);

    }

    This is incredibly useful for testing bridges and cross-chain protocols.

    Summary

    These 10 cheatcodes unlock advanced testing patterns:

  • vm.assume — constrain fuzz inputs
  • vm.store — manipulate storage
  • vm.mockCall — mock external calls
  • vm.expectEmit — verify event data
  • vm.recordLogs — capture events
  • vm.snapshot/revertTo — save and restore state
  • vm.warp/roll — time travel
  • vm.chainId — test multi-chain logic
  • vm.sign — generate signatures
  • vm.createSelectFork — fork multiple chains
  • Master these, and your test suite will catch bugs that others miss.

    Full cheatcode reference: book.getfoundry.sh/cheatcodes

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement