Skip to content

Foundry Setup

The last toolkit is the newest one and the last one I want to show you in this section.

It's Foundry, a completely fresh rewrite in Rust. It's blazing fast and not dependent on JavaScript at all.

Install Foundry

To install Foundry, you run an actual shell script on your computer.

You can, for example, create a new folder for foundry in our nft-project folder:

mkdir foundry
cd foundry
curl -L https://foundry.paradigm.xyz | bash
source ~/.bashrc 
foundryup

Now you will have foundry installed, which comes with three subcommands:

  • forge: the build, test, debug, deploy smart contracts
  • anvil: the foundry equivalent of Ganache
  • cast: low level access to smart contracts (a bit of a truffle console equivalent)

Initializing a new Project

To get started with foundry, initialize a new project in the foundry directory:

forge init .

This will download a starter repository and setup everything for the first use.

It looks a bit different than hardhat and truffle:

  • contracts are in the src folder
  • tests are in the test folder and are solely solidity tests
  • scripts are in the scripts folder and are solidity files
  • dependencies are added as git submodules into the lib folder
  • Project is configured using the foundry.toml file

After building (which you'll see in a second), the contract artifacts are in the out folder

It doesn't depend on JavaScript at all. Everything is a git submodule. And its also blazing fast.

Building and Testing

To build the smart contracts, simply run

forge build

It will compile all files and put the ABI, bytecode and AST as json files into subfolders with the name of the contracts in the out folder

Let's see how we can add our NFT into the folders...

Adding our NFT

First thing is to delete the existing contract, test and script:

rm src/*.sol test/*.sol script/*.sol

Then simply copy over the Spacebears.sol contract from Hardhat. If you try to run forge build, it will fail, because the package for openzeppelin isn't installed.

Here's one of the biggest differences to HardHat and Truffle: Foundry is not a JavaScript based framework and everything works with git submodules. Here we need to install github-user/repo-name, which is openzeppelin/openzeppelin-contracts for https://github.com/OpenZeppelin/openzeppelin-contracts!

Install openzeppelin with

forge install openzeppelin/openzeppelin-contracts

Which will fail, because we have changes in our branch, which we need to commit first:

If you run git status it will tell you what changed:

We added the Spacebear contract and removed all other contracts from the directories.

git add .
git commit -a -m "Added spacebear contract and removed everything else"

This should do the trick. Now, let's try again:

forge install openzeppelin/openzeppelin-contracts

We also need to change the imports, as we're not importing from the npm module @openzeppelin/..., we're importing from openzeppelin-contracts...:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "openzeppelin-contracts/token/ERC721/ERC721.sol";
import "openzeppelin-contracts/access/Ownable.sol";
import "openzeppelin-contracts/utils/Counters.sol";

contract Spacebear is ERC721, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("Spacebear", "SBR") {}

    function _baseURI() internal pure override returns (string memory) {
        return "https://ethereum-blockchain-developer.com/2022-06-nft-truffle-hardhat-foundry/nftdata/";
    }

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }

    function buyToken() public payable {
        uint256 tokenId = _tokenIdCounter.current();
        require(msg.value == tokenId * 0.1 ether, "Not enough funds sent");

        _tokenIdCounter.increment();
        _safeMint(msg.sender, tokenId);
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override(ERC721) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        pure
        override(ERC721)
        returns (string memory)
    {
        return string(abi.encodePacked(_baseURI(),"_",tokenId+1,".json"));
    }
}

Now the compilation works:

Let's have a look in the next lecture how we can do unit-testing with Foundry. It's a lot of fun, because foundry is

  1. blazing fast
  2. allows VM modifications from within solidity

Last update: November 13, 2022