Testing dApps on Ethereum Mainnet
This article will show how you can fork the Ethereum mainnet to simulate contract calls for testing without using real ETH.
While testing apps on testnets like Goerli, every developer faces this one big issue at least once - no access to tokens or smart contracts already deployed to the mainnet. So, these developers waste time manually deploying these specific contracts on the testnet instead of focusing on developing their app.
But what if I tell you there's a way to test your apps on the Ethereum mainnet without using real Ethereum funds? Yes, that's completely possible by using mainnet forking, and this article will look at how to do that. This article is for someone making an app dependent on already-deployed smart contracts on the mainnet.
Mainnet forking
Before we get into the action, we must know what mainnet forking is. Since you cannot do testing on the real mainnet because you might need to pay hefty gas fees, you can set up your own blockchain, which mimics the Ethereum mainnet when it comes to all the past data.
Imagine that you copy the Ethereum mainnet into your system and use it as your private testnet with dummy funds and full control over the blockchain. This will include all past data, such as the current state of blockchain, all previously deployed smart contracts, and all balances for different wallets.
Now, downloading an entire copy of the Ethereum mainnet can prove fatal to your computer system because the size of the download might be way too high. So, we use tools like Hardhat or Ganache to fork the mainnet. These set up a blank Ethereum blockchain for us. Whenever we request details such as transactions for a wallet, these tools fetch only the necessary data from the real Ethereum mainnet and return the data into our empty blockchain. This way, you don't need to download the entire blockchain, and you can still do testing on the data you need from the Ethereum mainnet.
In this article, we will be using Hardhat to fork the mainnet. Ganache has been throwing errors for me and is quite slow compared to Hardhat.
Setting up our mainnet fork
Creating a hardhat project
To create our own fork of the mainnet, we first need to create a hardhat project to use our configuration for the fork. Navigate to a safe directory, and run the following command in the terminal to fire up the hardhat project creation wizard:
npx hardhat
This should show a wizard like the following:
I tried to test this by choosing the option "Create an empty hardhat.config.js
", but it doesn't seem to work for me. So, choose the option Create a basic sample project.
Then you will be asked to choose the project directory; you can just press Enter to leave it the same and press y to add a .gitignore
file and install the required dependencies.
Editing hardhat configuration
Navigate to your project directory and open hardhat.config.js
in your favourite code editor. We need to make some additions in the module.exports
part where the project configuration is. Add the following configuration to inform hardhat that we intend to fork an existing blockchain:
module.exports = {
solidity: "0.8.4",
networks: {
hardhat: {
forking: {
enabled: true,
url: "<your-rpc-url>",
},
chainId: 1,
},
},
};
Make sure you replace <your-rpc-url>
to any Ethereum mainnet RPC. You can get that from Alchemy. We are setting chainId
as 1
explicitly here because I've experienced issues using MetaMask in the cases when the chainId
wasn't 1
.
Running our mainnet fork
To run the mainnet fork, run the following command in the terminal:
npx hardhat node --network hardhat
This tells hardhat to use the hardhat
network from our hardhat.config.js
, which informs hardhat to fork an existing blockchain, which is the Ethereum mainnet in our case. Now, our forked blockchain should fire up, and you should see details and test private keys for you to use:
NOTE: Do NOT use these private keys for real transactions on the Ethereum mainnet. These are publicly known private keys, and any funds sent to these accounts will be compromised. Keep accounts for real use and play far away from each other!
Adding forked mainnet to MetaMask
Now that our forked blockchain is running let's add it to MetaMask! Go to MetaMask settings > Networks and click on Add Network. Use the following details to add the network:
Note that we get a warning that chain ID 1
is already in use, but we are using it anyways to make our forked mainnet work with MetaMask. You can safely ignore that warning. Click on Save and switch to the Hardhat network on MetaMask.
Use any private keys listed on the terminal to add an account on MetaMask. These wallets have 10000 ETH to play around with by default.
Testing our mainnet fork on Uniswap
Go to the Uniswap app. Make sure you're connected to the hardhat network we recently added to MetaMask.
Try swapping some of your ETH for USDT, for example (if you face gas fee estimation errors, look at the troubleshooting section of this article):
Now, click on Swap. Note that the transaction might take longer than usual because Hardhat will query your RPC provider to get the details about the smart contract from the real mainnet. You will get a MetaMask popup to approve the transaction:
After approving the transaction, you should see your USDT populate in Assets section of your MetaMask wallet:
Now, if you switch back to the real Ethereum mainnet, you won't see the USDT because we just simulated the transaction on our forked mainnet.
You can now use this forked mainnet for your own contracts and interact with contracts already deployed on the Ethereum mainnet for testing purposes. You can check the hardhat documentation to see many things you can do with the forked mainnet.
Troubleshooting
Cannot estimate gas errors
You can receive these errors when just starting the fork or restarting the fork and making a transaction. This is because it's using the nonce for the real mainnet, and you need to reset the nonce. To do that, go to MetaMask settings > Advanced > Reset Account. This won't delete your account from MetaMask, just reset the nonce and remove transaction history. After doing this, your transactions should work normally.
Fork becomes unresponsive
Most of the time, this happens because MetaMask spamming contract requests whenever it is opened. A simple solution should be restarting the mainnet fork.
Conclusion
Mainnet forking can be useful in many scenarios, especially when you're building a token trading app where you need to focus on real tokens such as USDT. You can also use this as your private testnet to closely examine transactions. You can even go back and provide a custom block number to fork from that specific time. I'd recommend playing around with the fork and various options provided by hardhat.
Also, I made a video on this topic on my YouTube channel. Make sure you watch the video and show some support by subscribing; we are close to 1,000 subscribers :)