Part III · Chapter 28 of 43 · Shanghai · Capella

EIP-3855: PUSH0 — the opcode for the number everyone needed

Putting the constant zero on the stack is one of the most common things EVM code does — and until 2023 there was no instruction for it. Compilers either spent an extra byte on PUSH1 0 or reached for a hack. PUSH0 is a one-byte opcode that does exactly one thing, and because zero is everywhere, that tiny win adds up across every contract.

Updated Jun 24, 2026 · 7 min
Assumed
  • the EVM stack & opcodes
  • gas & code size

Shanghai, April 2023. Not every consequential upgrade is grand; some just fix a paper cut that every contract has been feeling for years. Here’s the paper cut: putting the number zero on the stack is one of the most frequent operations in all of EVM code — initializing variables, comparisons, zeroing memory, the boilerplate around every function — and the EVM had no opcode for it. There are opcodes to push any 1-to-32-byte constant, but not a clean one for the single most-used constant of all. EIP-3855 adds PUSH0. It’s about as small as an EIP gets, and it’s a nice lesson in why “small × everywhere” is worth a hard fork.

1 step No opcode for zero

The problem: two bad ways to push zero

To get a zero onto the stack, EVM code had two options, and neither was good. The obvious one is PUSH1 0x00 — but that’s two bytes (the PUSH1 opcode plus its 0x00 immediate) and costs 3 gas, spending a byte of precious contract code just to encode a constant the machine could produce for free. So compilers got clever and abused RETURNDATASIZE trick Using the RETURNDATASIZE opcode (1 byte, 2 gas) to push zero, exploiting the fact that returndata is empty (size 0) at points where compilers knew it would be. Smaller than PUSH1 0, but a hack that relies on context, not intent. : a one-byte opcode that happens to return 0 in the right context. It saves a byte, but it’s a hack — you’re reading a value that means “size of the last call’s return data,” relying on it being zero, instead of saying what you mean.

pushing 0 is everywhere — but there was no opcode for it PUSH1 0x00 2 bytes · 3 gas wastes a byte of code RETURNDATASIZE 1 byte · 2 gas a hack: it just happens to be 0 both leave 0 on the stack — but neither is a clean, cheap 'push zero' and 0 is pushed constantly, so the waste is everywhere
Two ways to put zero on the stack, both bad: PUSH1 0x00 spends two bytes and 3 gas, while the RETURNDATASIZE hack is one byte but relies on returndata happening to be empty. Both leave a 0, but neither is a clean 'push zero' — and zero is pushed constantly.

→ Step 2: give zero its own instruction.

2 step Add PUSH0

PUSH0: one byte, one job

PUSH0 The Shanghai opcode 0x5F. It pushes the constant 0 onto the stack. One byte of code, 2 gas (the base tier), and it says exactly what it does. (opcode 0x5F) does precisely one thing: it pushes the value 0. It’s one byte of code and costs 2 gas — smaller than PUSH1 0 and cheaper, and unlike the RETURNDATASIZE trick it’s honest about its intent, so it can’t be broken by a change in context. It slots neatly into the opcode space right before PUSH1 (0x60), completing the “push a constant” family with the zero-length case.

add a dedicated opcode: PUSH0 (0x5F) PUSH0 0x5F 0 the stack 1 byte of code · 2 gas · pushes the constant 0 smaller and cheaper than PUSH1 0, and no hack needed
PUSH0 (0x5F) pushes the constant 0 in a single byte for 2 gas. It's smaller and cheaper than PUSH1 0x00, and it means what it says — no reliance on returndata being empty.

→ Step 3: multiply by everywhere.

3 step Small × everywhere

The payoff: a byte saved millions of times

A single byte of code is nothing; a byte saved on every zero-push in every contract is a lot. Because zero is pushed constantly, swapping PUSH1 0 for PUSH0 measurably shrinks contract bytecode — and code size is the dominant cost of deploying a contract, as well as being capped at 24KB, so smaller code means cheaper deployments and more headroom for complex contracts. Execution gets a touch cheaper too, and compilers can drop the RETURNDATASIZE hack for something that actually says “push zero.”

zero is everywhere, so the savings add up before PUSH1 0 · PUSH1 0 · … after PUSH0 · PUSH0 · … smaller code → cheaper to deploy · more room under the 24KB limit and the RETURNDATASIZE hack can finally be retired
Swapping PUSH1 0 for PUSH0 across a contract shrinks its bytecode — cheaper to deploy, and more room under the 24KB code-size limit. Because zero is pushed everywhere, a one-byte saving compounds across every contract on the chain.
04 Go Deeper Where to take it from here