Understand Solidity Storage in depth
The slots in storage are stored as key-value pairs with a length of 256 bits (32 bytes). The default value of each slot is always 0, so we do not need to assign a value to 0 when the new declaration.
Basically, we can barely understand that variables in a contract will be stored in storage as follows:
- Storage only stores variables, not constant
- Each slot can store up to 256 bits (32 bytes)
- The variables in turn are in slot in the order of lower-order (ie from right to left).
- If the size of the variable exceeds the remaining size of the slot, this variable will be passed to the new slot.
- Struct creates a new slot, the struct elements are put into the slot in the same way as above.
- Fixed size array creates a new slot, struct elements are put into the slot in the same way as above.
- Dynamic size array creates a new slot, this slot only stores the length of the array, while the values in the array will be stored at other locations (we will talk more in detail later)
- Mapping always creates a new slot to hold a place, the values in the array will be stored in other locations, we will talk more in detail later.
- String creates a new slot, this slot stores both data & data length, we will talk more in detail later.
Going into practice, we will see that the combination of data types becomes much more complicated.
You may not know:
- All variables in the contract are stored in storage and are accessible
- Regardless of whether you have declared the variable
private
orinternal
not, it only works within the scope of the contract only, on the blockchain are all public. Of course, depending on the data type, there will be different encryption, but basically nothing is private at all.
The fixed size variables are single variables, using the basic data types such as
uint8, uint16, uint24, uint32, uint64, uint128, uint256, bool, address
we have a sample contract:contract StorageTest {
uint256 a;
uint256[2] b;
struct Entry {
uint256 id;
uint256 value;
}
Entry c;
}
The variables will be stored in storage as follows:
a
is stored in slot 0 and it occupies this slot due to the size ofa
256 bits (uint256)b
occupies a new slot (slot 1) which contains 2 elements. Because the data type of b isuint256
, each element will occupy a slot. This means that b [0] will occupy slot 1, and b [1] will occupy slot 2- Struct Entry has just been declared, it does not count
c
will occupy a new slot (slot 3) and then put 2 elements in the struct in. Just likeb
the above, every element isuint256
so each will occupy a slot. The result isc.id
occupied slot 3 andc.value
occupied slot 4
Another example:
pragma solidity ^0.5.9;
contract StorageTest {
uint8 public a = 7; //0
uint16 public b =10; //0
address public d = 0xbE03bCA4D65A86114668697867982Ecfc76f15F8; //0
bool public bb = true; //0
uint64 public c = 15; //0
uint256 public e = 200; //1
uint8 public f = 40; //2
uint256 public g = 789; //3
}
In this contract the variable is not full-slot size as before, so how is it arranged in storage?
a
8 bit size, put into slot 0b
16 bit size, put into slot 0d
is the address. The address has 40 hexadecimal characters, so 160 bitsbb
is bool type, only need 1 bit to store, but in solidity, the smallest data type is 8 bits, so bool will also occupy 8 bits; we put it into slot 0c
64 bit, we still can put it into slot 0 because (a+b+d+bb+c = 256 bit)e
256 bit, because slot 0 has been filled by (a,b,d,bb,c) with 256 bit, so e will be stored in slot 1f
8 bit, f has to be stored in slot 2 because of a full of 256 bit ofe
g
256 bit, put in slot 3
How do we verify this? We will deploy to a testnet network (here I use Ropsten) and then get the data to try.

