Finding Storage Slots
Translating account storage from Solidity to the EVM.
Smart contracts on Ethereum store their state in contract storage, which in the EVM is organized as a uint256 => uint256
mapping. Higher level languages like Solidity, Viper, and Yul map storage variables to key-value pairs in this mapping, known as storage slots, and the assignment of variables in each high level language to slots is determined by the compiler. In this page, we explain how this mapping works in Solidity.
Finding the Storage Layout
For a given smart contract, the storage layout can be determined using Foundry for storage variables or mappings using forge inspect
or cast index
:
Alternatively, you can run solc
using the storageLayout
option:
For any live contract on mainnet, the storage layout is shown at evm.storage.
Storage Layout Rules
The storage layout is determined according to rules specified in the Solidity documentation. To summarize these rules, they are determined by:
Storage variables are assigned slots by their order of appearance in a smart contract. If a contract uses inheritance, then variables are ordered starting from the most base-ward contract according to C3-linearization.
Each slot has size 32 bytes.
New variables are aligned to the start of a slot.
Multiple variables can be stored in a single slot, in which case they are concatenated in right-to-left order without separators.
Mappings, strings, and dynamic arrays are considered to occupy a single slot. If one of these contract variables lies in slot
p
, then:For dynamic arrays:
Slot
p
stores the length.Array data is stored in slots 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.
Example Layout for CryptoPunks
We give a worked example for the CryptoPunks smart contract deployed at:
The state variables and storage slots for this contract are shown below.
Variable | type | bytes | slot | data slot |
---|---|---|---|---|
imageHash | string | 32 | 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 |
|
balanceOf | mapping(address => uint256) | 32 | 11 |
|
Last updated