Setting Storage Slots in Hardhat

Hardhat provides a "hardhat_setStorageAt" method call on its network. This allows one to set the value of any storage slot in a Hardhat network. Since Hardhat supports mainnet forking, we can now tweak the slots in the mainnet fork. As a use case, it allows us to update our token balances making it possible to experiment with many Defi features. We will be seeing how we can update our balance in the WETH and USDT contracts.

Firstly we will setup a mainnet fork in Hardhat. For this update your hardhat.config like below. I have also pinned a block. Since I like to have a standalone network I have decided to spawn a hardhat node and then run my scripts interacting with the localhost provider. You may proceed as you wish.

We will be first trying to update our balance in the WETH contract https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2. To do this we will first have the identify the slot corresponding to the balance of the user. If you see the code of WETH contract, balances are stored in the balanceOf mapping. The storage slot of a key in a mapping is the keccak256 hash of (key+mapping slot). You can refer here for more details on the storage slot calculation https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays . So first we need to obtain the slot of the balanceOf mapping. balanceOf is the 4th variable and since no packing has been made before, it will be in the 3rd slot. ie. 1st variable - slot 0, 2nd variable - slot 1 and so on.

Now that we have the mapping slot, we can find the slot corresponding to the user's balance like this:

Now the checkerBalanceSlot holds the value of the storage slot. To verify that the storage slot we have calculated is correct, we can compare the values that we get from the balanceOf function call and the eth_getStorageAt method call. If our calculation was correct both must be returning the same value. Keep in mind that the eth_getStorageAt will return the value as a hexString whereas the balanceOf function call will return a BigNumber.

But this would be a bad method to verify due to false positives. If you have an account with 0 balance, then even if the storage slot was calculated wrongly, the value in the storage slot would mostly be 0. Hence comparing these 2 would be of no use. We will see a better way to do this verification a little later.

Changing the balance in WETH contract was easy because we were easily able to find that the balanceOf mapping was in the 3rd slot. But what if we had a little more complicated contract with lots of storage variables in which some variables were packed? Manually calculating the slot of the mapping in such a case would be a time-consuming process. We can let the machine do the work by brute forcing the slot.

For this example we will use the USDT Contract https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7 . You can see that manually finding the slot for the balanceOf mapping is tiresome. So let us devise our brute force approach.

We will take a slot. Check if it is actually the slot corresponding to the balanceOf mapping. If yes stop else take the next slot. For taking slots we will start from 0 and go on till we find the answer or reach a sensibly chosen limit. To verify that the slot that we have found actually corresponds to the balanceOf mapping, we can use the method described above ie. we will take an address and find its balance in 2 ways: by invoking the balanceOf function and by using the eth_getStorageAt method call. But this method suffers from the problem discussed above. So what we will do is we will set the value of the storage slot to a custom value. Then we will use the balanceOf function to retreive the balance. In case the balance returned is same as the value that we have set, that means that we have correctly calculated the slot. But doesn't that mean we will write rubbish in maybe meaningful slots? To avoid this we can make use of another set of features that Hardhat provides: evm_snapshot and evm_revert. Using evm_snapshot we will capture the state before we set the storage slot to rubbish and then after verification, we will use evm_revert to revert to the earlier snapshot.

This is how we get the balanceSlot. Once we have this, everything is similar to the WETH contract.

You can view the full code here: https://github.com/maanas2605/hardhat-setstorage