skip to content
← Go back

Use Foundry fork to read intenal state in Solidity?

Learn to query internal variables of a smart contract easily, using Foundry ⚒️ without calculating storage slots! 🧮

The trick is: vm.etch() cheatcode

Let’s say we have this simple Token contract already deployed

And we want to fetch the value of _decimals, which is an internal variable.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Token is ERC20("Token", "TKN") {
    uint8 internal _decimals;
}

To do so we first clone the contract in our foundry repo

Then add a public function that returns _decimals like this:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ModifiedToken is ERC20("Token", "TKN") {
    uint8 internal _decimals;

    function getDecimals() external view returns (uint8) {
        return _decimals;
    }
}

The last step is to create Test file as follow:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;

import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
import {Token} from "src/Token.sol";
import {ModifiedToken} from "src/ModifiedToken.sol";

contract TokenFetchInternalTest is Test {
    address deployedToken = 0x50ecf50eE63Af22a4bc450366413B6646a23B5AF;

    function setUp() public {
        vm.createSelectFork(vm.envString("MUMBAI_RPC_URL"));
    }

    function testFetchInternalValue() public {
        emit log_bytes(address(deployedToken).code);

        bytes memory code = vm.getCode("ModifiedToken.sol:ModifiedToken");
        vm.etch(deployedToken, code);

        emit log_bytes(address(deployedToken).code);

        ModifiedToken modifiedToken = ModifiedToken(deployedToken);

        console.log(modifiedToken.getDecimals());
    }
}
  1. We fork with RPC URL

  2. Using vm.etch() we can modify the bytecode locally, of an already deployed contract

  3. This allows us to call our new getDecimals() function, which exposes the internal variable’s value.

Another cool use case of vm.etch() is that you can add console.log to existing contracts and run tests

This way you can easily get intermediate values from a txn without debugging through the stack trace!


Happy coding!

Recent Articles