Slot 0:
truffle(ropsten)> web3.eth.getStorageAt('0x9129970dce1274D3D6FB94B705F21eaAe8fABF1F', 0)
'0x000000000000000f01be03bca4d65a86114668697867982ecfc76f15f8000a07
So, as I've described earlier. It's stored from right to left in Hexadecimal, let's dive right into the analysis
0x000000000000000f01be03bca4d65a86114668697867982ecfc76f15f8000a070x000000000000000f01be03bca4d65a86114668697867982ecfc76f15f8000a07
a
is 7 = 07 (Hex to Dec)b
is 10 = 0a (10 hex to Dec is a)c
is 0xbE03bCA4D65A86114668697867982Ecfc76f15F8d
is True = 01bb
is 15 = 0ftruffle(ropsten)> web3.utils.fromDecimal("0xbe03bca4d65a86114668697867982ecfc76f15f800")
'0xbe03bca4d65a86114668697867982ecfc76f15f800'
Slot 1:
e
200 = c8 (Hex to Dec)truffle(ropsten)> web3.eth.getStorageAt('0x9129970dce1274D3D6FB94B705F21eaAe8fABF1F', 1)
'0x00000000000000000000000000000000000000000000000000000000000000c8'
Slot :2
f
40 = 28 (Hex to Dec)truffle(ropsten)> web3.eth.getStorageAt('0x9129970dce1274D3D6FB94B705F21eaAe8fABF1F', 2)
'0x0000000000000000000000000000000000000000000000000000000000000028'
Slot 3:
g
789 = 315 (Hex to Dec)truffle(ropsten)> web3.eth.getStorageAt('0x9129970dce1274D3D6FB94B705F21eaAe8fABF1F', 3)
'0x0000000000000000000000000000000000000000000000000000000000000315'
Dynamic arrays always occupies a new slot, and this slot will store the length of the array.
Assuming there is an X array, the element in the dynamic array will be stored sequentially from
keccak256(X)
, one important note X is the string of 64 hexa (256 bit) characters, not the uint values.pragma solidity ^0.5.9;
contract StorageTest {
uint256 a; // slot 0
uint256[3] b; // slots 1-2-3
struct Entry {
uint256 id;
uint256 value;
}
Entry c; // slots 4-5
uint256[] d;
Entry[] e;
constructor () public {
d.push(1);
d.push(2);
d.push(3);
e.push(Entry(4, 5));
}
}
Length of
d
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",6)
'0x0000000000000000000000000000000000000000000000000000000000000003'
Value of `d[0]`
lengthof6="0x0000000000000000000000000000000000000000000000000000000000000006"
d0=web3.utils.soliditySha3(lengthof6)
'0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f'
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",d0)
'0x0000000000000000000000000000000000000000000000000000000000000001'
Value of
d[1]
= d0 + 1Convert
d0
to Decimal to make a summation operation111414077815863400510004064629973595961579173665589224203503662149373724986687 + 1 = 111414077815863400510004064629973595961579173665589224203503662149373724986688
Decimal of
d[1]
is 111414077815863400510004064629973595961579173665589224203503662149373724986688The address of
d[1]
should be presented in Hexadecimald1 = "0xF652222313E28459528D920B65115C16C04F3EFC82AAEDC97BE59F3F377C0D40"
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",d1)
'0x0000000000000000000000000000000000000000000000000000000000000002'
Similar to
d[2]
, d[2] = d[1] + 1111414077815863400510004064629973595961579173665589224203503662149373724986688 + 1 = 111414077815863400510004064629973595961579173665589224203503662149373724986689
Decimal of
d[1]
is 111414077815863400510004064629973595961579173665589224203503662149373724986689d2="0xF652222313E28459528D920B65115C16C04F3EFC82AAEDC97BE59F3F377C0D41"
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",d2)
'0x0000000000000000000000000000000000000000000000000000000000000003'
Check length of
e
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",7)
'0x0000000000000000000000000000000000000000000000000000000000000001'
Value of
e[0].id
truffle(ropsten)> lengthof7="0000000000000000000000000000000000000000000000000000000000000007"
'0000000000000000000000000000000000000000000000000000000000000007'
truffle(ropsten)> e0=web3.utils.soliditySha3(lengthof7)
'0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688'
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",e0)
'0x0000000000000000000000000000000000000000000000000000000000000004'
Value of
e[0].value
e[0].value = e[0].id + 1
0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688 + 1 = 0xA66CC928B5EDB82AF9BD49922954155AB7B0942694BEA4CE44661D9A8736C689
truffle(ropsten)> e1="0xA66CC928B5EDB82AF9BD49922954155AB7B0942694BEA4CE44661D9A8736C689"
'0xA66CC928B5EDB82AF9BD49922954155AB7B0942694BEA4CE44661D9A8736C689'
truffle(ropsten)> web3.eth.getStorageAt("0x22a77C59ab77B431E5c7236A531D032551cc0aD5",e1)
'0x0000000000000000000000000000000000000000000000000000000000000005'
Eg:
bool public contact;
bytes32[] public codex;

Mapping always takes up a new slot, but this slot doesn't store any value at all!
We have a contract which comprises the mapping
f
pragma solidity ^0.5.9;
contract StorageTest {
uint256 a; // slot 0
uint256[2] b; // slots 1-2
mapping (uint256 => uint256) f; //slot 3
constructor () public {
f[20] = 8;
f[567] = 133;
}
}
As we can straightforwardly realize that the mapping
f
allocated in slot 3f
will occupy slot 3f[20]
will occupy slotkeccak256(hex(20) + hex(7))
f[567]
will occupy slotkeccak256(hex(567) + hex(7))
After an above contract deployed, we check the information of the contract as follows
Last modified 1yr ago