<aside> 📘 Relates:

Solidity

Solidity layout and access of storage state variables simply explained

Solidity delegatecall usage and pitfalls

Understanding Smart Contract Vulnerabilities

</aside>

The Storage is like a key-value data structure that holds the state variables values of a Solidity smart contract.

Thinking of the storage as an array will help us understand it better. Each space in this storage “array” is called a slot and holds 32 bytes of data (256 bits). The maximum length of this storage “array” is 2²⁵⁶-1, so we can fit a lot of elements into it.

Each state variable declared in the smart contract will occupy a slot depending on its declaration position and its type.

For example, the following StorageLayout contract has only 1 state variable, id , since it’s the first one, it will be stored in slot index 0 of the storage.

contract StorageLayout {
    uint256 public id = 543;
}

Take a look at the following example:

contract StorageLayout {
    uint256 public id = 543;
    uint256 public count;
    address public owner;
}

The state variable id is located at storage slot index 0 because it’s the first declared. count is at slot index 1 because is the second. owner is at slot index 2 because it’s the third declared. And so on.

We could represent it as:

storage[0] = 543; // 0x000000000000000000000000000000000000000000000000000000000000021F
storage[1] = 0;
storage[2] = 0x0;

We see that we are assigning the number 543 to the index 0 of the storage. All the data in the storage is saved as bytes, so, converting the decimal value 543to hexadecimal byte format would be 021f , left padded to 32 bytes, that’s why I’m showing:

0x000000000000000000000000000000000000000000000000000000000000021f

Which is how the 543 number would be represented in storage.

We also see that not assigning a value to a state variable is equivalent to assigning its default or zero value based on the type; for a string type, the zero value would be represented as an empty string. For an address type the value would be represented as the zero address 0x0000000000000000000000000000000000000000 , 40 zeros or 20 empty bytes. For a uint256 it would be a 0, or 64 zeros if represented as bytes. For a bool type the zero value would be represented as false, which is 0x00, one byte long for this type. And so on.

All the values in the storage are saved ABI-Encoded and when retrieving the value using its variable they are decoded automatically.

We could use web3.js to access the storage for any contract directly, doing something like this:

For example, let’s call this contract that is deployed in the testnet Rinkeby at: 0x8Aa5C5B74F35a1cB01631bCA24D995d369670E60