# Building and testing smart contracts with Foundry

By [Nader Dabit](https://paragraph.com/@nader-dabit) · 2021-12-20

---

One of the things I really like about [Paradigm](https://www.paradigm.xyz/) is that they seem to be very focused on helping builders and developers, and are not afraid to get their hands dirty with code (people like [Anish Agnihotri](https://github.com/Anish-Agnihotri?tab=repositories) and [Georgios Konstantopoulos](https://github.com/gakonst?tab=repositories) are some of the best engineers in web3 or maybe anywhere). They also [share](https://www.paradigm.xyz/writing/) an enormous amount of some of the highest quality blockchain / web3 / crypto related content in existence. They definitely don't seem like the the typical VC firm.

They [recently created](https://www.paradigm.xyz/2021/12/introducing-the-foundry-ethereum-development-toolbox/) and open sourced [Foundry](https://github.com/gakonst/foundry), a new Solidity development environment.

Since it came out I've been wanting to try it out, and finally had the chance to this week.

In this post I want to give you a quick rundown of what I learned and how to get started with it.

Foundry Overview
----------------

Paradigm's description of Foundry is that **Foundry is a portable, fast and modular toolkit for Ethereum application development.**

It fits into the stack the same way that [Hardhat](https://hardhat.org/), [Truffle](https://trufflesuite.com/), and [Dapp Tools](https://dapp.tools/) do.

The main differences / selling points of Foundry are:

**1.** It allows you to write your tests and scripts in Solidity instead of JavaScript.

They make [a great case](https://www.paradigm.xyz/2021/12/introducing-the-foundry-ethereum-development-toolbox/#You_should_be_writing_your_tests_in_Solidity) about why writing tests in Solidity VS JavaScript is better, and they are spot on with most of their points.

There is just a lot less boilerplate and a lot less mental overhead. Once you write a few tests in Solidity you feel the difference.

**2.** It's fast.

Foundry is written in Rust and it is fast. They've documented a few [benchmarks here](https://www.paradigm.xyz/2021/12/introducing-the-foundry-ethereum-development-toolbox/#You_should_be_writing_your_tests_in_Solidity), but it's hard to do it justice until you use it (especially after using an alternative).

Foundry is made up of 3 CLI tools - [forge](https://github.com/foundry-rs/foundry/tree/master/forge), [cast](https://github.com/foundry-rs/foundry/tree/master/cast), and [anvil](https://github.com/foundry-rs/foundry#installation).

**Forge** is the Ethereum development and testing framework.

**Cast** is a CLI that allows you to interact with EVM smart contracts, send transactions, and read data from the network.

**Anvil** is a local Ethereum node, similar to Ganache or Hardhat node.

Building & testing a smart contract with Foundry
------------------------------------------------

> To install Foundry, you must first have Rust installed on your machine.

To get started, we'll [install Foundry](https://github.com/foundry-rs/foundry#installation):

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

Next, in an empty directory, we can use the `init` command to initialize a new project:

    forge init
    

The forge CLI will create a few files and folers, including **lib**, **src**, **script**, and **test**.

The **lib** directory contains [forge-std](https://github.com/foundry-rs/forge-std), a collection of helpful contracts for use with forge and foundry.

The **src** directory contains a barebones smart contract.

The **test** directory contains an example test.

The **script** directory contains an example script.

Let's create a basic smart contract to test out. Rename **Contract.sol** to **HelloWorld.sol** and update it with the following:

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.13;
    
    contract HelloWorld {
      string private greeting;
      uint public version = 0;
      
      constructor (string memory _greeting) {
        greeting = _greeting;
      }
    
      function greet() public view returns(string memory) {
        return greeting;
      }
    
      function updateGreeting(string memory _greeting) public {
        version += 1;
        greeting = _greeting;
      }
    }
    

Next, we can run a build and compile the ABIs:

This should create an **out** directory containing the ABIs for both the main contract as well as the test.

Next, let's update the name of **test/Contract.t.sol** to **test/HelloWorld.t.sol** and add the following code:

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.13;
    
    import "forge-std/Test.sol";
    import 'src/HelloWorld.sol';
    
    contract HelloWorldTest is Test {
        HelloWorld hello;
        function setUp() public {
          hello = new HelloWorld("Foundry is fast!");
        }
    
        function test1() public {
            assertEq(
                hello.greet(),
                "Foundry is fast!"
            );
        }
    
        function test2() public {
            assertEq(hello.version(), 0);
            hello.updateGreeting("Hello World");
            assertEq(hello.version(), 1);
            assertEq(
                hello.greet(),
                "Hello World"
            );
        }
    }
    

Forge comes built in with some really great testing features like assertions and gas cost snapshots.

In our test we've asserted equality using the `assertEq` utility.

To run the test, we can run:

    forge test
    

When the test is run, we'll see output for not only the success of the test, but also the gas cost:

![Forge test output](https://storage.googleapis.com/papyrus_images/ada785f5d6c91f9c465c96cf609987b3178fd78fbea4b6243de3cc6f504d544a.png)

Forge test output

There are also utilities for:

**truthiness** - `assertTrue`

**decimal equality** - `assertEqDecimal`

**greater than, less than** - `assertGt`, `assertGe`, `assertLt`, `assertLe`

You can view most of the assertions [here](https://book.getfoundry.sh/reference/ds-test.html#asserting).

### Fuzzing

Foundry also supports [fuzzing](https://en.wikipedia.org/wiki/Fuzzing).

This allows us to define function parameter types and the testing framework will populate these values at runtime.

If it does find an input that causes the test to fail, it will return it so you can create a regression test.

For instance, we can update the `test2` function to receive a function argument, and use the value in our test without ever having to define what it is:

    function test2(string memory _greeting) public {
        assertEq(hello.version(), 0);
        hello.updateGreeting(_greeting);
        assertEq(hello.version(), 1);
        assertEq(
            hello.greet(),
            _greeting
        );
    }
    

Now when we run the test, Foundry will automatically populate the `_greeting` variable when the test is run.

### Logging

Foundry also supports logging.

To log out the greeting, we can use `log`, `log_string`, or `log_named_string`:

    function test2() public {
        assertEq(hello.version(), 0);
        hello.updateGreeting("Hello World");
        assertEq(hello.version(), 1);
        string memory value = hello.greet();
        emit log(value);
        assertEq(
            hello.greet(),
            "Hello World"
        );
    }
    

To print the logs, we need to run the test script with the `-vv` flag:

    forge test -vv
    

To learn more about logs and traces, check out the documentation [here](https://book.getfoundry.sh/forge/tests.html?highlight=-vv#logs-and-traces).

To view all of the supported logging methods, check out the documentation [here](https://book.getfoundry.sh/reference/ds-test.html#logging).

Anvil
-----

You can start the local EVM test network at any time:

This will start a local network and spin up 10 accounts and private keys and log them out to the console.

Once the network is running, we can use `forge` to deploy the contract to the network.

To do so, update the script in **script/Contract.s.sol:**

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.13;
    
    import "forge-std/Script.sol";
    import { HelloWorld } from 'src/HelloWorld.sol';
    
    contract ContractScript is Script {
        function setUp() public {}
    
        function run() public {
            vm.broadcast();
            new HelloWorld("Hello from Foundry!");
        }
    }
    

Next, use one of the private keys given to you by Anvil to run this script:

    forge script script/Contract.s.sol:ContractScript --fork-url http://localhost:8545 --private-key $PRIVATE_KEY
    

Once the contract is deployed, the contract address will be logged out to your terminal.

We can then use `cast` to interact with it.

For read operations, we can use `cast call`:

    cast call $CONTRACT_ADDRESS "greet()(string)"
    

For transactions, we can use `cast send`, passing in a private key and any arguments:

    cast send $CONTRACT_ADDRESS "updateGreeting(string)" "My new greeting" --private-key $PRIVATE_KEY
    

Conclusion
----------

Foundry is a welcome addition to the web3 stack, bringing improved tooling and performance for smart contract testing and development.

Huge shout out and thank you to Paradigm for their work building developer tooling like Foundry, it is much appreciated.

---

*Originally published on [Nader Dabit](https://paragraph.com/@nader-dabit/building-and-testing-smart-contracts-with-foundry)*
