What is the Ethereum Virtual Machine (EVM)?
If you have ever interacted with a decentralized exchange, minted an NFT, or deposited tokens into a DeFi protocol, you have probably relied on a virtual machine without thinking about it. On Ethereum, that machine is the Ethereum Virtual Machine (EVM).
I find it helpful to start with a simple distinction: Ethereum is the blockchain network, while the EVM is the execution environment that processes smart contract code according to Ethereum’s rules. They work together, but they are not the same thing.
A blockchain needs more than a way to record transactions. It also needs a consistent method for deciding what those transactions actually do. If you send ETH to another address, the network must update the relevant balances. If you swap tokens through a smart contract, it must execute the contract’s logic, check its conditions, and calculate the resulting state changes.
That is where the EVM comes in.
According to the official Ethereum EVM documentation, the EVM executes compiled smart contract bytecode. Ethereum clients implement the protocol’s execution rules, allowing participating nodes to process valid transactions and determine their resulting state.
Think of the EVM as a shared rulebook for computation. Different nodes can run different client software, potentially on different operating systems and hardware, yet they must follow the same execution rules for the same valid blockchain history.
This consistency is fundamental. Imagine one node processing a token swap and deciding that Alice receives 10 tokens, while another independently concludes that she receives 12. Ethereum could not maintain a single agreed-upon state if such differences were allowed to stand.
The EVM’s execution rules help prevent that outcome.
How the EVM works in practice

Let’s walk through a simplified example.
Suppose you interact with a decentralized exchange and want to swap one token for another. Your wallet constructs and signs a transaction, then submits it to the network. Once the transaction is included in a block, Ethereum’s execution process evaluates the transaction and runs the relevant contract code.
That code might check your token balance, calculate the swap output, apply the pool’s pricing rules, and update several storage values. It may also call other contracts along the way.
The EVM executes the instructions needed to carry out those operations. If the transaction completes successfully, the resulting state changes become part of Ethereum’s updated state. If execution reverts, the transaction’s attempted state changes are rolled back, although the sender still pays for the gas consumed.
I would pay particular attention to that last distinction. A transaction can fail to deliver the result you wanted and still cost you ETH. Gas pays for computation, not for success.
The process looks roughly like this:
You submit a transaction
A wallet signs the transaction and broadcasts it
Ethereum processes the transaction
The transaction is included in a block and checked under protocol rules
The EVM executes contract code
Opcodes perform calculations, read data, write state, and make calls
Execution succeeds
Valid state changes are committed.
Execution reverts
Attempted state changes are rolled back; gas is still charged.
This is a simplified model, of course. Ethereum’s full transaction lifecycle includes validation, execution-layer processing, consensus, and other protocol details. But for understanding the EVM itself, the central point is that it executes instructions and determines the transaction’s execution result.
The EVM is deterministic by design
You might wonder how thousands of computers can execute the same smart contract without coordinating every individual instruction in real time.
The key is deterministic execution.
Given the same valid pre-state, transaction, block context, and protocol rules, EVM execution should produce the same result. The result includes the updated state and execution outcome. Ethereum’s protocol specifies the rules clients must implement to achieve this consistency.
That does not mean every transaction always produces the same result regardless of context. A contract can read the block timestamp, sender address, transaction input, or other permitted environmental values. Change those inputs, and the outcome may change. Determinism means the execution result follows from the complete set of relevant inputs rather than from arbitrary differences between nodes.
If you are writing a smart contract, this matters more than it may first appear. You cannot safely depend on a node’s local clock, filesystem, or some external API response magically being available inside EVM execution. External information must enter through an appropriate mechanism, such as a transaction or oracle update.
This separation makes replicated execution possible, but it also places limits on what smart contracts can do directly.
Why the EVM became a standard for smart contracts
The EVM’s influence extends well beyond Ethereum itself. Many blockchain networks support EVM-compatible execution because it gives developers a familiar environment for deploying Solidity contracts and using established tools.
That familiarity can reduce migration costs. A team may be able to reuse parts of its contract code, testing setup, deployment scripts, and developer workflow rather than building everything from scratch.
Still, I would not confuse a familiar development environment with identical behavior. EVM-compatible networks can differ in consensus, fees, available precompiles, block production, security assumptions, and ecosystem infrastructure.
The EVM gives developers a common execution model. It does not make every blockchain interchangeable.
The EVM as Ethereum’s state engine
To understand why the EVM matters, you need to look beyond individual instructions and consider Ethereum’s state.
Ethereum’s state represents information associated with its accounts and contracts. Depending on the account and application, this includes ETH balances, contract code, storage values, and other protocol-defined data. Transactions transform one valid state into another.
A useful way to think about a transaction is as a state-transition request: given the current state and a transaction’s execution context, what should the new state be?
The EVM supplies the execution rules for contract computation within that transition. Ethereum’s broader protocol defines the surrounding rules for processing transactions and blocks.
State transitions and smart contract logic
Imagine a basic token contract. It stores balances for different addresses and allows users to transfer tokens.
When Alice transfers 25 tokens to Bob, the contract must check whether Alice has enough tokens, subtract 25 from her balance, and add 25 to Bob’s balance. If the contract’s rules permit the transfer, the state changes reflect the new balances.
The code may look simple at the Solidity level, but the EVM ultimately executes lower-level instructions that perform the required checks, arithmetic, and storage operations.
This distinction matters because source code is not the same thing as execution. A Solidity statement that looks like a single operation can compile into multiple EVM instructions, some of which access persistent state or incur significant gas costs.
If you are investigating a costly transaction, reading the Solidity function is a good starting point, but it may not tell you the whole story. You may also need to inspect compiled bytecode, execution traces, storage access, and external calls.
Why EVM execution is deterministic
The EVM relies on defined execution semantics. Its instructions have specified behavior, and clients must implement those semantics consistently.
Cryptographic operations, arithmetic, comparisons, jumps, memory access, storage access, and contract calls all follow protocol-defined rules. Gas accounting is part of those rules, too.
This gives developers a predictable computational model, although it does not eliminate bugs in smart contracts or client implementations. A deterministic machine can execute flawed code perfectly consistently.
That is an important distinction: deterministic execution helps the network agree on what the code did; it does not guarantee that the code did what its author intended.
For example, if a contract contains a faulty authorization check, the EVM will not automatically recognize the developer’s intention and correct it. It will execute the instructions according to their defined semantics.
Why does the EVM use 256-bit words?
One detail that often surprises people is that the EVM operates as a stack machine with a maximum stack depth of 1,024 items, and each stack item is a 256-bit word.
Why 256 bits?
The Ethereum documentation explains that this size was chosen partly for convenient handling of 256-bit cryptographic values, including Keccak-256 hashes. It also fits the EVM’s role as a machine for processing blockchain-related data and arithmetic.
A 256-bit word can represent an unsigned integer from 0 to 2256-1. Solidity’s common uint256 type maps naturally to this word size, although Solidity supports narrower integer types as well.
You should not interpret this to mean that every EVM value or every piece of data occupies exactly 256 bits everywhere. Memory is byte-addressable, and ABI-encoded data follows its own layout rules. Storage packing can also allow certain smaller variables to share a storage slot, depending on their types and declaration order.
If you are optimizing a Solidity contract, that distinction is useful. Choosing uint8 instead of uint256 does not automatically reduce storage costs in every situation. The compiler’s generated code, storage layout, and the number of storage operations can matter more than the declared width alone.
For a developer, the practical lesson is simple: understand the EVM’s underlying data model, but evaluate optimizations using actual compiler output and measurements rather than assumptions based on type names.
EVM execution architecture: Stack vs memory vs storage

