Ethernaut x Foundry - 0x0 Hello Ethernaut

Ethernaut x Foundry - 0x0 Hello Ethernaut

Quick Introduction and Setup Instructions

ยท

5 min read

Foundry Primer

Foundry is a portable, fast and modular toolkit for Ethereum application development.

It is a re-implementation of Dapptools in Rust (we like the crab ๐Ÿฆ€) with no external dependencies and allows writing tests in Solidity directly.

Bye bye JavaScript/TypeScript ๐Ÿ‘‹ !

๐Ÿ’ก Recommended read: The Foundry Book

Installation

First we will need to download and install Foundry to our machine.

Follow these steps:

  1. (OPTIONAL) if foundry|forge was previously installed, make sure to delete the previous binaries or you might not get the latest update and it breaks stuff. e.g: rm -rf ~/.cargo/bin/cast && rm -rf ~/.cargo/bin/forge on macOS.
  2. curl -L https://foundry.paradigm.xyz | bash
  3. source ~/.(bash|zsh|...)rc or start a new terminal instance
  4. Run foundryup, that should install two binaries:forge & cast
  5. Sanity check:forge --version, the output should contain a timestamp like this: forge 0.1.0 (98f0771 2022-02-25T00:42:54.200449+00:00)

Essential commands

forge is the main CLI binary and takes in a bunch of subcommands. As with everything in Rust, documentation is well written and should be rather straightforward to follow. We will just go over the main commands that we will be using throughout the series.

  • forge init: this will create a new directory with the default template structure
  • forge build: compiles all the smart contracts in the /src directory.
  • forge test: runs all tests (by default) in the /src/test directory.
    • forge test -m ContractName -vvv: only run tests which name matches ContractName and print verbose information.
  • (OPTIONAL) forge remappings > remappings.txt: generate the mappings for our editor (VSCode).

Default Project Layout

Most Foundry projects should have a similar structure, here is how the default tempalte looks like with annotations:

musanking hello_foundry (master)
โžœ tree 
.
โ”œโ”€โ”€ foundry.toml                 // Project configuration file.
โ”œโ”€โ”€ lib                          // Contains libraries & dependencies installed with `forge install`.
โ”‚   โ””โ”€โ”€ ds-test                  // `ds-test` is the default test suite shipped with every Foundry project.
โ”‚       โ”œโ”€โ”€ LICENSE
โ”‚       โ”œโ”€โ”€ Makefile
โ”‚       โ”œโ”€โ”€ default.nix
โ”‚       โ”œโ”€โ”€ demo
โ”‚       โ”‚   โ””โ”€โ”€ demo.sol
โ”‚       โ””โ”€โ”€ src
โ”‚           โ””โ”€โ”€ test.sol
โ”œโ”€โ”€ out                         // Generated after running `forge build`. Contains compiled contracts and Application Binary Interface (ABI).
โ”‚   โ”œโ”€โ”€ Contract.sol
โ”‚   โ”‚   โ””โ”€โ”€ Contract.json
โ”‚   โ”œโ”€โ”€ Contract.t.sol
โ”‚   โ”‚   โ””โ”€โ”€ ContractTest.json
โ”‚   โ””โ”€โ”€ test.sol
โ”‚       โ””โ”€โ”€ DSTest.json
โ”œโ”€โ”€ remmapings.txt               // Helps editors like VSCode to locate library imports.
โ””โ”€โ”€ src                          // Your smart contracts should go in this directory. `forge build` will compile this by default.
    โ”œโ”€โ”€ Contract.sol
    โ””โ”€โ”€ test                     // Your tests should go in this directory. `forge test` will compile and run this by default.
        โ””โ”€โ”€ Contract.t.sol       // Naming convention for tests in Foundry.

6 directories, 9 files

This should be enough to get you started with Foundry. Now let's setup wargame!

Ethernaut

Ethernaut is a Web3/Solidity based wargame inspired in overthewire.org, to be played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'.

The official game can be played over at ethernaut.openzeppelin.com. It runs on Etherereum testnet Rinkeby so instead, we will be deploying our own local instance.

