✅Introduction
Setup
Install foundry toolchain:
curl -L https://foundry.paradigm.xyz | bash
foundryup
If everything goes well, you will now have four binaries at your disposal: forge
, cast
, anvil
, and chisel
.
First Steps with Foundry
Start a new project:
forge init <project_name>
Suppose we named the project "hello_foundry", then the project layout is:
$ cd hello_foundry
$ tree . -d -L 1
.
├── lib
├── script
├── src
└── test
4 directories
Build the prject:
forge build
Test the project:
forge test
Forge Cheatsheet
Dependency
Install dependency, such as solmate:
forge install transmissions11/solmate
The dependency is going to be installed in the /lib
directory.
Forge can remap dependencies to make them easier to import. Forge will automatically try to deduce some remappings for you:
$ forge remappings
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
solmate/=lib/solmate/src/
weird-erc20/=lib/weird-erc20/src/
These remappings mean:
To import from
forge-std
we would write:import "forge-std/Contract.sol";
To import from
ds-test
we would write:import "ds-test/Contract.sol";
To import from
solmate
we would write:import "solmate/Contract.sol";
To import from
weird-erc20
we would write:import "weird-erc20/Contract.sol";
You can customize these remappings by creating a remappings.txt
file in the root of your project.
Update dependency:
forge update lib/solmate
Remove dependency:
forge remove solmate
Tests
Forge will look for the tests anywhere in your source directory. Any contract with a function that starts with test
is considered to be a test. Usually, tests will be placed in test/
by convention and end with .t.sol
.
Run all tests:
forge test
Run a specific test:
forge test --match-contract ComplicatedContractTest --match-test testDeposit
Inverse versions of these flags also exist (--no-match-contract
and --no-match-test
).
Match a glob pattern:
forge test --match-path test/ContractB.t.sol
The inverse of the --match-path
flag is --no-match-path
.
Verbosity:
Level 2 (
-vv
): Logs emitted during tests are also displayed. That includes assertion errors from tests, showing information such as expected vs actual.Level 3 (
-vvv
): Stack traces for failing tests are also displayed.Level 4 (
-vvvv
): Stack traces for all tests are displayed, and setup traces for failing tests are displayed.Level 5 (
-vvvvv
): Stack traces and setup traces are always displayed.
Hello World
Copy the contract from solidity-by-example.org:
// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.17 and less than 0.9.0
pragma solidity ^0.8.17;
contract HelloWorld {
string public greet = "Hello World!";
}
Compile it:
forge build
Create a test file HelloWorld.t.sol
. Copy and paste the content of Counter.t.sol
into this new test file and write our own test cases:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/HelloWorld.sol";
contract HelloWorldTest is Test {
HelloWorld public helloWorld;
function setUp() public {
helloWolrd = new HelloWorld();
}
function testGreet() public {
assertEq(helloWorld.greet(), "Hello World!");
}
}
Note that setup()
will be executed before executing each test case, and test case function name must start with the test
prefix.
Run test:
forge test

Note that forge test
would run all test files in the test/
directory. If you only want to run a single test file:
forge test --match-path test/HelloWorld.t.sol

Let's make the test fail:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/HelloWorld.sol";
contract HelloWorldTest is Test {
HelloWorld public helloWorld;
function setUp() public {
helloWorld = new HelloWorld();
}
function testGreet() public {
assertEq(helloWorld.greet(), "Hello World?");
}
}
Run test with verbosity marks:
forge test --match-path test/HelloWorld.t.sol -vvv

You can see why the test case failed in the Traces
output.
Last updated
Was this helpful?