Cover photo

Stack, Memory and Storage

filosofiacodigo.eth

filosofiacodigo.eth and Cooldev1337

We reject abstraction
We believe in bytecode
Bytecode is only true form
Let’s code with bytes now


Welcome back to Bytecode Tuesday. Last week, we saw how your contract gets compiled, deployed, and executed on Ethereum. This week, we cover something even more fundamental: the three data regions of the EVM.

Let’s recall them from the last post:

  • Stack for last-in, first-out temporary values,

  • Memory for intra-transaction data (a byte array that resets every call), and

  • Storage slots for persistent state (a mapping of 32-byte keys to 32-byte values).

Now let’s break each one down

The Stack: Last-In, First-Out

Think of the stack like a pile of plates. You can only work with the top one. It’s:

  • Last-In, First-Out (LIFO)

  • Can hold up to 1024 items (each 32 bytes)

  • Super fast and cheap to use

Some Relevant Instructions:

PUSH1 VALUE → put VALUE on the stack
Bytecode: 60VALUE // Replace VALUE with your desire value in hexadecimal (1 byte)
PUSH32 VALUE → put VALUE on the stack
Bytecode: 7FVALUE // Replace VALUE with your desired value in hexadecimal (32 bytes)
ADD → pop the top two values, add them, and push the result
Bytecode: 01

Example:

Which translates as

PUSH1 0x02
PUSH1 0x03
ADD

Let’s now see how the stack is being manipulated in the following step by step animation.

post image
Step by step animation of our Stack manipulation example

Memory: The Scratchpad

Memory is like RAM: it’s cleared after each transaction or call. You can use it to:

  • Store temporary values during execution

  • Return data to the caller (via RETURN)

  • Store arrays, strings, etc.

Some Relevant Instructions

MSTORE → store 32 bytes at a memory offset
Bytecode: 52
MLOAD → read 32 bytes from memory
Bytecode: 51

Example:

Which translates as:

PUSH1 0x2A        // Value to store
PUSH1 0x00        // Memory offset
MSTORE             // Save 0x2A at offset 0x00, since MSTORE stores 32 bytes and we just push 1 byte (0x2A), the EVM left-pads the 1-byte value with 31 zero bytes to make it 32 bytes. This padded value is then stored at the specified memory offset (in this case 0x00).
PUSH1 0x20        // Length to return
PUSH1 0x00        // Start offset
RETURN              // Return memory [0x000….2A]

In the following animation notice how the memory operates based on stack inputs.

post image
Memory is byte-addressable, but MSTORE and MLOAD operate in 32-byte chunks.

Storage: The Blockchain's Hard Drive

Each smart contract has its own storage, which is:

  • Persistent: Survives across transactions

  • Expensive: Writing to storage costs significant gas.

  • Private to the contract: One contract cannot directly access another’s storage.

The EVM storage maps data using 32-byte (256 bits) keys and values.

So, the storage can be visualized as a map like:

0x0000000000000000000000000000000000000000000000000000000000000000 → 0x... (32 bytes)
0x0000000000000000000000000000000000000000000000000000000000000001 → 0x... (32 bytes)

Why 32 Bytes?

Ethereum uses 32 bytes words as the native unit for storage and computation. This design aligns naturally with the Keccak-256 hashing, which produces 32 bytes outputs. Since Ethereum relies heavily on this algorithm, for addresses, storage keys, and more, structuring everything around 32-byte chunks makes contract development efficient.

However, this design isn’t without tradeoffs. Storing small values, like a boolean or a single digit, still consumes the full 32 bytes, which means wasted space and higher gas costs. Developers often have to pack and unpack multiple values manually to use storage efficiently, adding complexity on both smart contracts and dApps frontends.

The EVM's 32-byte word size made sense for early Ethereum, especially given its focus on DeFi, tokens, and ownership primitives. But as the platform evolves into a general purpose execution layer, the limitations of this native word size become more apparent. There’s growing interest, including from Vitalik himself, in exploring a more flexible, byte oriented virtual machine that better supports new use cases and broader types of applications.

Some Relevant Instructions:

SSTORE → write value to a storage slot
Bytecode: 55
SLOAD → read from a storage slot
Bytecode: 54

Example:

Which translates as:

PUSH1 0x2A       // value
PUSH1 0x00       // storage key
SSTORE             // storage[0x000…00] = 0x2A, since SSTORE uses a 32-byte key and value, the EVM left-pads the 1-byte value with 31 zero bytes to make it 32 bytes in the same way as 
PUSH1 0x00       // key
SLOAD               // load from storage[0x000...00], as SSTORE, it left-pads the 1-byte key with 31 zero bytes

As you can see, the memory and the state operate very similarly, the main difference is the memory is addressed by byte pointers while the state by 32 byte slots.

post image
Unlike memory, storage persists after execution and is committed to Ethereum’s global state (in the Merkle Patricia Trie).

Stack vs Memory vs Storage

Feature

Stack

Memory

Storage

Lifetime

One instruction

One transaction

Forever

Cost

Very cheap

Medium

Expensive

Access

Only top values

Offset-based

Key-value

Use cases

Math & logic

Return values

Contract state

Hands-on Assignment

Write and check using this EVM Disassembler bytecode that:

  1. Stores the number 42 in memory

  2. Loads it back from memory

  3. Returns it

Bonus: Can you also store it in storage, then load and return it?

Bytecode isn’t magic. It’s just a sequence of stack gymnastics mixed with clever use of memory and storage.

Next week on Bytecode Tuesday, we’ll explore control flow, how to change the flow of execution inside a smart contract. Subscribe now for more weekly bytes and opcodes.


Stack, Memory and Storage