9

currently, I'm practicing solidity. However, I'm a little confused about accessing a private variable in a contract.

For example here;

address private a;
address private b;
mapping (bytes32 => uint) public people;
mapping (bytes32 => mapping(address => uint)) public listOfEmp;
bytes32[] public list;
bytes32 private z;

I can access 'a' with

web3.eth.getStorageAt("0x501...", 0)

How can I access 'z' here? From a different contract.

Thank you

riyoz
  • 431
  • 1
  • 6
  • 15

4 Answers4

7

You can access the storage of your contract even if it's private.

Try this:

web3.eth.getStorageAt("0x501...", 5)

If you want to access the map or array, check this document for layout of state variables: https://solidity.readthedocs.io/en/v0.4.24/miscellaneous.html

By the way, you should always use getProof to validate the value.

Pang
  • 9,564
  • 146
  • 81
  • 122
Peter Lai
  • 279
  • 2
  • 4
4

Think of Ethereum as a process running on your machine or remotely. Using web3.eth.getStorageAt you read data from the process memory. In the same way you can read the data of every program on your computer.

On other hands, high level programming languages like Java, C++ or Solidity frequently define access rules on variables and functions (private, protected, etc). But these rules only hold in the context of the program execution. For Solidity that context is the execution of transaction.

It means that the private field is private only for other contracts trying to read it. But can be read by external (and pretty low-level) API's like web3.eth.getStorageAt.

leonprou
  • 4,638
  • 3
  • 21
  • 27
0

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 the 1. variable at position 0 (the a in your example), and the 6. variable at position 5 (the z in your example).

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, string 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 */ );
tenbits
  • 7,568
  • 5
  • 34
  • 53
-6

I don't believe you can. A private variable is meant to only be used within the contract in which it is defined. See here: http://solidity.readthedocs.io/en/v0.4.21/contracts.html

  • You are talking about internal variables. A private variable can also be used in inherited contracts. – AlexAcc Oct 15 '21 at 08:13