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 slot p, 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 at keccak256(h(k) || p), where:

        • h(k) pads k to 32 bytes if k is a value type

        • h(k) is the unpadded keccak256 hash for strings or byte arrays

    • For bytes/string (string is viewed as bytes1[]):

      • If length is 32 or more bytes, slot p stores length * 2 + 1 and data is in keccak256(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;

Variable
type
size (bytes)
slot
data slot

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