Ensure the security of your smart contracts

How to Fork Mainnet for Testing

Author: MixBytes team
Intro
One of the most exciting features of DeFi is interoperability which allows constructing protocols using deployed smart contracts as base blocks for your protocol (e.g. Yearn Strategies). As a matter of fact, interoperability is one of the reasons why DeFi protocols are referred to as legos. Just like Lego blocks, you have to find the right way to fit two DeFi protocols together for specific use cases.
Developers have 2 options for testing smart contracts which integrate with at least one already existing protocol: creating a mock which implements all necessary functions of already deployed smart contract or using mainnet forking for tests. Mocks can mask very dangerous problems because usually they copy only the need function from real contract which can lead to incorrect work of a mock in some cases. That's the reason why we strongly recommend using mocks only if it is crucial, in all other cases you can use mainnet forking.
Example of usage mainnet forking in hardhat
In this article we will discuss how to set your hardhat project for mainnet forking. First of all, you must have an Infura or Alchemy API key to be able to use a RPC node to fork the state of the specific block. After getting an API key from one of the RPC providers you need to change your config file like this:

const CHAIN_IDS = {
  hardhat: 31337, // chain ID for hardhat testing
};
module.exports = {
  networks: {
    hardhat: {
      chainId: CHAIN_IDS.hardhat,
      forking: {
        // Using Alchemy
        url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}`, // url to RPC node, ${ALCHEMY_KEY} - must be your API key
        // Using Infura
        // url: `https://mainnet.infura.io/v3/${INFURA_KEY}`, // ${INFURA_KEY} - must be your API key
        blockNumber: 12821000, // a specific block number with which you want to work
      },
    },
    ... // you can also add more necessary information to your config
  }
}
After adding this to your hardhat.config.js file you can use all necessary information from the specific block. For example, you can impersonate an address and use some tokens from a random address to test your function (it is very useful when you need a rare nft to test one of your functions) or you can call any function from any contract only by adding a need interface to your project. Some tips for working with mainnet fork in hardhat test are presented in the example below.

// Function which allows to convert any address to the signer which can sign transactions in a test
const impersonateAddress = async (address) => {
  const hre = require('hardhat');
  await hre.network.provider.request({
    method: 'hardhat_impersonateAccount',
    params: [address],
  });
  const signer = await ethers.provider.getSigner(address);
  signer.address = signer._address;
  return signer;
};
// Function to increase time in mainnet fork
async function increaseTime(value) {
  if (!ethers.BigNumber.isBigNumber(value)) {
    value = ethers.BigNumber.from(value);
  }
  await ethers.provider.send('evm_increaseTime', [value.toNumber()]);
  await ethers.provider.send('evm_mine');
}
// Construction to get any contract as an object by its interface and address in blockchain
// It is necessary to note that you must add an interface to your project
const WETH = await ethers.getContractAt('IWETH', wethAddress);
Conclusion
Hereby you have learned how to fork the mainnet state for testing and also how to work with already deployed smart contracts via interfaces. More about useful tools for auditors and developers see in our upcoming articles.
Additional readings and tutorials
Other posts