DEVELOPING AND DEPLOYING SMART CONTRACTS WITH FOUNDRY

304
0

Foundry is a smart contract development toolkit for Ethereum application development written in Rust. It is an alternative technology to Ethereum development tools like Truffle and Hardhat.

Before we enter Foundry, smart contracts are self-executing digital contracts that execute predefined rules and run automatically on the blockchain. You can check out our blog post on smart contracts to learn more about smart contracts.

Foundry has the following four major components: 

  1. Forge: Forge is a smart contract testing framework that ships with Foundry. Forge tests, builds, and deploys your smart contracts.
  2. Cast: 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!
  3. Anvil: Anvil is a local testnet node shipped with Foundry. You can use it to test your contracts from frontends or interact with RPC. This is similar to Truffle’s ganache
  4. Chisel: Chisel is an advanced Solidity REPL shipped with Foundry. It can be used to quickly test the behavior of Solidity snippets on a local or forked network.

FOUNDRY INSTALLATION

Installing Foundry is done using the Foundry installer Foundryup.

To get started with the installation, open any terminal of your choice and run the following command:

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

After a successful installation, we should see results like this:

Next, we would follow the command to open another terminal tab and run this command:

foundryup

If correctly done, we should see results like this:

SETTING UP A PROJECT WITH FOUNDRY

Now that Foundry is installed, we will go ahead to initialize a new project with it using the command below:

forge init foundry-hello-world

Note that foundry-hello-world is the name of the project

Once the project is created, we can navigate into the directory and check the generated project by running the following command: 

Cd foundry-hello-world

In order to list all files and directories in the current directory, including hidden ones, and display them in the long format, we would  run the following command: 

Ls -al

We should see results like this:

Now we can go ahead to build the project by running this command:

Forge build

Once build is successful, you should see results like this:

INSTALLING DEPENDENCIES

By default, Forge handles dependencies through git submodules, allowing compatibility with any GitHub repository containing smart contracts.

To use OpenZeppelin, we need to install it as a dependency in our project, to-do so use the command

Forge install OpenZeppelin/openzeppelin-contracts

Forge install is the command for installing dependencies and Openzeppelin is the dependency,

The command above pulls the openZeppelin-contracts library, stages the .gitmodules file in git and makes a commit with the message “Installed openzeppelin-contracts”.

If done correctly, we should see results like this:

PROJECT LAYOUT

Forge is flexible on how you structure your project. By default, the structure looks like this:

You can configure Foundry’s behavior using foundry.toml.

  • Remappings are specified in remappings.txt.
  • The default directory for contracts is src/.
  • The default test directory is test/, where any contract with a function that starts with test is considered a test.
  • Dependencies are stored as git submodules in lib/.

You can configure where Forge looks for 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.

Note, if you are using VScode like I am, you might encounter errors while importing dependencies, like this 


To solve this error, the solution will require remapping

To do this run this command:

forge remappings > remappings.txt

What this command does is it creates a remappings.txt file inside the root directory of the project

At this moment the content in the file might look like this,

ds-test/=lib/forge-std/lib/ds-test/src/
erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
openzeppelin/=lib/openzeppelin-contracts/contracts/

WRITING SMART-CONTRACTS

Next, we will create a Simple token contract file named SimpleToken.sol

And replace it with Counter.sol in our foundry-hello-world project

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

import “openzeppelin/contracts/token/ERC20/ERC20.sol”;

contract SimpleToken is ERC20 {
    constructor(uint256 initialSupply) ERC20(“Simple Token”, “STK”) {
        _mint(msg.sender, initialSupply * (10 ** uint256(decimals())));
    }
}

TESTING THE SIMPLE TOKEN CONTRACT WITH FOUNDRY

Tests are written in solidity which minimizes context switching.

To test the token smart-contract above, we have to rename the test file from Counter.t.sol to SimpleToken.t.sol 

This code below is the test contract

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

import {Test, console2} from “forge-std/Test.sol”;
import {SimpleToken} from “../src/SimpleToken.sol”;

contract SimpleTokenTest is Test {
    SimpleToken public token;

    function setUp() public {
        token = new SimpleToken(1000000);
    }

    function test_InitialSupply() public {
        assertEq(token.name(), “Simple Token”);
        assertEq(token.symbol(), “STK”);
        assertEq(token.totalSupply(), 1000000 * 10 ** 18);

        uint256 ownerBalance = token.balanceOf(address(this));
        assertEq(ownerBalance, 1000000 * 10 ** 18);
    }
}

After writing the code test, write the code for the script named SimpleToken.s.sol

With this code

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

import {Script, console2} from “forge-std/Script.sol”;

contract SimpleTokenScript is Script {
function setUp() public {}

function run() public {
vm.broadcast();
}
}

After this, run forge test in your terminal, if run correctly you should see results like this

GENERATING GAS REPORT

To generate gas reports for our contracts, well use the –gas-report flag with the forge test command like this:

Forge-test –gas-report

If run correctly, we should see results like this:

DEPLOYING SMART CONTRACTS WITH FOUNDRY

Forge can deploy smart contracts to a given network with the forge create command.

Forge can deploy only one contract at a time.

To deploy a contract, you must provide an RPC URL (env: ETH_RPC_URL) and the account’s private key that will deploy the contract.

Now, we’ll deploy our SimpleToken contract to a network using the command
forge create –rpc-url <your_rpc_url> –private-key <your_private_key> src/SimpleToken.sol:SimpleToken

rpc-url is the RPC on the network you want to deploy to, private-key is your deployer wallet private key.

Solidity files may contain multiple contracts. :SimpleToken above specifies which contract to deploy from the src/SimpleToken.sol file.

Use the –constructor-args flag to pass arguments to the constructor.

To run the complete command for deploying the SimpleToken Contract to Binance smart chain testnet, we will run this command in our terminal:

forge create –rpc-url https://data-seed-prebsc-2-s2.binance.org:8545 –private-key <Your-private-Key>  src/SimpleToken.sol:SimpleToken  –constructor-args “Simple Token” “STK” 18 1000000000000000000000

If run correctly, we should see results like this:

Optionally, we can also verify contracts if you have your API key using the –verify <Etherscan-api-key> flag.

CONCLUSION

Foundry is a very convenient tool for deploying smart contracts. You can read and practice more on foundry by accessing their documentation.

You can also access this tutorial code on Git Hub.

Leave a Reply

Your email address will not be published. Required fields are marked *