Our setup will be based on the following two repository, credits to their respective authors:

Setting up the Project

At this point we could just fork Ciaran's repository and start hacking things up, which is what I initially did, but then I didn't really understand how it was designed.

So Instead we will be re-creating a similar structure of our own.

Installing libraries

Foundry provides an easy way to install libraries from GitHub repositories with the forge install <githubName/repository> subcommand. For this project we will only use two:

  • openzeppelin-contracts: a library for secure smart contract development. Build on a solid foundation of community-vetted code.

  • forge-std: leverages forge's cheatcodes to make writing tests easier and faster while improving UX.

To simplify imports, we will also edit the foundry.toml file and generate mappings for VSCode

[default]
src = 'src'
out = 'out'
libs = ['lib']
remappings = [
    'ds-test/=lib/ds-test/src/',
    'forge-std/=lib/forge-std/src/',
    'openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/',
]
# See more config options https://github.com/gakonst/foundry/tree/master/config

Then run forge remapping > remappings.txt (make sure the file is in the base directory alongside /src

Game Directory Layout

musanking ethernaut-foundry/src (main)[โœ˜!?โ‡ก]
โžœ tree
.
โ”œโ”€โ”€ core
โ”‚   โ”œโ”€โ”€ BaseLevel.sol
โ”‚   โ””โ”€โ”€ Ethernaut.sol
โ”œโ”€โ”€ levels
โ”‚   โ””โ”€โ”€ 01-Fallback
โ”‚       โ”œโ”€โ”€ Fallback.sol
โ”‚       โ””โ”€โ”€ FallbackFactory.sol
โ””โ”€โ”€ test
    โ””โ”€โ”€ 01-Fallback.t.sol

4 directories, 5 files
  • The /core directory contains the game's main logic in Ethernaut.sol taken from OpenZeppelin's Github and updated to Solidity v0.8.10.

  • Each level is composed of two contracts:

    • <LevelName>.sol which contains the main logic.
    • <LevelName>Factory.sol which extends the game's BaseLevel and helps us create level instances as well as validate win conditions.
  • The solution of each level is represented as a test file in the src/test directory and follow Foundry's name convention <LevelName>.t.sol

How to Write a Test

All of our tests will follow the same template:

  1. Initiate the main Ethernaut contract by calling the setUp() function.
  2. Create a level instance using that calls the appropriate Factory function.
  3. Program the attack logic to solve the challenge.
  4. Submit the solution and check the result.

๐Ÿ’ก More information about the ds-test library can be found in the Foundry Book

pragma solidity ^0.8.10;

import "ds-test/test.sol";
import "forge-std/Vm.sol";

import "../levels/01-Fallback/FallbackFactory.sol";
import "../core/Ethernaut.sol";

contract FallbackTest is DSTest {
    Vm vm = Vm(address(HEVM_ADDRESS));
    Ethernaut ethernaut;
    address eoaAddress = address(1337);

    function setUp() public {
        ethernaut = new Ethernaut();
        vm.deal(eoaAddress, 1 ether);
    }

    function testFallbackHack() public {
        /////////////////
        // LEVEL SETUP //
        /////////////////
        FallbackFactory fallbackFactory = new FallbackFactory();
        ethernaut.registerLevel(fallbackFactory);
        vm.startPrank(eoaAddress);
        address levelAddress = ethernaut.createLevelInstance(fallbackFactory);
        Fallback ethernautFallback = Fallback(payable(levelAddress));

        //////////////////
        // LEVEL ATTACK //
        //////////////////
        // (...)

        //////////////////////
        // LEVEL SUBMISSION //
        //////////////////////
        bool levelSuccessfullyPassed = ethernaut.submitLevelInstance(
            payable(levelAddress)
        );
        vm.stopPrank();
        assert(levelSuccessfullyPassed);
    }
}

This should be enough to get us started, make sure to follow each step and everything should work out fine. If you have any problem, drop a comment below and I'll try to help.

ย