How do I find storage slots?
Translating account storage from Solidity to the EVM
Storage variables for contracts written in higher level languages like Solidity, Vyper, or even Yul are stored in slots. Slots are keys in account storage, which is a uint256 => uint256
mapping, and their mapping to variables in higher level languages is determined by the language compiler. Here we explain how to find this mapping in Solidity.
Storage Variable Layout
The rules for laying out storage are summarized in the Solidity documentation. For a particular smart contract, the storage layout can be found by using the storageLayout
option in solc
. To summarize:
Slots have size 32 bytes.
Multiple variables can be stored in a single slot, in which case they are concatenated (without RLP, in right-to-left order).
New variables are aligned to the start of a slot.
mappings
,string
, and dynamic array are considered to occupy 32 bytes. If they are in slotp
, then:For dynamic arrays:
Slot
p
stores the length.Array data is stored starting at
keccak256(p)
sequentially, sharing slots if elements are at most 16 bytes.Nested arrays apply this recursively.
For mappings:
Slot
p
is empty.The value of key
k
starts atkeccak256(h(k) || p)
, where:h(k)
padsk
to 32 bytes ifk
is a value typeh(k)
is the unpaddedkeccak256
hash for strings or byte arrays
For bytes/string (
string
is viewed asbytes1[]
):If length is 32 or more bytes, slot
p
storeslength * 2 + 1
and data is inkeccak256(p)
.If length is at most 31 bytes, the lowest order byte stores
length * 2
and the highest order bytes store the raw data.
We give a worked example for the CryptoPunks smart contract.
Contract is at: https://etherscan.io/address/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb#code
The state variables start with:
string public imageHash = "ac39af4793119ee46bbff351d8cb6b5f23da60222126add4268e261199a2921b";
address owner;
string public standard = 'CryptoPunks';
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
uint public nextPunkIndexToAssign = 0;
bool public allPunksAssigned = false;
uint public punksRemainingToAssign = 0;
mapping (uint => address) public punkIndexToAddress;
mapping (address => uint256) public balanceOf;
imageHash
string
32
0
keccak(0)..
owner
address
20
1
standard
string
32
2
name
string
32
3
symbol
string
32
4
decimals
uint8
1
5
totalSupply
uint256
32
6
nextPunkIndexToAssign
uint
32
7
allPunksAssigned
bool
1/8
8
punksRemainingToAssign
uint
32
9
punkIndexToAddress
mapping (uint => address)
32
10
keccak(index . 10)..
balanceOf
mapping (address => uint256)
32
11
`keccak(address . 0..0 . 11) ..
Last updated