Demystifying Foundry
Tags: blockchain, ethereum, programming, tutorial
Published: 2022-06-20 01:15:07

## Introduction

If you're like me, you probably like trying out different technologies just for the fun of it and maybe you've been hearing everyone talk about Foundry and you don't know what it is or have the time to research about it or even go through the [foundry book](https://book.getfoundry.sh/index.html).  Well, if you fit into this category, this article is for you because I'll be talking about what foundry is, why I think you should use it and how you can use it.

## What is Foundry?

According to the foundry book:

> Foundry is a smart contract development toolchain.
Foundry manages your dependencies, compiles your project, runs tests, deploys, and lets you interact with the chain from the command line.
> 

You might be thinking isn't that what hardhat and truffle already do? Yeah, you're right but Foundry does it faster and personally, I think the best thing about Foundry is the tests. Usually, when working with hardhat or truffle, during smart contracts development you need to write tests for your smart contracts and they're usually in JavaScript. Some may enjoy this but not everyone enjoys switching between languages as they work and let's not forget the fact that not everyone knows JavaScript. 

Foundry takes a different approach with writing tests for smart contracts, tests are written in solidity and developers are given the ability to manipulate their test EVM environment to an unbelievable length and are also introduced to various types of tests, not just plain old unit tests. Foundry introduces smart contract developers to other types of testing such as Fuzzing, differential testing and other goodies that the Foundry team are still working on behind the scenes. 

At this point, you might be thinking “yeah, it has fancy tests so what?”, whether you thought of that or not I would like to stress the importance of testing smart contracts. At this early stage of blockchain technology in general, we are faced with various issues and one of the most annoying ones is security exploits on smart contracts which leads to the loss of millions of dollars in various protocols. These exploits push the reality of mass adoption of blockchain technologies further and further away and as blockchain developers, we do not want that because definitely when the ecosystem grows, so do we and that's why it is important for you to have a tool like Foundry in your arsenal to help you test your smart contracts beyond what other existing tools provide.

## Installation

So now that you understand the usefulness of Foundry, it's time you add it to your development toolkit. 

### On Linux and macOS

If you use Linux or macOS, you can get Foundry by the following the steps:
**Install the latest release by using foundry up**

This is the easiest option for Linux and macOS users.
Open your terminal and type in the following command:

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

This will download `foundryup`. Then install Foundry by running:

```bash
foundryup
```

If everything goes well, you will now have three binaries at your disposal: forge, cast and anvil.

### On Windows, build from source

If you use Windows, you need to build from source to get Foundry.

