Skip to content

Repository files navigation

Ripfuzz Standard Library

Standard library for writing ripfuzz harnesses.

Please refer to the list of currently available cheatcodes. More cheatcodes will be added as ripfuzz grows support.

Installation

To install using Foundry:

forge install pyk/ripfuzz-std

Alternatively, you can directly add it as a submodule:

git submodule add https://github.com/pyk/ripfuzz-std

Example

End-to-end smoke harness: examples/CounterHarness.sol.

import {Harness} from "ripfuzz/Harness.sol";

contract Counter {
    uint256 public count;
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function increment() external {
        require(msg.sender == owner, "not owner");
        count += 1;
    }

    function add(uint256 x) external {
        require(msg.sender == owner, "not owner");
        count += x;
    }
}

contract CounterHarness is Harness {
    Counter counter;

    function setup() external {
        addActor("user");
        address user = getActor(0);
        rvm.deal(user, 100 ether);

        rvm.prank(user);
        counter = new Counter();
    }

    function increment(uint256 actorId) external useActor(actorId) {
        counter.increment();
    }

    function add(uint256 actorId, uint256 x) external useActor(actorId) {
        x = bound(x, 1, 100);
        counter.add(x);
    }

    function invariant_OwnerIsUser() external {
        eq(counter.owner(), getActor(0), "owner is user");
    }

    function invariant_CountStaysBounded() external {
        ensure(counter.count() < 1_000_000, "count stayed below 1_000_000");
    }
}

Run with:

ripfuzz run CounterHarness

Usage

Inherit Harness to get cheatcodes, helpers, and assertions:

import {Harness} from "ripfuzz/Harness.sol";

contract MyHarness is Harness {
    // ...
}

Each feature has a runnable example under examples/.

Cheatcodes

Example: examples/CheatcodesHarness.sol

Access ripfuzz cheatcodes through rvm. Full list: src/RVM.sol.

rvm.deal(user, 100 ether);
rvm.prank(user);
rvm.warp(block.timestamp + 1 days);

Environment variables

Example: examples/GetEnvHarness.sol

Read process environment variables, including values from a project .env file:

// Required: reverts when the key is missing.
string memory rpcUrl = rvm.getEnv("ETH_RPC_URL");

// Optional: returns defaultValue when the key is missing.
string memory network = rvm.getEnv("NETWORK", "local");

Addresses

Example: examples/AddressHarness.sol

Create labeled addresses derived from a name:

address user = createAddress("user");
(address signer, uint256 privateKey) = createAddressAndKey("signer");

Actors

Example: examples/ActorsHarness.sol

Manage a pool of fuzz actors and prank as one per handler call:

function setup() external {
    addActor("Alice");
    addActor("Bob");
}

function act(uint256 actorId, uint256 x) external useActor(actorId) {
    // msg.sender == currentActor for this call
    target.doThing(x);
}
Helper Purpose
getActor(actorId) Actor at index, wrapped with modulo
actorCount() Pool size
getActorPrivateKey(actor) Key for actors created with addActor(string)
removeActor(actor) Remove from the pool
currentActor Actor selected by the active useActor

Bound

Example: examples/BoundHarness.sol

Clamp fuzz inputs into an inclusive range:

x = bound(x, 1, 100);

Logging

Example: examples/LoggingHarness.sol

Emit structured logs from handlers and assertions:

log("deposit");
log("amount", amount);
log("user", user);

Assertions

Example: examples/AssertionsHarness.sol

Fail the campaign with a logged message:

ensure(ok, "operation succeeded");
eq(a, b, "balances match");
neq(a, b, "ids differ");
unreachable("should never reach this path");

Tokens

Example: examples/TokensHarness.sol

Optional ERC20 helpers for non-standard tokens:

import {IERC20} from "ripfuzz/interfaces/IERC20.sol";
import {SafeERC20} from "ripfuzz/libraries/SafeERC20.sol";

using SafeERC20 for IERC20;

token.safeApprove(spender, amount);
token.safeTransfer(to, amount);

Fork

Example: examples/ForkHarness.sol

Load the RPC URL from the environment (including a project .env file), then fork and interact with a live contract such as USDC:

// .env
// ETH_RPC_URL=https://eth.meowrpc.com

string memory rpcUrl = rvm.getEnv("ETH_RPC_URL", "https://eth.meowrpc.com");
rvm.fork(rpcUrl, 25_708_159);

IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
rvm.label(address(usdc), "USDC");

// Fund an actor by writing the FiatToken balances mapping (slot 9).
bytes32 slot = keccak256(abi.encode(alice, uint256(9)));
rvm.store(address(usdc), slot, bytes32(uint256(1_000_000e6)));

usdc.safeTransfer(bob, amount);

Multi-fork

Example: examples/MultiForkHarness.sol

Load per-chain RPC URLs once in setup, then use modifiers to select forks:

// .env
// ETH_RPC_URL=https://eth.meowrpc.com
// BASE_RPC_URL=https://base.meowrpc.com

string internal ethRpcUrl;
string internal baseRpcUrl;

function setup() external {
    ethRpcUrl = rvm.getEnv("ETH_RPC_URL", "https://eth.meowrpc.com");
    baseRpcUrl = rvm.getEnv("BASE_RPC_URL", "https://base.meowrpc.com");
    // ...
}

modifier onEthereum() {
    rvm.fork(ethRpcUrl, 25_708_159);
    _;
}

modifier onBase() {
    rvm.fork(baseRpcUrl, 49_688_843);
    _;
}

function transferEthUsdc(uint256 actorId, uint256 amount)
    external
    onEthereum
    useActor(actorId)
{
    ethUsdc.safeTransfer(getActor(actorId + 1), amount);
}

License

MIT

About

Ripfuzz Standard Library

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages