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.
- 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.
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.
→ Step 2: give zero its own instruction.
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.
→ Step 3: multiply by 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.”