mkdir modify-storage-tutorial
cd modify-storage-tutorial
npm init -y
npm install dotenv hardhat @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle ethereum-waffle ethers chai
echo 'ALCHEMY_API_KEY=XXXXXXXXXX' >> .env
echo '.env' >> .gitignore
require("@nomiclabs/hardhat-waffle");
// read .env file
require('dotenv').config()
// Go to https://www.alchemyapi.io, sign up, create
// a new App in its dashboard, and export its key
// to the environment variable ALCHEMY_API_KEY
const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY;
if (!ALCHEMY_API_KEY) throw new Error("ALCHEMY_API_KEY required");
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: {
compilers: [
// you can add additional versions for your project
{
version: '0.8.9',
},
],
},
defaultNetwork: "hardhat",
networks: {
hardhat: {
forking: {
url: "https://eth-mainnet.alchemyapi.io/v2/" + ALCHEMY_API_KEY,
// specify a block to fork from
// remove if you want to fork from the last block
blockNumber: 14674245,
}
}
}
};
npx hardhat test
npm install @openzeppelin/contracts
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IUSDT is IERC20 {
function getOwner() external view returns (address);
function issue(uint256) external;
}
const { expect } = require("chai");
const { ethers } = require("hardhat");
const usdtAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"
// the slot must be a hex string stripped of leading zeros! no padding!
// https://ethereum.stackexchange.com/questions/129645/not-able-to-set-storage-slot-on-hardhat-network
const ownerSlot = "0x0"
it("Change USDT ownership", async function () {
const usdt = await ethers.getContractAt("IUSDT", usdtAddress);
const [signer] = await ethers.getSigners();
const signerAddress = await signer.getAddress();
// storage value must be a 32 bytes long padded with leading zeros hex string
const value = ethers.utils.hexlify(ethers.utils.zeroPad(signerAddress, 32))
await ethers.provider.send("hardhat_setStorageAt", [usdtAddress, ownerSlot, value])
expect(await usdt.getOwner()).to.be.eq(signerAddress)
})
npx hardhat test test/ChangeUSDTOwner.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
const usdtAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"
// the slot must be a hex string stripped of leading zeros! no padding!
// https://ethereum.stackexchange.com/questions/129645/not-able-to-set-storage-slot-on-hardhat-network
const ownerSlot = "0x0"
it("Mint USDT", async function () {
const usdt = await ethers.getContractAt("IUSDT", usdtAddress);
const [signer] = await ethers.getSigners();
const signerAddress = await signer.getAddress();
// storage value must be a 32 bytes long padded with leading zeros hex string
const value = ethers.utils.hexlify(ethers.utils.zeroPad(signerAddress, 32))
await ethers.provider.send("hardhat_setStorageAt", [usdtAddress, ownerSlot, value])
expect(await usdt.getOwner()).to.be.eq(signerAddress)
const amount = 1000
const before = await usdt.totalSupply()
await usdt.issue(1000)
const after = await usdt.totalSupply()
expect(after - before).to.be.eq(amount)
})
npx hardhat test test/MintUSDT.js
keccak256(padZeros(userAddress) . mappingSlot)
function getSlot(userAddress, mappingSlot) {
return ethers.utils.solidityKeccak256(
["uint256", "uint256"],
[userAddress, mappingSlot]
)
}
async function checkSlot(erc20, mappingSlot) {
const contractAddress = erc20.address
const userAddress = ethers.constants.AddressZero
// the slot must be a hex string stripped of leading zeros! no padding!
// https://ethereum.stackexchange.com/questions/129645/not-able-to-set-storage-slot-on-hardhat-network
const balanceSlot = getSlot(userAddress, mappingSlot)
// storage value must be a 32 bytes long padded with leading zeros hex string
const value = 0xDEADBEEF
const storageValue = ethers.utils.hexlify(ethers.utils.zeroPad(value, 32))
await ethers.provider.send(
"hardhat_setStorageAt",
[
contractAddress,
balanceSlot,
storageValue
]
)
return await erc20.balanceOf(userAddress) == value
}
async function findBalanceSlot(erc20) {
const snapshot = await network.provider.send("evm_snapshot")
for (let slotNumber = 0; slotNumber < 100; slotNumber++) {
try {
if (await checkSlot(erc20, slotNumber)) {
await ethers.provider.send("evm_revert", [snapshot])
return slotNumber
}
} catch { }
await ethers.provider.send("evm_revert", [snapshot])
}
}
it("Change USDC user balance", async function() {
const usdc = await ethers.getContractAt("IERC20", usdcAddress)
const [signer] = await ethers.getSigners()
const signerAddress = await signer.getAddress()
// automatically find mapping slot
const mappingSlot = await findBalanceSlot(usdc)
console.log("Found USDC.balanceOf slot: ", mappingSlot)
// calculate balanceOf[signerAddress] slot
const signerBalanceSlot = getSlot(signerAddress, mappingSlot)
// set it to the value
const value = 123456789
await ethers.provider.send(
"hardhat_setStorageAt",
[
usdc.address,
signerBalanceSlot,
ethers.utils.hexlify(ethers.utils.zeroPad(value, 32))
]
)
// check that the user balance is equal to the expected value
expect(await usdc.balanceOf(signerAddress)).to.be.eq(value)
})
npx hardhat test test/ChangeBalanceOf.js
contract LendingPoolAddressesProviderRegistry is ... {
mapping(address => uint256) private _addressesProviders;
address[] private _addressesProvidersList;
...
function getAddressesProvidersList()
external
view
returns (address[] memory)
{ ... }
function getAddressesProviderIdByAddress(
address addressesProvider
)
external
view
returns (uint256)
{ ... }
...
interface ILendingPoolAddressesProviderRegistry {
function getAddressesProvidersList() external view returns (address[] memory);
function getAddressesProviderIdByAddress(address addressesProvider) external view returns (uint256);
}
npx hardhat console
const target = await ethers.getContractAt("ILendingPoolAddressesProviderRegistry", "0x52D306e36E3B6B02c153d0266ff0f85d18BCD413")
await target.getAddressesProvidersList()
[
'0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5',
'0xAcc030EF66f9dFEAE9CbB0cd1B25654b82cFA8d5'
]
await ethers.provider.getStorageAt(target.address, "0x0")
await ethers.provider.getStorageAt(target.address, "0x1")
await ethers.provider.getStorageAt(target.address, "0x2")
0x000000000000000000000000b9062896ec3a615a4e4444df183f0531a77218ae
0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000002
await ethers.provider.send(
"hardhat_setStorageAt", [
target.address,
// the slot must be a hex string stripped of leading zeros! no padding!
// https://ethereum.stackexchange.com/questions/129645/not-able-to-set-storage-slot-on-hardhat-network
"0x2",
// storage value must be a 32 bytes long padded with leading zeros hex string
ethers.utils.hexlify(ethers.utils.zeroPad(3, 32))
]
)
await target.getAddressesProvidersList()
[
'0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5',
'0xAcc030EF66f9dFEAE9CbB0cd1B25654b82cFA8d5',
'0x0000000000000000000000000000000000000000'
]
const arraySlot = ethers.BigNumber.from(ethers.utils.solidityKeccak256(["uint256"], [2]))
const elementSlot = arraySlot.add(2).toHexString()
const value = "0xDEADBEEF"
const value32 = ethers.utils.hexlify(ethers.utils.zeroPad(value, 32))
await ethers.provider.send(
"hardhat_setStorageAt", [
target.address,
elementSlot,
value32,
])
[
'0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5',
'0xAcc030EF66f9dFEAE9CbB0cd1B25654b82cFA8d5',
'0x0000000000000000000000000000000000000000'
]
for (uint256 i = 0; i < maxLength; i++) {
if (_addressesProviders[addressesProvidersList[i]] > 0) {
activeProviders[i] = addressesProvidersList[i];
}
}
return activeProviders;
const deadBeefSlot = ethers.utils.solidityKeccak256(
["uint256", "uint256"],
[0xDEADBEEF, 1]
)
await ethers.provider.send(
"hardhat_setStorageAt",
[
target.address,
deadBeefSlot,
ethers.utils.hexlify(ethers.utils.zeroPad(1, 32))
]
)
await target.getAddressesProvidersList()
[
'0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5',
'0xAcc030EF66f9dFEAE9CbB0cd1B25654b82cFA8d5',
'0x00000000000000000000000000000000DeaDBeef'
]
npx hardhat run scripts/ChangeAaveAddressProviderList.js