Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. Foundry is consistently 1.5x-11x faster than Hardhat.
TESTING SPEED
Forge, Foundry’s testing framework, is the fastest EVM test runner that exists.
– 23-335x faster than Dapptools – the more tests + fuzzing, the bigger the speedup
– >16x faster than Hardhat (rewrote v3-periphery’s tests in Solidity) pic.twitter.com/xTU1j2zvo6— Georgios Konstantopoulos (@gakonst)
Installation
Using foundryup
The easiest way to get Foundry is to install the latest release by using foundryup
.
On Linux and macOS systems, this is done as follows:
curl -L https://foundry.paradigm.xyz | bash
This will download foundryup
. To start install Foundry, run:
foundryup
If everything goes well, you will now have two binaries at your disposal: forge
and cast
.
On Windows, build from source.
Running foundryup
again will update to the latest Foundry release. You can also revert to a specific version of Foundry with foundryup -v $VERSION
.
Building from source
To build from source, you need to get Rust and Cargo. The easiest way to get both is by using rustup
.
On Linux and macOS systems, this is done as follows:
curl https://sh.rustup.rs -sSf | sh
It will download a script and start installation.
On Windows, download and run rustup-init
from rustup.rs. It will start the installation in a console.
After this, run the following to build Foundry from source:
cargo install --git https://github.com/gakonst/foundry --bins --locked
Using with Docker
Foundry can also be used entirely within a Docker container. If you don’t have it, Docker can be installed directly from Docker’s website
Once installed, you can download the latest release by running:
docker pull ghcr.io/gakonst/foundry:latest
It is also possible to build the docker image locally. From the Foundry repository, run:
docker build -t foundry .
ℹ️ Note
Some machines (including those with M1 chips) may be unable to build the docker image locally. This is a known issue.
Creating a new project
To start a new project with Foundry, use forge init
:
{{#include ../output/hello_foundry/forge-init:command}}
This creates a new directory hello_foundry
from the default template. This also initializes a new git
repository.
If you want to create a new project using a different template, you would pass the --template
flag, like so:
$ forge init --template https://github.com/FrankieIsLost/forge-template hello_template
For now, let’s check what the default template looks like:
$ cd hello_foundry
{{#include ../output/hello_foundry/tree:all}}
The default template comes with one dependency installed: ds-test
. This is the preferred assertion library used for Foundry projects. Additionally, the template also comes with an empty starter contract and a simple test.
Let’s build the project:
{{#include ../output/hello_foundry/forge-build:all}}
And run the tests:
{{#include ../output/hello_foundry/forge-test:all}}
You’ll notice that two new directories have popped up: out
and cache
.
The out
directory contains your contract artifact, such as the ABI, while the cache
is used by forge
to only recompile what is necessary.
Project Layout
Forge is flexible on how you structure your project. By default, the structure is:
{{#include ../output/hello_foundry/tree-with-files:output}}
- You can configure Foundry’s behavior using
foundry.toml
. - Remappings are specified in
remappings.txt
. - The default directory for contracts is
src/
. - The default directory for tests is
src/test/
, where any contract with a function that starts withtest
is considered to be a test. - Dependencies are stored as git submodules in
lib/
.
You can configure where Forge looks for both dependencies and contracts using the --lib-paths
and --contracts
flags respectively. Alternatively you can configure it in foundry.toml
.
Combined with remappings, this gives you the flexibility needed to support the project structure of other toolchains such as Hardhat and Truffle.
For automatic Hardhat support you can also pass the --hh
flag, which sets the following flags: --lib-paths node_modules --contracts contracts
.
Writing Tests
Forge can run your tests with the forge test
command. All tests are written in Solidity.
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 src/test
by convention and end with .t.sol
.
Here’s an example of running forge test
in a freshly created project, that only has the default test:
{{#include ../output/hello_foundry/forge-test:all}}
You can also run specific tests by passing a filter:
{{#include ../output/test_filters/forge-test-match-contract-and-test:all}}
This will run the tests in the ComplicatedContractTest
test contract with testDeposit
in the name. Inverse versions of these flags also exist (--no-match-contract
and --no-match-test
).
You can run tests in filenames that match a regex with --match-path
.
{{#include ../output/test_filters/forge-test-match-path:all}}
The inverse of the --match-path
flag is --no-match-path
.
Logs and traces
The default behavior for forge test
is to only display a summary of passing and failing tests. You can control this behavior by increasing the verbosity (using the -v
flag). Each level of verbosity adds more information:
- Level 2 (
-vv
): Logs emitted during tests are also displayed. - 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.
Framework | Remote RPC | Local RPC | Cached |
---|---|---|---|
Blocknative | N/A | N/A | 0m3.529s |
Dapptools | 52m17.447s | 17m34.869s | 3m25.896s |
Ganache | 10m5.384s | 1m2.275s | 0m22.662s |
Hardhat | 8m26.483s | 0m35.145s | 0m7.531s |
Foundry | 6m59.875s | 0m13.610s | 0m0.537s |
Tenderly | N/A | N/A | 0m1.9315s |
Cast tool
cast
is Foundry’s command-line tool for performing Ethereum RPC calls. You can make smart contract calls, send transactions, or retrieve any type of chain data – all from your command-line!
How to use cast
To use cast
, use the cast
keyword followed by a subcommand:
$ cast <subcommand>
Examples
Let’s use cast
to retrieve the total supply of the DAI token:
{{#include ../output/cast/cast-call:all}}
cast
also provides many convenient subcommands, such as for decoding calldata:
{{#include ../output/cast/cast-4byte-decode:all}}
Conclusion
Foundry is an exceptionally fast developer toolkit for EVM based blockchains. You should definitely use it to fastrack your development process.
Leave a Reply