---
# System prepended metadata

title: Foundry article

---

# Foundry for studying hacks

## Intro

This article aims to explain how the Foundry smart contract development framework can be used in studying hacks. It can be a useful workflow for beginners who prefer studying hacks as a step in mastering Solidity skills.

## Setting up

Foundry is a smart contract development framework, like Hardhat or Brownie. Like others, it allows the compilation and deploying smart contracts and projects, writing all the variety of necessary tests.
The key advantages are:
- it is fast
- you write in Solidity, both smart contracts, tests, and script (no need to switch between Python/JS and Solidity)

Follow this guide to install Foundry:
https://book.getfoundry.sh/getting-started/installation


The simplest way to build a project is running:
```
forge init
```

As a result, you will have this project structure.
```
├── lib
│   └── forge-std
        ...
├── script
│   └── Counter.s.sol
├── src
│   └── Counter.sol
└── test
│   └── Counter.t.sol
├── foundry.toml
├── README.md
```

This project has a basic smart contract and a simple unit test. We can run the test:
```
forge test
```



## Forking

By default, Foundry launches a network on a local machine. But you can easily customize to use any real network - Foundry allows forking networks, including specifying the exact block.

In practice it is widely used for real-world tests - some projects prefer testing their project in the environment of real tokens and other projects.

Here we should introduce the concept of **Cheatcodes**. Foundry has built-in precompiles - it treats some smart contract calls as reserved commands, allowing some magic not allowed in real networks.

There is a huge list of available Cheatcode commands:
https://book.getfoundry.sh/forge/cheatcodes?highlight=cheatc#cheatcodes

Manipulating cheatcodes is the key to mastering Foundry.

Now let's write a test:

```
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";

contract ForkTest is Test {

    function setUp() public {
        vm.createFork(MAINNET_RPC_URL);
    }
    
    function test_PrintBalanceCETH {
        address cETH = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
        uint256 balanceOnCETH = cETH.balance;
        console.log("Balance on cETH: ", balanceOnCETH);
    }
```

We created `ForkTest` file, which inherits from Test (imported from `forge-std/Test.sol`). This is how we connect our Cheatcodes.

It has `function setUp()` - this is a reserve function name. It will be run before every other test function. So here we can indicate operations that are the same for every test function in the file. Forking perfectly fits here:
```
vm.createFork("MAINNET_RPC_URL")
```

This cheatcode tries to find an RPC named "MAINNET_RPC_URL" in your `.env` file in the project directory.

So we should configure an env file - you should add ".env" file to the project directory.
Fill it with this line:
```
MAINNET_RPC_URL=https://eth.llamarpc.com
```

You can register on an RPC provider website to take the key (like Alchemy).
Or you can google any publicly available RPCs (but remember that not all of them will allow forking).

If your tests require multiple RPCs, you can indicate any necessary amount of them in your `.env` file.

Technically it is possible to run multiple forks and switch between them in one test file, everything using just Cheatcode commands. So, having multiple RPCs is the case.

When `function setUp()` sets a fork, it is time for tests. They must be named as `test_YourTestName`.
```
function test_PrintBalanceCETH {
        address cETH = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
        uint256 balanceOnCETH = cETH.balance;
        console.log("Balance on cETH: ", balanceOnCETH);
    }
```
This function introduces a new Cheacode - `console.log()`.
It is very handy and allows printing on your console.

So, this test takes the mainnet cETH address and prints its ETH balance.

To execute tests, run the following forge terminal command:
```
forge test
```

You will notice that you see nothing printed. That's because Foundry has multiple levels of script detailization.

As described in Foundry docs:

Level 1 (`forge test`): Only displays a summary of passing and failing tests.
Level 2 (`forge test -vv`): Logs emitted during tests are also displayed. That includes assertion errors from tests, showing information such as expected vs actual.
Level 3 (`forge test -vvv`): Stack traces for failing tests are also displayed.
Level 4 (`forge test -vvvv`): Stack traces for all tests are displayed, and setup traces for failing tests are displayed.
Level 5 (`forge test -vvvvv`): Stack traces and setup traces are always displayed

So, you will see your `console.log()` if you run tests as:
```
forge test -vv
```