Download and run `rustup-init` from [rustup.rs](https://rustup.rs/). It will start the installation in a console.

After this, run the following to build Foundry from source:

```bash

cargo install --git https://github.com/foundry-rs/foundry foundry-cli anvil --bins --locked
```

To update from source, run the same command again.

## Getting Started

Now that we have Foundry installed on our machine, let’s do something with it and I think building, testing, deploying and verifying an ERC20 contract with Foundry would not hurt. 

We will start by creating a Foundry project, first, go into the directory you want to create your project in then run the following commands:

```bash
# creates foundry project
forge init hey-foundry

# navigates into the project
cd hey-foundry 

# shows you the content of the project
tree -L 2
```

These commands should create a new Foundry project called `hey-foundry` , enter the directory of the project then show you the contents of your project. 

![foundry-project-structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zcgg4j6yaodeuxtkyzhr.png)

In our newly generated Foundry project, we have three directories and one `toml` file which are: 

- `src`
- `test`
- `script`
- `lib`
- `foundry.toml`

Now let’s talk briefly about what each of them is for, the `src` directory is where our smart contracts are kept, the `test` directory is where we write our tests, the `script` directory is where we store how solidity scripts, the `lib` directory is similar to a `node_modules` directory and it contains dependencies we install and finally we have the `foundry.toml` file, which we use to configure the behaviour of our foundry project.

<aside>
💡 Note: the `src`, `test`and `script` directories both have one file each in them by default called `Contract.sol`, `Contract.t.sol`  and `Contract.s.sol` respectively. The `lib` directory has a folder in it too called `forge-std`.

</aside>

Next, you should open the `hey-foundry` project in your preferred code editor.

Instead of writing our own ERC20 contract from scratch, let’s use the OpenZeppelin implementation and what’s a better way to use their contracts than installing their contracts into our project, so we can import them for our usage. Usually, with other toolchains, we would need to use the **Node Package Manager**(NPM) to install the OpenZeppelin contracts but with Foundry, we have the privilege to use something that is faster and also has less baggage. To install the OpenZeppelin contracts into our project we would need to run the following command:

```bash
forge install OpenZeppelin/openzeppelin-contracts 
```

If you check the `lib` directory you should you should see the `openzeppelin-contracts` folder.

Now open the `src`  directory and delete the `Contract.sol`  file and create a new file called `MyToken.sol`, the contents of this file should look like this:

Now open the `src` directory and rename the `Contract.sol` file as `MyToken.sol` then copy and paste the code below into it.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    address public owner;

    constructor() ERC20("My Token", "MTKN") {
        owner = msg.sender;
        _mint(msg.sender, 1000 * 10**decimals());
    }

    function mint(address account, uint256 amount) public {
        require(msg.sender == owner, "Only Owner can mint");
        _mint(account, amount);
    }

    function burn(address account, uint256 amount) public {
        require(msg.sender == owner, "Only Owner can burn");
        _burn(account, amount);
    }
}
```

In your code editor you should be getting an error on line 4 `import "@openzeppelin/contracts/token/ERC20/ERC20.sol";` , this is because your code editor cannot find the path to the file we want to import. We can fix this by running:

```bash
forge remappings > remappings.txt 
```

This should create a remappings.txt file which contains the path to all the dependencies that we have installed in our project and we can use this file to direct other files that want to import our dependencies on where they can get it. The contents of this file should look like this:

```
openzeppelin-contracts/=lib/openzeppelin-contracts/
forge-std/=lib/forge-std/src/
ds-test/=lib/forge-std/lib/ds-test/src/
```

And remember our `MyToken.sol` file is trying to import the OpenZeppelin ERC20 contract from `@openzeppelin` which would work if we had installed the dependency with **NPM** but since we installed it with `forge` we have to do a little tweaking. Don’t worry it’s not that complex all we have to do is change the line that has `openzeppelin-contracts/=lib/openzeppelin-contracts/` in the `remappings.txt` file to this:

 

```
@openzeppelin/=lib/openzeppelin-contracts/
```

If you followed the steps correctly, you should notice that the error has been resolved. This is not the only way to solve this error but for simplicity's sake, we’ll just go with this for now.

## Testing our contract

Now that we are done writing our smart contract, it is ideal we test it. So as we did before, rename the `Contract.t.sol` file as `MyToken.t.sol`. Once you’re done copy and paste the following code into the file.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "../src/MyToken.sol";

contract MyTokenTest is Test {
    // Target contract
    MyToken myToken;

    // Actors
    address owner;
    address ZERO_ADDRESS = address(0);
    address spender = address(1);
    address user = address(2);

    // Test params
    string public name = "My Token";
    string public symbol = "MTKN";

    uint256 public decimals = 18;
    uint256 amount = 1000 * 1e18;
    uint256 public initialSupply = 1000 * 1e18;

    event Transfer(address indexed from, address indexed to, uint256 value);

    //  =====   Set up  =====

    function setUp() public {
        owner = address(this);
        myToken = new MyToken();
    }

    //  =====   Initial state   =====

    /**
     *  @dev Tests the relevant initial state of the contract.
     *
     *  - Token name is 'My Token'
     *  - Token symbol is 'MTKN'
     *  - Token initail supply is '1000000000000000000000'
     *  - Token decimals is '18'
     */
    function testinitialState() public {
        // assert if the corrent name was used
        assertEq(myToken.name(), name);

        // assert if the correct symbol was used
        assertEq(myToken.symbol(), symbol);

        // assert if the correct initial supply was set
        assertEq(myToken.totalSupply(), initialSupply);

        // assert if the correct decimal was set
        assertEq(myToken.decimals(), decimals);
    }

    //  =====   Functionality tests   =====

    /// @dev Test `mint`

    // Only Owner should be able to mint
    function testFailUnauthorizedMinter() public {
        vm.prank(user);
        myToken.mint(user, amount);
    }

    // Should not be able to mint to the zero address
    function testFailMintToZeroAddress() public {
        vm.prank(owner);
        myToken.mint(ZERO_ADDRESS, amount);
    }

    // Should increase total supply
    function testIncreseTotalSupply() public {
        uint256 expectedSupply = initialSupply + amount;
        vm.prank(owner);
        myToken.mint(owner, amount);
        assertEq(myToken.totalSupply(), expectedSupply);
    }

    // Should increase recipient balance
    function testIncreaseRecipientBalance() public {
        vm.prank(owner);
        myToken.mint(user, amount);
        assertEq(myToken.balanceOf(user), amount);
    }

    // Should emit Transfer event
    function testEmitTransferEventForMint() public {
        vm.expectEmit(true, true, false, true);
        emit Transfer(ZERO_ADDRESS, user, amount);
        vm.prank(owner);
        myToken.mint(user, amount);
    }
}
```

