# Using Foundry to give yourself $1bn

By [Brock](https://paragraph.com/@brocke) · 2022-02-09

---

so you want to be a billionaire?
================================

Okay, okay, so you won’t _technically_ become a billionaire from this method (sorry for the clickbait). You will be a pretend billionaire though. And hopefully, this post helps you develop a sick DeFi app that takes the world by storm that takes you a step closer to _actually_ become one. This post outlines how to use [forge-std](https://github.com/brockelmore/forge-std) to improve your experience using mainnet forks and existing protocols & tokens. Using `forge-std`, we can remove the need of performing a swap or impersonating an account to obtain arbitrary tokens - **in just 3 lines of code**. Even if you never write mainnet fork tests, you will find value in this post as writing token balances is just _one_ application of the tools in the stdlib.

If you don’t know what mainnet forking is, or why you might want to use it, check out this great write up of using mainnet forks in Forge:

[https://mirror.xyz/susheen.eth/bRCzT2QLdNINMVk8251udkfjHW\_T9ascCQ1DV9hURz0](https://mirror.xyz/susheen.eth/bRCzT2QLdNINMVk8251udkfjHW_T9ascCQ1DV9hURz0)

forge cheatcodes
================

Forge, the testing framework built into `foundry`, takes queues from dapptools with giving users special abilities to interact with the EVM that otherwise is impossible. These abilities are called “cheatcodes” - and they are truly OP. You can see a brief overview of them here:

[https://onbjerg.github.io/foundry-book/forge/cheatcodes.html](https://onbjerg.github.io/foundry-book/forge/cheatcodes.html)

This book also has a guide for getting setup with Foundry, if you haven’t already.

enough preamble, lets rival Bezos in wealth
===========================================

First, add `forge-std` as a dependency in your `forge` repo:

    forge install brockelmore/forge-std
    

Create a test contract and add the following:

    import "ds-test/test.sol";
    import "forge-std/stdlib.sol";
    import "forge-std/Vm.sol";
    
    interface IERC20 {
        function balanceOf(address) external view returns (uint256);
    }
    
    contract TestContract is DSTest {
        using stdStorage for StdStorage;
        StdStorage stdstore;
    
        function writeTokenBalance(address who, address token, uint256 amt) internal {
            stdstore
                .target(token)
                .sig(IERC20(token).balanceOf.selector)
                .with_key(who)
                .checked_write(amt);
        }
    }
    

(okay technically this is more than 3 lines of code, but if you flattened out the `stdstore` call the function itself would be 3 lines)

But boom! This function will now let you write the balance of _any_ address for (almost) _any_ token. All you have to do is call something like:

    writeTokenBalance(address(this), address(dai), 1_000_000_000 * 1e18);
    

You are now a billionaire - congrats!

You’re encouraged to stop reading and go try this out. I am now going to go into how this works, why its so cool, and some limitations.

how it works
------------

This will get a bit technical. For you as a user, all you have to know is:

1.  Set the target contract with `stdstore.target(target)`
    
2.  Set the function signature with `.sig(MyContractInterface(target).myFunc.selector)`
    
3.  Set the inputs with `.with_key(an_address_or_uint_or_whatever)`
    
4.  if multiple inputs, just add more `with_key`
    

This is basically a nicer ux for doing a low level `abi.encodeWithSelector`. As a user, just look at what the function takes as input and think “okay param 1 of the function is an address that is read and I want to give this test contract some tokens so I will do `with_key(address(this))`”.

`forge-std` does some magic under the hood that will be explained here. First let me explain what inside `forge` makes this possible.

There are 2 cheatcodes that give us this crazy power. The first is the `record()` cheatcode. This tells forge to _record_ all `SLOAD`s and `SSTORE`s for each address. The second is `accesses(address)`, which takes an address and returns 2 lists, one of the `SLOAD`s that occurred on that address, the other the `SSTORE`s that occurred on that address. With this, the `stdlib` does the following:

        stdstore_vm.record();
        bytes32 fdat;
        {
            (, bytes memory rdat) = who.staticcall(cald);
            fdat = bytesToBytes32(rdat, 32*field_depth);
        }
        
        (bytes32[] memory reads, ) = stdstore_vm.accesses(address(who));
    

This is rather unreadable (sorry), so lets break it down. We tell forge to `record` by making a call to the cheatcode address (`stdstore_vm`). We then make a call to `who` (the specified target), passing in the abi encoded calldata (`cald`). The calldata is constructed from the `.sig(bytes4)` call we did above (i.e. `IERC20(token).balanceOf.selector`) and the keys, or inputs (i.e. `.with_key(address(this))`). This is rather low level, but basically it constructs the data needed to do a low level call to the target, recording the slots read. The stdlib then uses the `accesses` cheatcode to get all the storage slots read.

That was a little complex. Basically, the flow is:

1.  Parameterization of the call you are going to make (`sig` and `with_key` functions)
    
2.  Start recording
    
3.  Make the call to the target smart contract
    
4.  Grab the `SLOAD`s
    
5.  If multiple slots are read, use the `store` cheatcode to set the storage slot to a known magic number and make the call again, checking if its equal to the magic number
    
6.  if it is, we have identified the slot, if not continue iterating thru the reads
    

We have now found the exact storage slot that is used to store information for a particular function call! Now, if the user used `checked_write(value)`, just do a `vm.store(target, slot, value)`, or if they used `find()`, just return the slot to them. And presto-chango, the balance of the user has been updated to the value passed into `checked_write`.

why this is so cool
-------------------

First, it is _guaranteed_ to work for any `view` function that reads a single storage slot & isn’t a packed storage slot. It doesn’t matter if it is a mapping, flat variable, or extremely deeply nested mapping. So if you want to change the admin of a contract, an internal balance in a protocol, or manipulate an oracle price, you can use the stdlib to do so.

For example, take the follow contract as an example:

    contract StorageTest {
        function hidden() public view returns (bytes32 t) {
            bytes32 slot = keccak256("my.random.var");
            assembly {
                t := sload(slot)
            }
        }
    }
    

We would _never_ be able to find the above storage slot without the pattern described above. But with our pattern its extremely easy:

    function testStorageHidden() public {
        uint256 slot = stdstore.target(address(test)).sig("hidden()").find();
        assert(uint256(keccak256("my.random.var")) == slot);
    }
    

We just set the target as the test address, tell the stdlib what signature to use, and call `find`. And since we record the accesses, we are able to find even a custom slot like the hash of “my.random.var”. We could write to it just by swapping out `find` with `checked_write(100)`, writing 100 to the slot.

Sometimes, you may want to write to a specific field of a struct that is in storage. You can accomplish this by specifying the `depth` of the field, i.e:

    struct A {
        uint256 a; // depth 0
        uint256 b; // depth 1
    }
    

Just add a `.depth(1)` to your call to `stdstore` like this:

    stdstore
        .target(address(test))
        .sig(test.MyStructA.selector)
        .depth(1)
        .checked_write(100);
    

And it would write to the `b` field in `struct A`.

limitations
-----------

As mentioned above, when we don’t satisfy the above constraints (`view`, single non-packed slot), we don’t have guarantees that it will work. For most standard ERC20s, this isn’t an issue. Most of the time if it doesn’t work, it won’t _silently_ not work. It detects most errors and will revert. If it doesn’t work for a particular token, you can tap into the record + accesses yourself and try to find the slot yourself. Then just use the `store` cheatcode and you are off to the races.

closing thoughts
----------------

This pattern is crazy powerful, not just for mainnet forks, but also generally in your tests. I personally use it a ton for giving test “users” (i.e. addresses that interact with my contracts via the `prank` cheatcode) tokens, instead of transferring from another address (to guarantee I don’t run out of tokens). Additionally, as mentioned before, the `record`, `accesses(address)`, `load`, and `store` cheatcodes are incredible tools for writing secure contracts. I use `load` all the time for reading internal/private variable state when testing.

Thanks for reading. Build shit, make money

Brock

---

*Originally published on [Brock](https://paragraph.com/@brocke/using-foundry-to-give-yourself-1bn)*