## Forking to study hacks

We will use this repository by SunWeb3Sec to study the library of hacks.
https://github.com/SunWeb3Sec/DeFiHackLabs

It is a large Forge repository with a long list of hack demonstrations.
You can find all the tests in `src/test`. To the day of writing this article, the repository consists of more than 300 hacks.

First of all, follow the setup instruction in the README of the repo, as cloning a repo has a different script.

When you have it locally, take a look at the repo structure - this repo has many tests, but don't run everything with `forge test` - it will take a lot of time to run them all.

Instead, use this:
```
forge test --contracts src/test/NAME_OF_THE_FILE.t.sol -vv
```
Or this one:
```
forge test --match-path src/test/NAME_OF_THE_FILE.t.sol -vv
```

## Simple hack

In this section we will go through one of the hacks - CowSwap exploit.
https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/CowSwap_exp.sol

This is the transaction of the hack.
https://etherscan.io/tx/0x90b468608fbcc7faef46502b198471311baca3baab49242a4a85b73d4924379b
As you can see an attacker managed to build a malicious calldata. So this hack will be very simple to demonstrate additional Foundry possibilities.

First of all, look at the setUp() function:
```
    function setUp() public {
        cheats.createSelectFork("mainnet", 16_574_048);
        vm.label(address(DAI), "DAI");
        vm.label(address(swapGuard), "SwapGuard");
        vm.label(address(GPv2Settlement), "GPv2Settlement");
    }
```
This type of forking is almost the same. There is a small difference between:
```
vm.createSelectFork()
and
vm.createFork()
```
You can learn in deep here:
https://book.getfoundry.sh/cheatcodes/forking

`cheats` and `vm` have no difference for our purposes.

But both `createSelectFork()` and `createFork()` can accept the second argument - the block number. As you can see for this attack it is `16_574_048`.

Then you have this cheatcode:
```
vm.label(address(DAI), "DAI");
```
It helps to read traces better. If traces in your console print address `0x6B175474E89094C44Da98b954EedeAC495271d0F` it will be displayed with the "DAI" label. It is handy in analyzing complicated traces.

Then, you have the attack flow in the function `testExploit()`. Here is the question - who is the attacker in this test? For all tests, Foundry has a default `msg.sender` and `tx.origin`. For this simple attack, they are not changed, because everyone can make this attack, even some default address.


## More complex hack

Here we will study this Hundred Finance hack.
https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/HundredFinance_2_exp.sol

The official post-mortem:
https://blog.hundred.finance/15-04-23-hundred-finance-hack-post-mortem-d895b618cf33

It utilizes the inflation attack, a well-known attack vector. If you are not familiar, we recommend our article explaining the topic in deep.
https://mixbytes.io/blog/overview-of-the-inflation-attack

The exploit test is messy. First of all, it has three smart contracts in it.
- contractTest - test script itself
- ETHDrain - code of one of the attacker contracts
- tokenDrain - same thing, but for tokens

This is done to have all smart contract codes in one file. As the repo reserves one file for an attack.

If the call is made from a deployed smart-contract it will be treated as usual - that this deployed smart-contract makes a call (`msg.sender` is changed according to EVM rules).

In the attack script, we have these lines:
```
        ...
        cheats.startPrank(HundredFinanceExploiter);
        hWBTC.transfer(address(this), 1_503_167_295);
        cheats.stopPrank();
        ...
```

`startPrank()` is the extremely important cheatcode. It allows changing a `msg.sender` for the next calls. It ends with the `cheats.stopPrank()` which changes `msg.sender` back to the default address.

The widely used alternative is `prank()`. It changes a `msg.sender` only for the next call.

The script ends with taking a flashloan. But it is not the end, because it continues in the flashloan callback - at the function `executeOperation()`.
This function runs a few attacks - one `ETHDrains()` and multiple `tokenDrains()`. You can find these functions below.

Each of them follows the same pattern where some of the attack steps are placed either in `tokenDrains()` function or in tokenDrain smart contract constructor.

Each step is followed by `console.log()` - as a result, the attack is very well documented.


## Further steps

Now you are familiar with Foundry. You can use it in development or keep studying hacks in the same way as we did here.

