Library / SDK
foundry-rs/forge-std avatar
foundry-rs/forge-std

forge-std: the helper layer that makes Foundry cheatcodes usable

A collection of helpful contracts and libraries for use with Forge and Foundry

1,062 stars533 forksSolidityApache-2.0

At a glance

What is it?
forge-std is the standard library for Foundry's Forge, wrapping the raw cheatcodes in contracts that are pleasant to use, with stdError for revert assertions, stdStorage for locating storage slots without a layout, and stdCheats for pranking, funding addresses and manipulating time. It suits anyone writing Solidity tests with Forge, and it inherits the constraints of the cheatcode set it wraps.
Who is it for?
Adopt forge-std if you write Solidity tests with Forge, because stdStorage in particular removes the guesswork from finding a storage slot and stdCheats gives the pranking and funding helpers a consistent interface. Do not adopt it as a general Solidity library, since it is a test-only dependency that depends on the cheatcode set of the Foundry version you build against.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Solidity, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library is, and what it wraps

The Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry. It leverages Forge's cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes.

That last clause is the definition of the project. The cheatcodes are the primitives that let a Solidity test do things a test should not be able to do, record what the EVM did, change who the next call comes from, move the block timestamp. The std contracts are the ergonomics on top of them, and the point of the library is to take the raw calls and turn them into something you can write a test with.

The scale of the thing is small and deliberate. The package manifest lists the published files as src/**/* and nothing else, the licence is Apache-2.0 or MIT, and the version in the manifest is 1.17.0, matching the latest tagged release v1.17.0 from 2026-09-30. The previous releases are v1.16.2 from 2026-06-30 and v1.16.1 from 2026-04-29, so the library moves at the pace of the toolchain it supports.

The repository layout is conventional for the ecosystem: a src directory with the contracts, a test directory, a scripts directory, a foundry.toml, a package.json for npm consumption, and a RELEASE_CHECKLIST.md that documents how a release is cut.

The documentation lives in the Foundry Book, in the Forge-Std guide, and that is where the reference for the whole library sits rather than in the README, which only introduces the main contracts.

Installing it, and the two ways it arrives

The install command is one line, and it uses Forge's own package manager.

bash
forge install foundry-rs/forge-std

That is the standard route, and it brings the library into your project in the way every Foundry project expects, as a dependency you can import by path.

The second route is npm, and the package manifest is what makes it work. The package is named forge-std, the version is 1.17.0, the description matches the project, the licence field reads Apache-2.0 OR MIT, the author is the contributors to the Forge Standard Library, and the files list restricts the published package to src/**/*.

json
  "name": "forge-std",
  "version": "1.17.0",
  "license": "(Apache-2.0 OR MIT)",
  "files": [
    "src/**/*"
  ]

The dual licence is worth noting because it means a project with either licence policy can vendor the library.

Either way, the library is a test-time dependency. It is imported in a test contract, not in the contract under test, and the imports in the examples are always the same, import "forge-std/Test.sol", with the test contract inheriting from Test.

stdError: revert assertions without hand written error types

stdError is a helper contract for errors and reverts, and the README says it is particularly helpful for the expectRevert cheatcode, because it provides all the compiler built-in errors. The contract itself carries the full list of error codes.

The use case is the one that every Solidity test eventually hits: asserting that a call reverts, and asserting that it reverts for the right reason. Without a helper, you either assert on the raw revert data, which means writing the error selector by hand, or you assert only that a revert happened, which is a much weaker test.

The example shows the shape of it.

solidity
import "forge-std/Test.sol";

contract TestContract is Test {
    ErrorsTest test;

    function setUp() public {
        test = new ErrorsTest();
    }

    function testExpectArithmetic() public {
        vm.expectRevert(stdError.arithmeticError);
        test.arithmeticError(10);
    }
}

The library contract under test is a separate contract with an arithmeticError function, and the assertion is that the call reverts with the arithmetic error the Solidity compiler itself produces for an underflow.

The value is in the precision. expectRevert with a specific error means the test fails if the code starts reverting for a different reason, which is exactly the regression you want to catch. A test that only checks that something reverted passes happily when the revert moves to a different line.

stdStorage: finding a slot without knowing the layout

stdStorage is the largest of the helpers, and the README says so, because of all the overloading needed to make the experience decent. It is a wrapper around the record and accesses cheatcodes, and it can always find and write the storage slots associated with a particular variable without knowing the storage layout.

The mechanism is worth understanding, because it is clever and it has a cost. It works by recording all the SLOADs and SSTOREs during a function call. If there is a single slot read or written, it returns that slot immediately. Otherwise, behind the scenes, it iterates through and checks each one, which is where the depth parameter comes in, and for a struct the depth is the field depth.

