22

Given a contract, how can I find out the current value of one of its variables (as a dev, not from the context of another contract), if it's not public?

2 Answers 2

18

If you know the location in storage, you can use web3

eth.getStorageAt(address, location)

The variables are generally sequential, the first variable declared is in position 0, the second in position 1, etc.

For dynamic arrays, their position contains the length of the array, and the data starts at sha3(position)

1
  • 3
    I guess this no longer works, now it accepts some other parameter called key , it is not clear wheter its a variable name or what
    – Nulik
    Commented May 14, 2020 at 14:10
1

With web3.eth.getStorageAt you can read the complete storage of any contract externally (off-chain). But I want to make it clear - it is not as simple, as calculating the slots location could be tricky.

getStorageAt returns a storage slot, which is 32 bytes. And to read the variables data, you indeed have to know the order in which the variables are declared in a contract, but also you need to know the data types. As the variables of the fixed size could be stored in a single storage slot if they occupy <= 32 bytes.

For example, if your contract would be:

address private a;
bool    private b;

Reading the first slot web3.eth.getStorageAt("0x501...", 0) would return not just the address a but also the value for b.

The address requires 20 bytes. The bool: 1 byte. That is why the second variable is not stored at the second slot, but is placed into the first one, as the first slot has enough storage space left: 32bytes - sizeof(address) = 12bytes.

This means, you need to know the slot number for a variable, but also the size and offset.

address private a; // slot 0, size 20, offset 0
bool    private b; // slot 0, size 1,  offset 20

Things get more complicated with arrays, mappings, strings, and dynamically sized types.

For example, for arrays:

  • Fixed length: each item as a single variable:
// slot 0 for item0
// slot 1 for item1
// slot 2 for item2
address[3] private a; 

// slot 3
address private b;
  • Dynamic length:
// slot 0 for current array length
// each item at specific index is accessed under the slot: keccak256(encodePacked(0)) + index
address[] private a; 

// slot 1
address private b;
  • Multiple slots per item in dynamic arrays
struct Data {
    address user;
    uint256 balance;
}
// slot 0 for current array length
// 2 slots per item
// a[0].user would be   : keccak256(encodePacked(0)) + 0 * 2 + 0 
// a[0].balance would be: keccak256(encodePacked(0)) + 0 * 2 + 1

// a[1].user would be   : keccak256(encodePacked(0)) + 1 * 2 + 0 
// a[1].balance would be: keccak256(encodePacked(0)) + 1 * 2 + 1
Data[] private a; 

// slot 1
address private b;

Another caveat could be inheritance:

contract A {
    // slot 0
    address private a;
}
contract B is A {
    // slot 1
    address private b;
}

This could be indeed very complicated manually to count the positions and offsets, that's why I use 0xweb auto-generated classes to access private storage of contracts, which are validated at Etherscan and co. The tool generates TypeScript class to call functions (read/write) of the contract, but also it generates a storage reader class, so you can access all variables by name, for example - if your contract is validated at Etherscan, you could generate the TypeScript class as:

0xweb i 0x501... --name MyContract --chain eth
import { MyContract } from '@0xweb/eth/MyContract/MyContract'

let contract = new MyContract();
await contract.storage.a();
await contract.storage.z();
// or the first item of the array
await contract.storage.list(0);
// mapping
await contract.storage.people('0xFF01'/* bytes32 */ );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.