If you are learning Solidity, few distinctions are more useful than understanding the EVM’s stack, memory, and storage. They all hold data, but they serve different purposes and have different lifetimes.
I would learn these concepts before spending too much time on small gas-saving tricks. If you do not know where your contract’s data lives, it becomes much harder to reason about both its behavior and its cost.
| **Area** | **What it does** | **Lifetime** | **Practical consideration** |
| Stack | Holds values used by EVM instructions | During execution | Limited depth; operations use 256-bit words |
| Memory | Holds temporary, byte-addressable data | During an execution frame | Expands as needed and incurs expansion costs |
| Storage | Holds persistent contract state | Across transactions | Access and update costs can be substantial |
| Transient storage | Holds temporary transaction-level state shared across internal calls | Until the transaction ends | Useful for some temporary coordination and locking patterns |
Stack: where instructions work with values
The EVM uses a stack-based execution model. Instructions push values onto the stack, remove values from it, and perform operations on those values.
For example, an arithmetic instruction can take operands from the stack and place the result back on it. Other instructions use stack values as memory offsets, storage keys, jump destinations, or call parameters.
You do not usually manipulate the EVM stack directly when writing ordinary Solidity. The compiler generates the necessary instructions. However, understanding the stack becomes useful when reading opcode traces, reviewing assembly, or investigating low-level execution behavior.
The stack has a maximum depth of 1,024 items. Exceeding that limit causes execution to fail. The 256-bit word size also means that stack values are not simply arbitrary-length integers.
I would not treat the stack as the main target for everyday gas optimization. Most Solidity developers have more practical gains to investigate in storage access, memory allocation, unnecessary computation, and external calls. Still, the stack explains how the EVM executes instructions under the hood.
Memory: temporary working space
EVM memory is temporary and byte-addressable. A contract can use it to hold intermediate values, construct call data, work with arrays, or prepare return data.
Unlike persistent storage, memory does not remain part of the contract’s state after execution ends. It is associated with an execution frame. An internal or external call can create another frame with its own memory context.
Memory expansion is not free. The EVM charges gas when execution expands the active memory region, with a cost function that grows nonlinearly as more memory is used. That means the cost of expanding memory is not simply a constant fee for every additional byte.
Here is a practical example. Suppose a function constructs a large byte array before passing it to another contract. That array may require memory allocation and copying, both of which can contribute to gas usage.
The right optimization depends on what the function is actually doing. Reducing unnecessary copies may help in some cases, but a developer should not assume that every memory operation is expensive or that a complicated rewrite will necessarily improve gas usage.
Storage: persistent contract state
Storage is where a smart contract keeps persistent state. A token contract, for example, may store balances and allowances. A lending protocol may store collateral positions, debt values, and configuration parameters.
Storage values survive after a transaction finishes. That persistence makes storage essential for most stateful applications, but it also makes storage operations an important consideration for gas.
The exact cost depends on the operation and the current protocol rules. Reading a slot and changing a slot are not equivalent operations, and the gas cost of an SSTORE depends on factors such as the original, current, and new values, as well as relevant transaction context.
This is where I would encourage you to think in terms of the application’s data model. If a function repeatedly reads the same state value, it may be possible to load it once and reuse a local variable. If a function writes state unnecessarily, you may be able to remove redundant updates.
But be careful: caching values or changing storage patterns can affect correctness, reentrancy behavior, and compiler output. Gas optimization should begin with a correct implementation and be verified with tests and measurements.
Transient storage: temporary state across calls
Transient storage, introduced through EIP-1153, adds a transaction-scoped key-value mechanism accessible through TLOAD and TSTORE.
Its defining feature is its lifetime. Transient storage can be shared across internal calls during a transaction, but its values are discarded when that transaction ends. Unlike persistent storage, it does not become part of the contract’s lasting state.
This makes it useful for certain patterns where a contract needs temporary coordination between calls. A reentrancy guard is one example, provided the implementation follows the relevant security requirements and handles call behavior correctly.
I would not treat transient storage as a universal replacement for ordinary storage. If a value needs to be available in a later transaction, transient storage is the wrong place for it. And because transient storage has specific semantics, developers need to understand how it behaves across CALL, DELEGATECALL, and reverting execution frames.
The useful mental model is that memory belongs to an execution frame, transient storage lasts for a transaction, and persistent storage lasts across transactions. Once you understand those lifetimes, many design decisions become easier to reason about.
From Solidity to opcodes: How smart contracts execute
You generally do not write smart contracts by typing EVM opcodes. Most developers use a high-level language such as Solidity, then compile the source code into bytecode that an EVM-compatible runtime can execute.
However, if you want to understand contract deployment, debugging, and gas consumption, you should know what happens between the source code and execution.
The Solidity compilation pipeline
A simplified workflow looks like this:
Solidity source code
Functions, state variables, modifiers, and contract logic
Solidity compiler (solc)
Parsing, semantic analysis, optimization, and code generation
EVM bytecode
Instructions used for contract creation and runtime execution
ABI
Interface description for encoding calls and decoding results
Deployment and execution
The EVM processes the bytecode when the contract is called
The compiler does more than translate Solidity keywords into machine instructions. It checks types and semantics, generates intermediate representations, and may apply optimizations before producing EVM bytecode. The precise pipeline depends on compiler settings and version.
The output commonly includes several artifacts:
- Creation bytecode: the code executed when deploying a contract. It can initialize the contract and return the runtime bytecode that will be stored on-chain.
- Runtime bytecode: the code executed when the deployed contract receives a call.
- ABI (Application Binary Interface): the interface description used by applications and other contracts to encode function calls and interpret return values and events.
- Metadata: information about the compiler and source files, which can help others reproduce compilation and verify deployed code.
The official Solidity documentation explains the compiler and its output formats in more detail.
One distinction worth remembering: the ABI is not the same thing as executable bytecode. It describes how to interact with a contract; it does not, by itself, implement the contract’s logic.
How the ABI connects a function call to bytecode
Suppose you call a function named transfer on a token contract. Your wallet or application needs to encode the function name and arguments into the format expected by the contract.
For a standard external Solidity function, the first four bytes of the call data are typically the function selector. This selector is derived from the Keccak-256 hash of the function’s canonical signature, excluding parameter names and return types. The encoded arguments follow according to the ABI rules.
The contract’s dispatcher code uses the selector to route execution to the appropriate function logic.
This is a useful detail if you are debugging failed calls. A contract interaction can fail because the function does not exist, the arguments are encoded incorrectly, the caller lacks permission, or a condition inside the function reverts. Those are different problems, and the error message or execution trace can help you distinguish them.
There are exceptions and special cases, including fallback and receive functions, proxy contracts, and contracts that handle call data manually. So the selector model is a useful starting point, not a complete description of every possible contract interface.
Opcodes: the EVM’s instruction set
Once Solidity has been compiled, the EVM executes bytecode made up of instructions known as opcodes.
Some opcodes perform arithmetic or comparisons. Others access memory and storage, inspect the execution environment, control program flow, or call another contract.
| **Opcode** | **General purpose** | **Why you might care** |
| ADD | Adds values | Basic arithmetic |
| MUL | Multiplies values | Arithmetic operations |
| MLOAD | Reads from memory | Working with temporary data |
| MSTORE | Writes to memory | Preparing data for later use |
| SLOAD | Reads persistent storage | Accessing contract state |
| SSTORE | Writes persistent storage | Updating contract state |
| CALL | Calls another account or contract | Interacting with external code |
| DELEGATECALL | Executes another contract’s code in the caller’s context | Commonly used in proxy patterns; requires careful security design |
| TLOAD / TSTORE | Reads or writes transient storage | Sharing temporary state within a transaction |
You can explore instruction behavior and gas information in the Ethereum opcode reference.
I would not expect every Solidity developer to memorize the entire opcode list. What matters is knowing enough to investigate a problem. If a function consumes more gas than expected, an execution trace can reveal whether the cost comes from storage writes, memory expansion, hashing, external calls, or repeated computations.
Gas: the cost of computation
Gas measures the computational effort required to execute operations on Ethereum. It is not itself ETH. The transaction fee depends on the gas consumed and the applicable gas price components.
The Ethereum gas documentation explains why this system exists: without a way to limit and charge for computation, a transaction could consume excessive resources or run indefinitely.
Consider a contract function that loops through an array and updates each element. The amount of work grows with the number of elements processed. If the function consumes more gas than the transaction’s available limit, execution runs out of gas and fails.
The network cannot simply let that transaction continue until it finishes. Gas limits bound the work performed within a transaction.
A common misconception is that gas is charged only when a transaction succeeds. In fact, a transaction that runs out of gas or reverts still incurs fees for the work performed, subject to the protocol’s fee rules. State changes from the failed execution are rolled back, but the gas already consumed is not refunded merely because the intended outcome was not achieved.
For developers, this creates a practical balancing act. You want to minimize unnecessary computation and storage operations, but you must not sacrifice correctness or security just to reduce gas.
A more compact function is not automatically a better function. The right goal is efficient, understandable code that behaves correctly under realistic inputs and adversarial conditions.
EVM compatibility vs EVM equivalence