solidity
struct T {
    // depth 0
    uint256 a;
    // depth 1
    uint256 b;
}

The API is a fluent chain, and the examples show the operations: find to locate the slot, checked_write to write to it, with_key for mappings, depth for struct fields, and sig to select the variable by function selector or by name.

solidity
uint256 slot = stdstore.target(address(test)).sig("exists()").find();
stdstore.target(address(test)).sig("exists()").checked_write(100);

Two things stand out. The library works for storage locations that are essentially impossible to guess, because the example with a hidden variable uses a slot derived from a keccak hash of a string, and the comment says iterating through slots would not find it while this mechanism does. And the mapping case needs the keys, because the slot of a mapping element is derived from the key.

The limitation is stated plainly as well. By default, writing to packed storage variables is not supported and will throw an error, and packed slot support can be enabled by calling enable_packed_slots() before using find() or checked_write().

stdCheats: pranking and funding, with a safety rule

stdCheats is a wrapper around the miscellaneous cheatcodes that need wrappers to be more developer friendly. The README lists the scope: functions for pranking, dealing with ETH and tokens, deploying contracts, creating test addresses, time manipulation and fuzzing helpers.

The interesting part is the distinction between three functions that all look like they do the same thing.

Users may expect ETH to be put into an address with prank, but that is not the case, for safety reasons. If an address already has ETH, you should just use prank. If you want to change that balance explicitly, use deal. And if you want to do both, hoax is the right one.

solidity
    function testHoax() public {
        // we call `hoax`, which gives the target address
        // eth and then calls `prank`
        hoax(address(1337));
        test.bar{value: 100}(address(1337));

The safety reasoning is about test readability rather than security. A prank that silently also funds the address hides one effect inside another, so a test that fails may be failing because of a balance change you did not mean to make. Splitting the three operations makes each one visible in the test body.

The same applies to the rest of the surface. A helper for creating test addresses and a helper for manipulating time are conveniences that keep the cheatcode calls out of your test logic, which is the general pattern the whole library follows.

The alternative: calling cheatcodes directly

There is a real alternative to this library, and it is the raw cheatcode calls.

You can write a Foundry test with no std contracts at all. The vm cheatcode is available to any test contract, so you can call vm.prank, vm.deal, vm.expectRevert and vm.record yourself, and for simple cases that is less code than importing and inheriting from Test.

The difference in approach is in what you have to write. The raw route gives you the primitives with no argument validation and no conventions, so every test decides for itself what prank plus funding means, what a record and access sequence looks like, and what happens when a storage slot is packed. The std route encodes the decisions once, which is why stdStorage refuses packed writes unless you opt in, and why prank does not fund an address.

The cost of the std route is a dependency. The library is versioned against the toolchain, it inherits the cheatcode set of the Foundry release you build against, and it makes a couple of decisions for you that you may not want, since the whole library is built around the assumption that a test should be explicit about what it fakes.

For a project that already uses forge-std, the choice is made for you. For a new project, the deciding factor is how much of the surface you actually use, since a project that only needs vm.expectRevert does not need the rest of it.

Editorial conclusion

Adopt forge-std if you write Solidity tests with Forge, because stdStorage in particular removes the guesswork from finding a storage slot and stdCheats gives the pranking and funding helpers a consistent interface. Do not adopt it as a general Solidity library, since it is a test-only dependency that depends on the cheatcode set of the Foundry version you build against. Before you install it, pin the version that matches your Foundry release, because the library is released alongside the toolchain and its API moves with it.

Frequently asked questions

How do I install forge-std?

The README gives forge install foundry-rs/forge-std, which uses Forge's own package manager. The library is also published to npm under the name forge-std, with the package restricted to src/**/* and licensed Apache-2.0 OR MIT.

What does stdStorage do?

It is a wrapper around the record and accesses cheatcodes that can always find and write the storage slots associated with a variable without knowing the storage layout. It works by recording all SLOADs and SSTOREs during a function call, and it supports a depth parameter for struct fields. Writing to packed variables is not supported by default and needs enable_packed_slots() first.

What is the difference between prank, deal and hoax?

prank does not put ETH into the address, for safety reasons, so use it when the address already has ETH. deal changes a balance explicitly. hoax does both, giving the address ETH and then calling prank.

How do I assert that a call reverts with a specific compiler error?

Use the expectRevert cheatcode with a member of stdError, which provides all the compiler built-in errors. For example, vm.expectRevert(stdError.arithmeticError) before the call under test.

Official sources

  1. foundry-rs/forge-std on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/foundry-rs-forge-std.svg)](https://hysenlabs.com/projects/foundry-rs-forge-std)

Was this useful?