Bytecode#

Now that we have the EVM, we can talk a little more about the Bytecode. Bytecode is executed by the EVM.

Valid Bytecode is simply a list of valid opcodes and their operands. We call a list of opcodes and their operands a program.

Simple Push#

The program that pushes 0x42 on the stack would look like this.

SIMPLE_PUSH = [0x60, 0x42]

or simply: 0x6042

Instructions and Data#

As you can see, the bytecode consists of both instructions and data. It is up to the program writer to make sure that data and instructions are not mixed up.

This is what we call the von Neumann architecture.

EVM Versions#

The EVM is not static. It evolves over time through hard forks - coordinated upgrades that add new opcodes or change existing behavior.

Major Ethereum Forks#

Fork

Date

Key Changes

Frontier

Jul 2015

Original EVM

Homestead

Mar 2016

Added DELEGATECALL

Byzantium

Oct 2017

Added REVERT, STATICCALL

Constantinople

Feb 2019

Added CREATE2, SHL, SHR, SAR

Istanbul

Dec 2019

Added CHAINID, SELFBALANCE

London

Aug 2021

Added BASEFEE

Shanghai

Apr 2023

Added PUSH0

Cancun

Mar 2024

Added TLOAD, TSTORE

Prague

May 2025

Added AUTH, AUTHCALL

For more up-to-date information, check the Ethereum History page.

Bytecode Compatibility#

Different EVM versions support different opcodes. For example:

  • PUSH0 (added in Shanghai) creates more efficient bytecode than PUSH1 0x00

  • CREATE2 (added in Constantinople) enables deterministic contract addresses

  • TLOAD/TSTORE (added in Cancun) provide transient storage

Our mini-EVM implements the Cancun version with all modern opcodes.

Simple Add#

Another simple program could look like this. Here we push 0x42 and 0xFF on the stack and add them together and put the result back onto the stack.

SIMPLE_ADD = [0x60, 0x42, 0x60, 0xFF, 0x01]

After executing this we should have a stack with exactly one element 321 on it.

In the next step we will use the EVM that we built to actually execute these programs.