Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Foundry Fundamentals: Fast, Native Testing & Deployment

Hello! Welcome to your next lesson in Module 2: "Modern Development & Testing Toolchain."

Introduction

In our last lesson, we set up a professional development environment using Hardhat, leveraging your expertise with TypeScript to create a type-safe workflow for compiling and testing smart contracts. This approach, where tests are written in a separate language like TypeScript or JavaScript, is powerful and very common in the industry.

Today, we're going to explore a different, increasingly popular philosophy with a tool called Foundry. Foundry is a blazing-fast, portable, and modular toolkit for Ethereum application development, written in Rust. Its main differentiator is that it allows you to write your tests directly in Solidity.

This lesson will guide you through the entire Foundry workflow, from initial setup to testing and deployment.

Learning Outcome: By the end of this lesson, you will be able to configure Foundry for fast, Solidity-native testing and deployment workflows.

1. What is Foundry and Why Use It?

While Hardhat integrates the JavaScript/TypeScript ecosystem into smart contract development, Foundry champions the idea of staying within the Solidity ecosystem as much as possible. This can lead to a more streamlined experience, as you don't need to context-switch between languages.

Foundry consists of a few core command-line tools:

  • forge: Used to compile, test, fuzz, and deploy your smart contracts.
  • cast: A tool for making EVM calls, sending transactions, and retrieving chain data from the command line.
  • anvil: A local testnet node, similar to Hardhat Network.
  • chisel: A Solidity REPL (Read-Eval-Print Loop).

The following article from Paradigm, the creators of Foundry, provides a great high-level overview of its core principles.

Introducing the Foundry Ethereum development toolbox

This article, 'Introducing the Foundry Ethereum development toolbox', explains the motivation behind Foundry. It argues for the benefits of writing tests in Solidity, which is the core philosophy we'll be exploring today.

Please read the sections 'Why Foundry?' and 'You should be writing your tests in Solidity'. This will provide context on why a tool like Foundry was created and how it contrasts with the JavaScript-based testing you saw with Hardhat.

2. Installation and Project Setup

Getting started with Foundry is straightforward. It uses a version manager called foundryup for installation, similar to rustup for Rust or nvm for Node.js.

The following video from Patrick Collins provides a clear walkthrough of the installation and project initialization process.

Intro to Foundry | The FASTEST Smart Contract Framework

This clip from 'Intro to Foundry' will guide you through installing Foundry and creating your first project.

Watch from the beginning until 03:26. This segment covers: Installing Foundry using foundryup. Initializing a new project with forge init. A tour of the default project structure (src, lib, test, foundry.toml).

Action: Follow these steps to set up your project:

  1. Install Foundry: Open your terminal and run the installation command shown in the video (and on the Foundry GitHub page).

    curl -L https://foundry.paradigm.xyz | bash
    

    Then, run foundryup to install the latest version. After this, you'll need to restart your terminal or source your profile (source ~/.bashrc, source ~/.zshrc, etc.) for the forge command to be available.

  2. Initialize a Project:

    mkdir my-foundry-project
    cd my-foundry-project
    forge init
    

You now have a basic Foundry project. The structure is simple:

  • src/: Contains your contract source code (e.g., Counter.sol).
  • test/: Contains your Solidity test files (e.g., Counter.t.sol).
  • lib/: Contains dependencies, managed as Git submodules.
  • foundry.toml: The project configuration file, analogous to Hardhat's hardhat.config.ts.
  • .gitmodules: Tracks the Git submodules for your dependencies in lib/.

3. Dependencies and Compilation

Just like using npm install in a Node.js project, you'll need to manage external dependencies like the OpenZeppelin contracts. Foundry handles this by pulling repositories directly from GitHub.

A crucial part of the configuration is setting up remappings. This tells the Solidity compiler how to resolve import paths. For example, you can remap @openzeppelin/ to lib/openzeppelin-contracts/contracts/.

This next clip demonstrates this process clearly.

Intro to Foundry | The FASTEST Smart Contract Framework

Let's continue with the Patrick Collins video to see how to manage dependencies and configure remappings.

Watch from 04:15 to 07:54. This part covers: Using forge install to add the OpenZeppelin contracts. Encountering a compilation error because the import path isn't resolved. Adding a remapping to foundry.toml to fix the issue. Using forge build to compile the contracts.

To summarize the workflow:

  1. Install: forge install <github-user>/<repo-name>
  2. Remap: Open foundry.toml and add the remapping:
    [profile.default]
    src = "src"
    out = "out"
    libs = ["lib"]
    remappings = ["@openzeppelin/=lib/openzeppelin-contracts/"]
    
  3. Compile: forge build

4. The Testing Workflow

This is where Foundry's philosophy truly shines. You write your tests in Solidity, in files ending with .t.sol.

