Forking Ethereum Mainnet: Mint Your Own DAI

Ryan Ghods
Ethereum Grid
Published in
5 min readNov 5, 2019

There are a handful of reasons why you may want to fork Ethereum Mainnet to use on your local developer machine. Chief among them is the ability to test your smart contracts against mainnet chain code and addresses. For example, if your smart contract interacts with external ERC20 tokens or contracts, like DAI or Compound, having a forked mainnet chain to run your test suite against can be incredibly helpful in simplifying your workflow.

One tool, ganache-cli, can extend off of any Ethereum archive node endpoint, directing new requests to itself and letting anything older pass through to the underlying node. It even has a nifty feature to let you unlock any addresses or contracts so you can control them without needing authorization.

This is a compelling alternative to using a public testnet, like Rinkeby, or using your own self-mining developer environment. In these cases, you would have to deploy your own contracts for any existing artifacts you may be utilizing and swap the addresses in your contract.

Now, if you’re sufficiently convinced, let’s walk through forking mainnet for development.

Step 1: Install Ganache CLI

To get started, first install ganache-cli globally:

npm install -g ganache-cli

Once installed, the command we’re looking for is:

ganache-cli --fork NODE_URL

where NODE_URL is the node endpoint you would like to fork, typically http://localhost:8545. No node is running on that port yet, so let’s change that.

Note Added 5/7/2021:
Check out
Hardhat’s mainnet forking as a modern alternative to ganache.

Step 2: Initialize an Active Ethereum Node

Running a node can be done using remote infrastructure or, ideally, by using your own machine.

Infura

Infura is great remote node service that gives you a free allowance of 100,000 requests per day. However, to access chain data greater than 30 minutes old, you have to pay an additional $250 per month for an archive node.

Alchemy

Check out Alchemy as an alternative archive node provider.

Geth

Learn more about geth and syncing your own local node.

Now we will start ganache-cli with:

ganache-cli --fork http://localhost:8545

You should see output similar to this:

As the logs indicate, ganache-cli generates 10 accounts with 100 ETH each for use in your tests. Further down in the terminal output, you should see that you’ve forked off of the node on http://localhost:8545 and that, on the last line, your forked node is now Listening on 127.0.0.1:8546.

The forked chain client is available on port 8546.

Using your new node

Now that your forked node is up and running, let’s give it a try with a test transaction. We’ll use MetaMask to visually interact with the network.

First, change MetaMask’s network to our forked client by clicking on Custom RPC in the network dropdown and inputting http://localhost:8546 and a ChainID of 1.

Connect to a Custom RPC network
Provide the URL of the forked node on port 8546

Now, you can use your already available accounts within MetaMask to interact with the forked network.

If you switch back to your terminal, you can see all the new requests that ganache-cli is responding to:

At this point, you’re ready to develop and test Dapps on your forked network. Just make sure you’re pointing to the relevant port (8546) where appropriate — don’t accidentally test on mainnet!

Note that if you send multiple transactions, then eventually reset the network, you will probably need to reset the nonce for your account in MetaMask by navigating to Setting > Advanced > Reset Account. The nonce is the number of transactions sent from a given address for each network, and if you are changing networks like this, it can throw this number off. A wrong nonce results in rejected transactions, as it must be sequential from your last registered (or pending) transaction.

Unlocking addresses

A handy feature of ganache-cli is the --unlock parameter, where you can unlock any address — including contract addresses! This can be useful in your test suites to get around what would otherwise be limitations.

For example, let’s take a look at getting some DAI for the new accounts ganache-cli has generated for us. This would naturally be helpful if a smart contract we’re developing interacts with DAI. In our test suite we can interact with the contract, check that DAI balances change as necessary, etc.

Edit (2021/02/14): Since this article was written, DAI was upgraded to Multi Collateral DAI and the old DAI is now referred to as SAI (Single Collateral Dai). The example GitHub repo was previously updated with test cases for both contracts, but now that SAI is fully phased out, the example is updated just for the current DAI (MCD).

Minting SAI

This section is outdated, see note above.

If you take a look at the SAI ERC20 contract on Etherscan, you can see that the contract implements a function called mint that adds new supply to an address:

event Mint(address indexed guy, uint wad);
...
function mint(address guy, uint wad) public auth stoppable {
_balances[guy] = add(_balances[guy], wad);
_supply = add(_supply, wad);
Mint(guy, wad);
}

The contract requires auth so not every person can mint SAI for themselves 😄, but with our awesome friend ganache-cli we can just add the address of the contract to --unlock so we can send a transaction to itself, minting SAI for our own accounts.

The command would look like so:

ganache-cli --fork NODE_URL --unlock 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359

The next challenge is that the address doesn’t have any ether, so you cannot actually execute a transaction yet.

Usually we could just send some ether to the address, however the contract doesn’t have a default payable function. It therefore can’t accept any incoming ether.

However, there is a loophole we can use! It turns out you can send ether to any address by setting the intended address as the receipt of a contract selfdestruct (read more about that here if you would like). A contract called ForceSend.sol does exactly that.

All in all, this file is what the code would look like to mint 100 SAI for each of our first 5 accounts in a Truffle test suite on our forked and unlocked mainnet: https://github.com/ryanio/truffle-mint-dai/blob/master/test/sai.js

Minting DAI

Check out the code in this file: https://github.com/ryanio/truffle-mint-dai/blob/master/test/dai.js

Thanks!

Hopefully this tutorial helps simplify your development workflow.

--

--