PUSH0 opcode: A significant update in the latest solidity version 0.8.20

PUSH0 opcode: A significant update in the latest solidity version 0.8.20
image source: https://www.vectorstock.com/

Solidity just released its newest version of Solidity, 0.8.20.

And, as always, along with it comes quite a few new changes, improvements, bug fixes, etc.

However, there is one imperative update in this version that you as a smart contract developer must be aware of.

That is - The inclusion of a new opcode called PUSH0.

Wolf celebrating the new PUSH0 opcode 

A quick important note from the Solidity team’s official announcement blog on PUSH0:

The new compiler switches the default target EVM version to Shanghai, which means that the generated bytecode will include PUSH0 opcodes.

First things first, what exactly is PUSH0?

PUSH0 opcode is actually fairly simple.

It is an instruction with just one specific job, i.e., to push the constant value ZERO onto the stack. That’s it, trust me.

Although PUSH0 has recently been included with solidity version 0.8.20, its significance has been quite evident since 2021, with the inception of EIP-3855.

In the following sections, we will delve deeper into the significance of this seemingly simple opcode and explore its crucial implications.

What we had so far...

If you take a quick look at the opcodes related to PUSH operations, that we had so far (Instruction 0x60 to 0x7f), you will realize we had opcodes from PUSH1 to PUSH32.

This technically means, we had ways to PUSH any item from 1 byte to 32 bytes onto the stack by using the respective opcode as per our need.

PUSH1  = 0x60, // Place 1 byte item on stack.
PUSH2  = 0x61, // Place 2 byte item on stack.
PUSH3  = 0x62, // Place 3 byte item on stack.
PUSH4  = 0x63, // Place 4 byte item on stack.
PUSH5  = 0x64, // Place 5 byte item on stack.
PUSH6  = 0x65, // Place 6 byte item on stack.
PUSH7  = 0x66, // Place 7 byte item on stack.
PUSH8  = 0x67, // Place 8 byte item on stack.
PUSH9  = 0x68, // Place 9 byte item on stack.
PUSH10 = 0x69, // Place 10 byte item on stack.
....
PUSH31 = 0x7e, // Place 31 byte item on stack.
PUSH32 = 0x7f, // Place 32 byte item on stack.

However, what we didn’t have was an adequate way of pushing ZERO onto the stack.

💡
Why would we want to PUSH ZERO value onto the stack?

In case you are wondering why would we even want to PUSH zero value onto the stack, you should check out EIP3855 for more details on the same as there are many instances where ZERO is required on the stack for imperative operations.

Therefore, in order to push ZERO onto the stack, we had workarounds like:

  • With “PUSH1 00”: using the PUSH1 opcode to push zeroes onto the stack,
  • Using multiple DUP instructions to duplicate zeroes and put them on the stack, etc.

While the workarounds mentioned above did the job, they weren’t adequate enough.

Using "PUSH1 00", for instance, is encoded as two bytes and technically consumes more gas than it should put a zero on the stack.

And using multiple DUP instructions might inflate the contract code size and isn’t an optimized approach.

As previously mentioned, this new opcode might seem quite simple but it does solve all these issues that were being ignored for quite a long.

A part of the EIP3855 documentation says:

To put the “waste” into perspective, across existing accounts 340,557,331 bytes are wasted on PUSH1 00 instructions, which means 68,111,466,200 gas was spent to deploy them.
In practice, a lot of these accounts share identical bytecode with others, so their total stored size in clients is lower, however, the deploy time cost must have been paid nevertheless.

That’s some insane amount of gas wasted for just putting zeros on the stack. 🤯

Enters PUSH0 opcode

Now with the inclusion of PUSH0, we shall be able to solve all these issues at once.

PUSH0 opcode shall allow us to directly push a constant 0 value onto the stack without the need to use multiple DUP instructions or a combination of other opcodes.

With the new PUSH0 opcode, we now have:

  • Adequate mechanism of pushing zero onto the stack,
  • Reduced contract bytecode size as we can replace quite a few opcodes with just PUSH0,
  • Minimizing the use of DUP instructions for duplicating zeros on the stack, etc

Time to Verify

It's Web3 folks, so 👇

Don't trust, Verify.

Let's try to verify all of the above-mentioned details about PUSH0 opcode with an example and see if it actually does the magic. 🪄

We will take this small contract for example,

contract PushZero_Test{
    uint256 public num;

    function set(uint256 _n) public{
        num = _n;
    }
}

Then, let's Compile and Deploy this contract:

  • First, use older versions like 0.8.19 or 0.8.16, etc.
  • Then using the latest version, 0.8.20.

Results? 🤔

  1. Contract Code Size
    ➡️ Contract bytecode size using older version        = ~678 characters
    ➡️ Contract bytecode size using 0.8.20 versions = ~646 characters
  2. Deployment Gas Cost ⛽️
    ➡️ Contract Deployment cost using older versions     = ~61511 gas
    ➡️ Contract Deployment cost using 0.8.20 versions   = ~58909 gas

As a result of this short experiment, we can clearly verify that with the inclusion of PUSH0 opcode, we now have:

  • Reduced contract code size, and
  • Reduced contract deployment gas cost

Hmmm,  the opcode does its magic well. 🧐

Don't skip the experiments.
Try and verify yourself too

☢️ Important Warning for Solidity Devs

Although PUSH0 opcode is now included with the latest solidity compiler, you need to be careful while using it on any other chain than ETH mainnet.

There are still other chains like L2s that don't recognize the PUSH0 opcode. Therefore, if you try to use the latest compiler to deploy your contract on any such chain that doesn't support this opcode, your contract deployment shall fail.

And you might get an error that looks something like this 👇

Failed deployment while using 0.8.20 version on Polygon Mumbai 

Therefore, in such cases, you must ensure that you have selected the correct EVM version. Learn more about selecting the right EVM version to target, HERE.

Alright decipherer, hope that was helpful.

See you later. 👋🏻

Join Decipher with Zaryab today

Let's learn and build better, secure Smart Contracts

Subscribe Now