Solidity's Storage And Memory Layout
We have understood what the storage and memory are. Persistent and temporary, respectively. Now we are going to take a look at how these two data storage locations are laid out.
Storage
Solidity's storage layout has a finite amount of space, which is broken down into 32-byte groups called slots
, and each slot can hold a 256-bit value. These slots start from index 0 and can stretch to an index limit of (2^256) - 1
. It is safe to say that the storage can never get full. Cool, isn't it?
Values stored in storage slots are stored as bytes32
values, and sometimes, can be packed, as we will see later on in this book. To retrieve the value of a Solidity storage variable, the 32-byte value stored in the corresponding slot is retrieved. In some cases - when the value in the slot has been packed -, the value retrieved is worked on by methods of shifting or masking to retrieve the desired value.
💡 Remember when we said one should know how the EVM works before moving on with Yul? Yeah, that's one of the reasons why. You cannot retrieve what you do not know how it was stored.
Memory
Solidity's memory layout, unlike the storage layout is quite tricky. While the storage has a defined maximum slot
index of
(2^256) - 1
that can hold 32-byte values, the memory is a large group of 32-byte slots that their data can not
be retrieved by passing a slot index. But instead, data stored in the memory are retrieved by picking
a particular location and returning a specific number of bytes from that point in the memory.
"Why?", you may ask, it is because the default number of bytes returned from any point in memory is 32
and in
cases where the point started from is the middle of a particular 32-byte slot, it will encroach into the next slot.
You can imagine memory slots as laid out end to end, in a way that data retrieval can be started from any point and stopped at any point. Unlike storage that returns only the 32-bytes stored at an index, nothing more, nothing less.
If you do not understand that, do not sweat it. You will get a better grasp of it when we talk about variable storage in memory section.
These positions in memory start from 0x00
and are in groups of 32-bytes, meaning that the slots are in th is way:
0x00
- 0x1f
0x20
- 0x3f
0x40
- 0x5f
0x60
- 0x7f
...
0xm0
- 0xnf
According to the Solidity Docs, there are some reserved memory slots for some purposes.
0x00
- 0x3f
(64 bytes): Scratch space for hashing methods.
0x40
- 0x5f
(32 bytes): Currently allocated memory size (aka. free memory pointer).
0x60
- 0x7f
(32 bytes): Zero slot.
Scratch space can be used between statements (i.e. within inline assembly). The zero slot is used as initial value for dynamic memory arrays and should never be written to (the free memory pointer points to 0x80 initially) [3].
💡 Position
0x40
always holds the next free memory location.
💡 It is safest to use
mload(0x40)
to get the next free memory pointer when trying to store data to memory as storing in a memory location with existing data overwrites that location.
💡 The positions and values in memory that we will use over the course of this book to access memory points will be written in hexadecimals (
0x**
) as they are easier to read, since the EVM already deals in hexadecimals.