Memory
EVM memory is a byte-addressable, dynamically expanding linear array. Unlike storage (which persists between transactions), memory is ephemeral — it exists only for the duration of a single execution context. This was one of the distinctions I had to be careful to internalize early.
Expansion semantics
Memory starts empty. Any read or write beyond the current size causes automatic expansion:
- The new size is rounded up to the next 32-byte boundary (word-aligned)
- New bytes are initialized to zero
- Expansion has a gas cost (covered in Module 2): where is the word count.
The quadratic term makes large memory allocations progressively expensive. This prevents contracts from allocating gigabytes of memory on every node for free.
Key operations
MSTORE — write 32 bytes to memory
MSTORE pops an offset and a value from the stack, then writes the 32-byte big-endian representation of the value at that offset in memory.
Before: Stack [ ..., 0x2A, 0x00 ] ← 0x00 (offset) is on top
MSTORE: pop offset (0x00), pop value (0x2A)
write 32 bytes of 0x2A at memory[0x00..0x20]
After: Stack [ ... ]
Memory[0x00..0x20] = 0x000...002A
MLOAD — read 32 bytes from memory
MLOAD pops an offset, reads 32 bytes from memory, and pushes the result as a U256.
Before: Stack [ ..., 0x00 ] ← offset on top
MLOAD: pop offset (0x00), read memory[0x00..0x20]
After: Stack [ ..., 0x2A ] ← value loaded from memory
MSTORE8 — write a single byte to memory
MSTORE8 pops an offset and a value, then writes only the lowest byte of the value at the offset.
Before: Stack [ ..., 0xFF, 0x05 ] ← 0x05 (offset) is on top
MSTORE8: pop offset (0x05), pop value (0xFF)
write byte 0xFF at memory[0x05]
After: Stack [ ... ]
Memory[0x05] = 0xFF
Note that MLOAD and MSTORE always read/write exactly 32 bytes, but the offset can be any byte position — it does not need to be word-aligned. Reading from offset 1 reads bytes [1, 33) — a quirk that caught me off guard at first.
A common mistake is assuming MLOAD/MSTORE require 32-byte-aligned offsets. They don't — MLOAD(1) reads bytes [1, 33), overlapping two "words." This matters when reasoning about memory layout.
Memory layout visualization
After MSTORE(0x00, 42) and MSTORE(0x20, 7), memory looks like:
Each row is 32 bytes (one word). MSTORE writes a U256 value starting at the given byte offset.
Yellow Paper Map
In the Yellow Paper, memory is . The memory size function is:
Where f is the byte offset, s is the size in bytes, and is the current word count. Our expansion_size method implements this.
Exercises
- 1.7 — Store and load a U256 value; store individual bytes and verify they appear in the loaded word
- 1.8 — Reading uninitialized memory returns zero; expansion always rounds to 32 bytes; large offsets expand correctly
Run: cargo test memory