Don’t worry if some of it seems weird to you now, by the end of the tutorial you should have a good understanding of what is happening in the code above but for now just know we’re writing unit tests for our `MyToken.sol` contract.

We’ll now go through the code together line by line. 

```solidity
import "forge-std/Test.sol";
import "../src/MyToken.sol";
```

Over here, we import two files, the contract we want to test - `MyToken.sol` and a contract containing utilities we’ll need while testing our contract - `Test.sol` from the forge standard library. 

```solidity
contract MyTokenTest is Test {
```

We create a contract named `MyTokenTest` and inherit the `Test` contract from `"forge-std/Test.sol"`.

```solidity
  // Target contract
    MyToken myToken;

    // Actors
    address owner;
    address ZERO_ADDRESS = address(0);
    address spender = address(1);
    address user = address(2);

    // Test params
    string public name = "My Token";
    string public symbol = "MTKN";

    uint256 public decimals = 18;
    uint256 amount = 1000 * 1e18;
    uint256 public initialSupply = 1000 * 1e18;

    event Transfer(address indexed from, address indexed to, uint256 value);

```

Think of these guys as the variables we need to make our tests work.

```solidity
    //  =====   Set up  =====

    function setUp() public {
        owner = address(this);
        myToken = new MyToken();
    }
```

This is an important function that is used when writing Foundry tests, as the name implies we are setting up our test. We usually do things like deploying our contracts in it and that’s what we’re doing here, we’re also assigning an address to the `owner` state variable. The address of the `MyTokenTest` is set as the owner because it is the address that deploys it.

```solidity
    //  =====   Initial state   =====

    /**
     *  @dev Tests the relevant initial state of the contract.
     *
     *  - Token name is 'My Token'
     *  - Token symbol is 'MTKN'
     *  - Token initail supply is '1000000000000000000000'
     *  - Token decimals is '18'
     */
    function testinitialState() public {
        // assert if the corrent name was used
        assertEq(myToken.name(), name);

        // assert if the correct symbol was used
        assertEq(myToken.symbol(), symbol);

        // assert if the correct initial supply was set
        assertEq(myToken.totalSupply(), initialSupply);

        // assert if the correct decimal was set
        assertEq(myToken.decimals(), decimals);
    }

```

Over here we are testing for the initial state of the `MyToken` contract we just deployed in the `setUp` function. Since we assigned the deployed contract to the identifier `myToken`, we can invoke functions in it by writing `myToken.<function_name>()`. 

ERC20 tokens usually have a name, symbol, initial supply and decimals, so here we’re trying to check if the correct ones were set. We do this by using the `assertEq` function, which checks if the two parameters passed into it are equal.

<aside>
💡 Note: We get the `assertEq` function from the `Test.sol` file we imported earlier.

</aside>

```solidity
  //  =====   Functionality tests   =====

    /// @dev Test `mint`

    // Only Owner should be able to mint
    function testFailUnauthorizedMinter() public {
        vm.prank(user);
        myToken.mint(user, amount);
    }

    // Should not be able to mint to the zero address
    function testFailMintToZeroAddress() public {
        vm.prank(owner);
        myToken.mint(ZERO_ADDRESS, amount);
    }

    // Should increase total supply
    function testIncreseTotalSupply() public {
        uint256 expectedSupply = initialSupply + amount;
        vm.prank(owner);
        myToken.mint(owner, amount);
        assertEq(myToken.totalSupply(), expectedSupply);
    }

    // Should increase recipient balance
    function testIncreaseRecipientBalance() public {
        vm.prank(owner);
        myToken.mint(user, amount);
        assertEq(myToken.balanceOf(user), amount);
    }

    // Should emit Transfer event
    function testEmitTransferEventForMint() public {
        vm.expectEmit(true, true, false, true);
        emit Transfer(ZERO_ADDRESS, user, amount);
        vm.prank(owner);
        myToken.mint(user, amount);
    }
```