If you have looked at different blockchain ecosystems, you have probably seen networks advertise themselves as “EVM-compatible.” The phrase is useful, but it can mean different things depending on the network and what exactly is compatible.
I would separate two questions:
- Can you reuse familiar Ethereum development tools and deploy Solidity contracts with limited changes?
- Does the target environment reproduce Ethereum’s execution behavior closely enough for the specific application you are moving?
Those questions overlap, but they are not identical.
What EVM compatibility usually means
An EVM-compatible chain supports some meaningful portion of the EVM developer environment. It may accept EVM bytecode, support Solidity contracts, and integrate with familiar wallets, deployment tools, and libraries.
Examples include BNB Smart Chain, Polygon PoS, Avalanche C-Chain, and several Ethereum layer-2 networks. However, their architectures and operating assumptions differ, so you should evaluate each network separately rather than assuming they all provide exactly the same environment.
Compatibility can reduce friction. A team with an existing Solidity application may be able to reuse much of its codebase, tests, and deployment workflow.
But compatibility does not automatically guarantee identical execution costs, identical block timing, identical precompiles, or identical security assumptions.
For instance, a contract might compile and deploy successfully on two EVM-compatible networks but behave differently if it depends on a network-specific precompile, assumes a particular gas schedule, or reads environmental values that differ between chains.
What EVM equivalence means
EVM equivalence generally refers to a closer match in execution semantics: bytecode and contract behavior are intended to follow the relevant EVM rules.
The term needs context, though. There is no single marketing label that guarantees every protocol detail is identical across all environments. Some discussions distinguish equivalence at the virtual-machine level from equivalence with the broader Ethereum execution environment.
This distinction becomes especially important with zkEVMs, where projects may deliberately change parts of Ethereum’s surrounding architecture to make cryptographic proof generation more practical.
Compatibility vs. equivalence at a glance
| **Dimension** | **EVM compatibility** | **EVM equivalence** |
| Main goal | Support familiar EVM development and execution | Match EVM execution behavior closely |
| Solidity contracts | Often reusable, sometimes with changes | Intended to behave consistently at the relevant VM level |
| Developer tooling | May be reusable, depending on the environment | Generally offers stronger compatibility at the VM level |
| Gas and fees | Can differ from Ethereum | Execution equivalence does not imply identical transaction fees across networks |
| Broader protocol behavior | May differ substantially | Depends on whether the claim covers only the VM or more of Ethereum’s execution environment |
What should you check before migrating a dApp?
Imagine that you are moving a lending protocol from Ethereum to another EVM-compatible chain. The source code compiles, the deployment succeeds, and your basic tests pass. Are you done?
I would say no. You still need to examine the assumptions the application makes about its execution environment.
Check whether required precompiles are available, whether external dependencies exist on the target chain, and whether the application relies on block timestamps, block numbers, transaction ordering, or chain-specific behavior. Review the gas schedule and test realistic transaction paths, including failures and interactions between contracts.
You should also examine the chain’s consensus and security model, validator or sequencer arrangements, bridge dependencies, and operational reliability. Those factors do not all belong to EVM compatibility itself, but they matter to the security of the application you are deploying.
A contract that executes correctly can still be exposed to risks elsewhere in the system.
The takeaway is that EVM compatibility reduces the cost of moving between environments, but it does not remove the need for chain-specific testing and security review.
The next generation: Parallel EVM architectures
Traditional EVM execution has largely followed a sequential model for processing transactions within a block. This provides a clear execution order and makes state changes easier to reason about. However, it also creates a potential performance bottleneck: a multicore machine cannot automatically use every core to execute transactions at the same time if the execution engine must preserve the prescribed order and account for state dependencies.
Parallel EVM designs aim to improve this situation by allowing independent transactions to execute concurrently when it is safe to do so.
That sounds straightforward until you consider how smart contracts interact.
Why sequential execution can be a bottleneck
Suppose a block contains two transactions:
- Transaction A updates a balance in Contract X.
- Transaction B updates a separate balance in Contract Y.
If the transactions do not depend on each other’s state, their computations may be able to run independently. In principle, a system could execute them on different CPU cores and combine the resulting state changes while preserving the required transaction semantics.
Now change the example. Suppose both transactions update the same pool’s reserves, or one transaction changes a value that the other reads to calculate its result. Executing them independently without checking for conflicts could produce the wrong outcome.
The challenge is not simply running more instructions at once. It is identifying which operations can run concurrently while preserving the correct result.
How parallel execution can work
Parallel EVM systems can use techniques such as transaction scheduling, access tracking, optimistic execution, and conflict detection. The exact approach depends on the implementation.
In an optimistic model, the system may execute transactions concurrently based on an initial assumption that their state accesses will not conflict. It then checks whether those assumptions held. If a conflict is detected, the system may need to re-execute some work or resolve the transactions according to its rules.
Other designs may use declared access information or different scheduling mechanisms to identify potential conflicts earlier.
The important point is that parallel execution must preserve the semantics that users and contracts rely on. A faster result that changes the meaning of a transaction is not a valid optimization.
If you are evaluating a parallel EVM, I would look beyond the advertised transaction-per-second number. Ask which workloads benefit, how conflicts are detected, how much work is repeated, and whether the benchmark reflects realistic application behavior.
A workload containing many independent transfers may behave very differently from a workload in which thousands of transactions compete to update the same popular contract.
Parallel EVM projects and related approaches
Several projects have pursued higher throughput through different execution designs. Their architectures evolve, so the descriptions below are starting points for research rather than fixed performance rankings.
Monad
Monad is an EVM-focused blockchain project that emphasizes parallel execution while aiming to preserve compatibility with Ethereum smart contracts. Its design and execution model are documented in the official Monad documentation.
If you are considering deploying an existing Solidity application, check the current documentation for execution semantics, tooling support, and any implementation-specific limitations. EVM compatibility alone does not tell you how your application’s workload will perform.
Sei
Sei has developed EVM-related infrastructure alongside its broader blockchain architecture. Its documentation describes the network’s current developer environment and execution capabilities; see the official Sei documentation.
I would check the specific version and network you plan to use rather than relying on a general claim about parallel execution. The behavior available to developers can change as a network upgrades its software.
Neon EVM
Neon EVM provides an environment for running EVM-compatible applications within the Solana ecosystem. It is worth distinguishing this from a standalone EVM-focused layer-1 that implements parallel execution as its central design.
If your application uses Neon, you should investigate its execution environment, transaction model, and interactions with Solana rather than assuming it behaves exactly like Ethereum. Start with the official Neon documentation.
These projects should not be treated as interchangeable implementations of one identical design. Their goals, architectures, and execution environments differ.
It is also useful to distinguish the EVM from Solana’s own runtime. Solana uses its sBPF-based execution environment and a model in which programs access account data supplied through instructions. Solana’s runtime has its own mechanisms for scheduling and transaction processing. Neon EVM, by contrast, brings an EVM-compatible execution environment into that ecosystem.
Does a Parallel EVM automatically mean faster dApps?
No. Parallel execution creates an opportunity for higher throughput, but the benefit depends on the workload, implementation, available hardware, and network-level constraints.
Consider a popular decentralized exchange pool. Many transactions may attempt to modify the same pool state. Even if the network supports parallel execution, these conflicting operations may limit how much useful concurrency is possible.
Now consider an application whose transactions mostly operate on independent accounts or contracts. It may offer more opportunities for concurrent execution.
This is why I would avoid treating parallelism as a magic switch that makes every smart contract faster. It is an execution strategy with trade-offs, and its practical value depends on what your application actually does.
For developers, the best approach is to profile the target workload, test under realistic contention, and measure end-to-end behavior. The theoretical capacity of an execution engine is only one part of the story.
Zero-knowledge EVMs (zkEVMs): Understanding Type 1 to Type 4
Parallel EVMs and zkEVMs are often mentioned in the same discussions about blockchain scaling, but they address different challenges.
A Parallel EVM focuses on executing transactions concurrently where possible. A zkEVM focuses on making EVM-style execution compatible with zero-knowledge proof systems, allowing a prover to generate cryptographic evidence that execution followed the relevant rules.
That evidence can then be verified without requiring the verifier to repeat every computation in the same way. In rollup designs, validity proofs can help Ethereum verify that a batch of transactions was processed correctly, subject to the system’s architecture and security assumptions.
One clarification is worth making early: a zkEVM is not automatically a privacy tool. Zero-knowledge cryptography can support privacy, but many zkEVM systems use proofs primarily to establish the validity of execution and state transitions.
Why build a zkEVM?
Ethereum’s execution model was not originally designed to make every operation easy to prove with a zero-knowledge circuit. Some instructions, cryptographic operations, and data structures can be expensive to represent and verify in such systems.
A zkEVM must balance two goals: reproducing EVM behavior closely enough for the intended use case, and making execution proofs practical to generate.
The trade-off becomes particularly important if you want to reuse an existing Solidity application. Greater compatibility can make migration easier, while changes that simplify proof generation may require adjustments to tooling, contract code, or assumptions about the environment.
Vitalik Buterin’s original explanation of zkEVM types introduced a taxonomy that helps describe these trade-offs. The types are useful conceptual categories, not a permanent ranking of every project.
Type 1: Fully Ethereum-equivalent
A Type 1 zkEVM aims to reproduce Ethereum as closely as possible, including the execution environment and surrounding protocol details relevant to proving the chain.
The attraction is straightforward. If a system can prove Ethereum execution with high fidelity, it has a strong path toward reusing existing Ethereum infrastructure and verifying Ethereum-style execution without introducing unnecessary differences.
The difficulty is proof generation. Ethereum’s existing structures and operations were not all optimized for zero-knowledge proving, so reproducing them faithfully can be computationally demanding.
If you are evaluating a Type 1 design, I would focus on more than its compatibility claim. You also need to understand proof-generation costs, proving latency, hardware requirements, and how those costs affect the system’s ability to operate reliably.
Type 2: Fully EVM-equivalent
Type 2 systems aim to preserve EVM behavior while allowing some differences in the broader Ethereum environment, such as state structures or other components outside the virtual machine itself.
The idea is to retain strong smart contract compatibility while making parts of the system easier to prove.
This can be attractive for developers because most ordinary contracts may not need to know how the underlying state tree is implemented. However, compatibility can become more complicated for applications that directly depend on particular Ethereum data structures, historical proofs, or other protocol-level details.
So, if your dApp is a standard token contract, the differences may be largely invisible. If it verifies historical state or relies on specialized infrastructure, you need a more careful review.
Type 3: Almost EVM-equivalent
Type 3 systems allow some differences in EVM behavior to make the implementation easier to build or prove. Depending on the design, this may involve changes to less commonly used features, precompiles, or other edge cases.
The benefit is that proof generation may become more practical without abandoning most of the EVM developer experience.
The trade-off is compatibility. An application that depends on a feature that is missing or behaves differently may require changes, even if the majority of Solidity contracts work as expected.
I would treat this category as a reminder to test your actual dependencies rather than assume every EVM feature is available. If your application relies on a particular precompile or handwritten bytecode, check that functionality directly.
Type 4: High-level-language equivalent
Type 4 systems approach compatibility from a different direction. Instead of aiming to reproduce every detail of EVM bytecode execution, they compile high-level languages such as Solidity into a proving-friendly target environment.
This can reduce some of the overhead associated with proving low-level EVM execution. It may also offer an efficient path for ordinary high-level contracts.
However, source-level compatibility does not guarantee that every bytecode-level assumption carries over. Differences can affect debugging tools, deployment addresses, compiler behavior, low-level assembly, and contracts that rely on specific EVM details.
For example, CREATE2-derived addresses depend on the relevant bytecode and deployment parameters. If a target environment produces different bytecode, assumptions about precomputed contract addresses may no longer hold.
That is the kind of detail I would investigate before migrating a production application. The source code may look familiar, yet infrastructure around the contract can still depend on the exact compilation and execution model.
Comparing the four zkEVM types
Type 1
Highest Ethereum fidelity
Aims to preserve Ethereum’s execution and surrounding protocol details. The proving workload can be demanding because fewer changes are made to accommodate zero-knowledge systems.
Type 2
EVM-equivalent execution
Aims to retain EVM behavior while modifying some surrounding structures to improve proof generation or system design.
Type 3
Selective compatibility trade-offs
Allows certain differences to make implementation or proving more practical. Some contracts or specialized features may need adjustments.
Type 4
High-level-language compatibility
Compiles high-level smart contract code to a proving-friendly target. This can improve proving efficiency, but bytecode-level equivalence and tooling reuse may be more limited.
The taxonomy originated in 2022, and projects have changed since then. Do not assume that a historical classification is a definitive label for a project’s current architecture. Review its current technical documentation and compatibility claims before making a decision.
Which zkEVM type is best?
There is no universal winner.
If your priority is reproducing Ethereum execution as faithfully as possible, a design closer to Type 1 may be attractive. If your application needs high compatibility at the VM level while allowing changes to surrounding structures, a Type 2 approach may be worth considering. If proof efficiency is a major priority, designs that make selective trade-offs may offer advantages.
But those are starting points, not a substitute for evaluation.
I would compare the actual system’s proof-generation latency, verification cost, throughput, tooling, supported instructions and precompiles, upgrade process, and security assumptions. You should also investigate how the system handles failures and what happens if proving infrastructure is delayed or unavailable.
The number attached to a zkEVM type tells you something about its design philosophy. It does not, by itself, tell you whether the system is secure, economical, or suitable for your application.
EVM vs non-EVM virtual machines
The EVM is influential, but it is not the only way to execute smart contracts. Other blockchain ecosystems use different runtimes and programming models, each with its own approach to state, execution, and developer tooling.
If you are choosing a chain for a new application, I would start with your requirements rather than assuming that the most familiar VM is automatically the best choice.
EVM vs Solana’s SVM
Solana uses a runtime based on its own sBPF bytecode format. Programs are compiled for the Solana environment, and mutable application state is held in separate accounts supplied to instructions. This differs from the EVM model, in which contracts interact with their own persistent storage and other accounts through EVM instructions.
The official Solana program documentation explains how programs are compiled, deployed, and executed.
| **Dimension** | **EVM** | **Solana runtime (SVM)** |
| Common development languages | Solidity, Vyper, and other supported toolchains | Rust and other supported approaches |
| Compiled format | EVM bytecode | Solana Bytecode Format (sBPF) |
| State model | Contract storage is part of Ethereum’s global state | Programs are stateless; mutable data lives in separate accounts |
| Execution model | EVM instruction semantics, with chain-specific execution environments | Solana runtime with account-based instruction execution |
| Developer ecosystem | Broad Solidity tooling and EVM-compatible networks | Solana-specific tooling, libraries, and account model |
One important caveat: SVM is often discussed in connection with parallel execution, but Solana’s execution behavior is not simply a copy of a Parallel EVM. Its account model, runtime, transaction scheduling, and execution rules are distinct.
For developers, the difference affects how you design an application. On Solana, you must think carefully about which accounts an instruction reads or writes and how those accounts are supplied. On an EVM chain, you generally reason about contract calls, storage, and the interactions between contracts.
Neither approach makes application design effortless. They simply place different constraints on the developer.
If you already have a Solidity codebase, an EVM environment may make reuse more straightforward. If you are building from scratch and your application fits Solana’s account and execution model, the Solana ecosystem may be worth evaluating on its own merits.
EVM vs Move VM
Move is a programming language and ecosystem built around explicit resource-oriented programming concepts. Move-based networks, including Aptos and Sui, use Move-based execution environments, although their architectures and execution models are not identical.
A key idea in Move is that assets can be represented through resource-oriented types and rules that constrain how values are created, moved, or destroyed. This can help express certain safety properties at the language level.
In Solidity, developers commonly represent tokens and other assets through contract state and carefully designed functions. Security depends on the correctness of those functions, access controls, and the surrounding execution environment.
Move’s resource model offers a different way to structure such logic. It does not eliminate the need for security reviews, and it does not guarantee that every Move application is safer than every Solidity application. The quality of the implementation and its assumptions still matter.
| **Dimension** | **EVM and Solidity** | **Move-based environments** |
| Core programming model | Smart contracts with explicit storage and call semantics | Resource-oriented language and type system |
| Asset handling | Usually expressed through contract logic and storage | Move’s resource model constrains how resource values are handled |
| Ecosystem | Large established Solidity and EVM tooling ecosystem | Move-specific languages, frameworks, and developer tooling |
| Portability | EVM bytecode can be reused across compatible environments, subject to differences | Move code must target the relevant Move-based environment and its APIs |
| Main consideration | Contract correctness, execution behavior, gas, and external calls | Resource semantics, language rules, execution model, and network-specific architecture |
I would avoid treating Aptos and Sui as identical simply because both use Move. They have different architectural choices and ecosystem details. If you are considering either platform, review its current language documentation, runtime behavior, and development tools rather than relying on a broad label.
EVM vs WASM
WebAssembly (WASM) is a portable binary instruction format originally developed for efficient execution across different environments. Blockchain systems can use WASM-based runtimes to execute smart contracts compiled from supported languages.
One attraction is language flexibility. Depending on the runtime, developers may be able to work with Rust, C, C++, or other languages that can compile to the required target.
However, WASM does not automatically mean that a blockchain runtime is faster, safer, or more decentralized than the EVM. Those properties depend on the implementation, supported system interfaces, resource metering, validator hardware requirements, and the application workload.
A blockchain runtime must also define deterministic execution rules. Ordinary WASM capabilities do not, by themselves, supply every rule a blockchain needs for gas accounting, state access, or consensus-compatible execution.
If you are choosing a WASM-based environment, investigate the actual runtime and its supported APIs. Check how computation is metered, how contracts access persistent state, which languages and toolchains are supported, and how the system prevents nondeterministic behavior from affecting execution.
Which virtual machine should you choose?
I would compare the options across five practical questions:
- Existing code: Do you need to reuse Solidity contracts and EVM tooling, or can you start with another language and runtime?
- State model: Does the application fit a contract-storage model, an account-oriented model, or a resource-oriented model?
- Workload: Does your application benefit from a particular execution strategy, and have you tested that assumption?
- Security: What are the runtime’s security properties, upgrade controls, dependencies, and operational assumptions?
- Ecosystem: Can your team find the libraries, testing frameworks, infrastructure, and expertise it needs?
The EVM has a major advantage in the breadth of its established developer ecosystem. But that does not make it the right answer to every application design question.
A virtual machine is one component of a blockchain platform. Your choice should reflect the whole system, including its execution semantics, state model, developer experience, and security assumptions.
Frequently asked questions about the EVM
What is the Ethereum Virtual Machine, and how does it work?
The Ethereum Virtual Machine (EVM) is the execution environment that processes smart contract bytecode and determines how contract execution changes Ethereum’s state. When a transaction calls a smart contract, the EVM executes its instructions according to protocol-defined rules, with gas limiting the computational work performed.
What is the difference between EVM storage, memory, and stack?
The stack holds values used by EVM instructions, memory provides temporary working space during an execution frame, and storage keeps persistent contract state across transactions. Transient storage provides another option for temporary state that can be shared across calls within a transaction and is cleared when that transaction ends.
What are EVM opcodes, and how does gas work?
Opcodes are the low-level instructions executed by the EVM, including arithmetic operations, memory access, storage access, and contract calls. Gas measures the computational effort required by these operations and limits the resources a transaction can consume. Transaction fees depend on the gas used and the applicable fee rules, and failed transactions can still incur gas fees.
What is the difference between EVM compatibility and EVM equivalence?
EVM compatibility generally means that a blockchain supports a useful portion of the EVM development and execution environment. EVM equivalence makes a stronger claim about matching EVM execution semantics. Neither term automatically guarantees identical fees, consensus behavior, infrastructure, or security assumptions across networks.
What is a Parallel EVM?
A Parallel EVM is an execution environment that aims to process independent transactions concurrently where possible. It can improve throughput when the workload allows useful parallelism, but transactions that access conflicting state may require coordination, conflict detection, or re-execution.
What are the four types of zkEVM?
Vitalik Buterin’s taxonomy describes Type 1 as fully Ethereum-equivalent, Type 2 as fully EVM-equivalent with differences in the surrounding Ethereum environment, Type 3 as almost EVM-equivalent, and Type 4 as high-level-language equivalent. These categories describe different compatibility and proving trade-offs; they should not be treated as a universal ranking of security or overall quality.
Why does the EVM use 256-bit words?
The EVM uses a stack machine with 256-bit words, a choice that supports convenient handling of 256-bit cryptographic values such as Keccak-256 hashes. The word size also aligns with Solidity’s common uint256 type, but it does not mean that every data structure or variable always occupies exactly 256 bits.
Final thoughts: Understanding the EVM beyond the basics
The EVM is easier to understand once you stop thinking of it as just the software that runs Solidity contracts. It is an execution model that helps Ethereum nodes process smart contract logic consistently, manage state changes, and account for computational resources.
For me, the most useful starting point is to connect the low-level mechanics to the decisions developers actually make. Understanding storage and memory helps you reason about gas. Understanding bytecode and opcodes helps you investigate execution failures. Understanding compatibility helps you avoid costly assumptions when moving a dApp to another network.
The newer developments deserve the same practical mindset. Parallel EVMs offer ways to improve execution throughput, but the gains depend on transaction dependencies and workload characteristics. zkEVMs use a different set of trade-offs, balancing compatibility with the cost of generating proofs. Meanwhile, SVM, Move-based environments, and WASM runtimes provide alternative approaches to contract execution and state management.
If you are building a Web3 application, I would not choose a platform based on a single headline metric or technical label. Look at the code you need to run, the behavior your application requires, the tools your team can use, and the security assumptions you are prepared to accept.
The real advantage of understanding the EVM is that you can evaluate these trade-offs for yourself rather than relying on marketing claims or compatibility labels.