Writing a Basic Test

A test contract typically inherits from Test, which is part of Foundry's standard library (forge-std). This gives you access to assertion functions (like assertEq, assertTrue) and cheat codes.

  • The setUp() function is executed before each test function, similar to a beforeEach hook in Mocha or Jest. It's used to deploy contracts and set up the initial state for each test.
  • Test functions are prefixed with test.

Intro to Foundry | The FASTEST Smart Contract Framework

This segment shows how to write and run a basic test in Solidity.

Watch from 07:54 to 11:20. Focus on: The structure of the test file (StakeContract.t.sol). Importing the contract-under-test and the Test contract. Using the setUp() function to deploy contracts. Writing a simple test function that calls a contract method and uses assert. Running the test with forge test.

Powerful Testing Features: Fuzzing and Cheat Codes

Foundry comes with powerful features out-of-the-box that require extra setup in other frameworks.

  1. Fuzz Testing: Instead of writing dozens of tests with different inputs, you can write one "property-based" test. By adding parameters to your test function, Foundry will automatically run it hundreds of times with random inputs to try and find edge cases that cause it to fail.

  2. Cheat Codes: These are special functions accessible via a vm instance that allow you to manipulate the blockchain's state within a test. You can change msg.sender (vm.prank), modify block timestamps (vm.warp), or even directly write to a contract's storage slots.

This next clip provides a quick introduction to both concepts.

Intro to Foundry | The FASTEST Smart Contract Framework

Let's look at how fuzzing and cheat codes work in practice.

Watch from 11:20 to 14:42. This section introduces: Fuzzing (11:20): See how adding a uint256 amount parameter to the test function turns it into a fuzzer that automatically finds a failing input. Cheat Codes (12:55): Understand the concept of cheat codes and see a simple example of using vm.roll to change the block number.

5. The Deployment Workflow

Once your contracts are tested, you need to deploy them. Foundry offers two primary methods: a simple command-line deployment and a more powerful script-based approach.

Method 1: Simple Deployment with forge create

For quick deployments, you can use the forge create command. You need to provide an RPC URL for the target network and a private key for the deploying account.

forge create --rpc-url <YOUR_RPC_URL> --private-key <YOUR_PRIVATE_KEY> src/YourContract.sol:YourContract

You can also pass constructor arguments using --constructor-args. While simple, this method is not easily repeatable or version-controlled.

Method 2: Repeatable Deployments with Scripts

A more robust and professional approach is to use deployment scripts, which are also written in Solidity. These scripts live in the script/ directory and typically end in .s.sol.

This workflow allows you to define complex deployment sequences, perform on-chain actions post-deployment (like minting tokens or setting permissions), and manage it all in version control.

The following video provides an excellent, detailed walkthrough of creating and running a deployment script.

Deploy Smart Contract With Foundry

The 'Deploy Smart Contract With Foundry' video from Smart Contract Programmer will guide us through the modern, script-based deployment workflow.

This is a comprehensive walkthrough. Please watch from 00:11 to 08:05. (00:11) Setting up a .env file to securely manage your private key and RPC URL. (00:39) Creating the deployment script file (.s.sol) and inheriting from Script. (03:18) Using vm.startBroadcast() and vm.stopBroadcast() to wrap your deployment transactions. This is the core of the script. (06:49) Running the script with forge script, first in simulation mode and then for real with the --broadcast flag. The --verify flag for Etherscan verification is also shown.

This script-based method is the recommended workflow for any serious project, as it makes your deployments deterministic, repeatable, and easy for others to audit and execute.

Conclusion

You have now configured a complete Foundry project and walked through its primary workflows for testing and deployment. You've seen how it differs from Hardhat by keeping you entirely within the Solidity ecosystem.

Key Takeaways:

  • Foundry is a fast, Rust-based toolkit centered around Solidity-native development.
  • forge init scaffolds a project, and foundry.toml is used for configuration, including dependency remappings.
  • Dependencies are managed with forge install, which adds them as Git submodules.
  • Tests are written in Solidity (.t.sol files), using the setUp() function for initial state and test functions for assertions.
  • Foundry includes powerful built-in features like fuzz testing and cheat codes (vm).
  • Deployment is best handled with repeatable Solidity scripts (.s.sol) and executed with the forge script command, using flags like --broadcast and --verify.

Next Steps:

You have now set up and used the two dominant smart contract development environments: Hardhat (with TypeScript) and Foundry (with Solidity). In the next lesson, we will dedicate our time to a direct comparison of their testing philosophies and workflows. This will help you understand the trade-offs and decide which tool, or combination of tools, you prefer for your own projects.

Can't find a good explanation? Sign up and we'll make it for you

Sign up