In this part of the code, we test the `mint` function for different cases in a series of functions. The comments I added at the top of each function should provide enough information about what they do, although the lines where I used `vm.prank()` and `vm.expectEmit()` may seem alien to you. 

These two functions are examples of what are known as cheat codes in Foundry, we use them to manipulate the test EVM environment to procure real-life situations, and there are cheat codes to manipulate `block.timestamp` , `block.number`, the nonce and so [much more](https://book.getfoundry.sh/cheatcodes/index.html). 

So what are `vm.prank()` and `vm.expectEmit()` used for? `vm.prank()` is used to change the `msg.sender` of the next call and you pass the address you want the next call to use as `msg.sender` into `vm.prank` as a parameter while `vm.expectEmit` on the other hand, helps us check if the next event emitted in a function is what we expect.

<aside>
💡 For more information on Foundry cheat codes, check out this [link](https://book.getfoundry.sh/cheatcodes/index.html). For more information on `vm.prank()` and `vm.expectEmit()` check out the following links respectively, [vm.prank()](https://book.getfoundry.sh/cheatcodes/prank.html) and [vm.expectEmit()](https://book.getfoundry.sh/cheatcodes/expect-emit.html).

</aside>

Now that you understand the code, let’s run it. We run tests in Foundry with the following command:

```bash
forge test 
```

After running `forge test` , you should see this in your terminal:

![foundry-test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nce1u13awwinn7pxthw3.png)

And with that, we’re done testing our contract. Congrats for making it this far, here have some coffee ☕.

## Deploying and Verifying

Prior to now, we would run extensive commands in our terminal to deploy our contracts but recently the Foundry team introduced something phenomenal and that’s solidity scripting. If you have experience using hardhat you should be familiar with using scripts to deploy smart contracts, that’s practically the same thing we’re doing here but instead of writing our scripts in JavaScript, we write them in solidity instead. 

Now, that we have the theoretical part out of the way let’s get coding. First of all, we would need to rename the `Contract.s.sol` file that’s in the `script` directory to `MyToken.s.sol`.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "../src/MyToken.sol";

contract MyTokenScript is Script {
    function run() external {
        vm.startBroadcast();
        MyToken myToken = new MyToken();
    }
}
```

Our script is pretty similar to a normal solidity smart contract so it easy to understand. Basically, it is a contract that has one function called `run` and that’s where we deploy our contract, the only thing that is alien here is `vm.startBroadcast`. All `vm.startBroadcast` does is *create transactions that can later be signed and sent onchain.* 

Now that we have written our script, it is time we run this code so we can deploy our contract to the Rinkeby testnet but before we do that we have to create a `.env` file storing our Rinkeby RPC URL, private key and Etherscan key . The `.env` file should have the following variables provided:

```
RINKEBY_RPC_URL=
PRIVATE_KEY=
ETHERSCAN_KEY=
```

Next, we run the following commands :

```bash
# To give our shell access to our environment variables
source .env

# To deploy and verify our contract
forge script script/MyToken.s.sol:MyScript --rpc-url $RINKEBY_RPC_URL  --private-key $PRIVATE_KEY --broadcast --verify --etherscan-api-key $ETHERSCAN_KEY -vvvv
```

![contract-deployed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5t53nwwbtx2tprc0ouwu.png)

Well done! You’ve just deployed the `MyToken` contract to the Rinkeby network and that’s not all you did, you also verified the contract on Etherscan all with one command.

## Conclusion

Although Foundry is a new tool and some developers might be sceptical to use it because of how comfortable they have become with previously existing tools if you’re one of them I would ask you to give Foundry a shot because Foundry is a tool that is beyond its time and is essential to the general advancement of the Smart contract development community. 

In this tutorial, we only covered the surface of Foundry. To learn more about Foundry you could try going through the [Foundry book](https://book.getfoundry.sh/index.html) and use Foundry more. Thanks for reading till next time Fren^-^.