The Ultimate ZK-EVM Comparison Guide

The Ultimate ZK-EVM Comparison Guide

All ZK-EVMs (Zero-knowledge Ethereum Virtual Machines) aim to achieve the same goal; make Ethereum more scalable and grow the adoption of web3.

As a developer, there are now several different options you have to create and deploy smart contracts onto a ZK-EVM, each with unique implementations, benefits, and drawbacks.

In this post, we'll cover the different kinds of ZK-EVMs as defined by Vitalik, the pros and cons of each, discuss which companies are working on them, and deploy smart contracts to each one.

Let's dive into it!

Recap: What Is A ZK-EVM?

If you're reading this and you aren't already familiar with zero-knowledge proofs or ZK-EVMs, I'd encourage you to read two of my previous posts linked below.

First, a basic understanding of ZK-EVMs:

If you want to go deeper, I also made a post covering how zero-knowledge proofs work, without any of the math:

TLDR: ZK-EVMs are Ethereum rollups that batch transactions together and post a "validity proof" of that batch back to the Ethereum blockchain + they're EVM-compatible, meaning you don't need specialized tools to work with them.

The EVM, Opcodes & Bytecode

The Ethereum Virtual Machine (EVM) is the runtime environment for smart contracts on Ethereum. Ethereum not only stores all accounts and balances similarly to Bitcoin but additionally stores what's called a "machine state".

The machine state is stored in a trie data structure, and changes from block to block after executing the transactions contained within that block.

The EVM is deterministic, meaning a set of instructions performed on any given state will result in the same new state every time. A simple analogy for this is to imagine a game of chess.

The chess board (Ethereum) has a large (in Ethereum, infinite) number of states the game can be in. The game has rules on what moves (transactions) can be performed, and restrictions on who can perform what actions (using accounts, signatures, etc.).

Just like Ethereum, players make moves (submit transactions), and the game (Ethereum) dictates what rules are allowed, and executes them, resulting in a new board (world) state after each move (block).

The Ethereum documentation describes this as a mathematical function:

Given an input, it produces a deterministic output. It therefore is quite helpful to more formally describe Ethereum as having a state transition function:

Y(S, T)= S'2

Given an old valid state (S) and a new set of valid transactions (T), the Ethereum state transition function Y(S, T) produces a new valid output state S'

High-Level Languages & Bytecode Compilation

As a developer on Ethereum, or any other EVM-compatible blockchain you're usually writing smart contracts in Solidity (although other languages like Vyper and Yul exist).

As with all other high-level languages, they're intended to be easily readable for humans, so we don't have to worry about the hard stuff like registers, memory addresses, and call stacks, and instead focus on usability.

However, machines don't understand this Solidity nonsense; the EVM included. The EVM understands bytecode, which is binary; machine-readable code.

The bytecode represents a series of EVM opcodes, each of which performs a specific operation in the EVM; sometimes these opcodes are concerned with stack operations like PUSH (add to the stack), or POP (remove from the stack) but also handles blockchain-specific operations like ADDRESS and BALANCE.

Each item is a 256-bit word, which was chosen for the ease of use with 256-bit cryptography (such as Keccak-256 hashes or secp256k1 signatures).

As developers, this means we need a way to convert our smart contract code from SolidityOpcodesBytecode in order for it to be executable by the EVM.

Luckily for us, there are compilers that do this for us.

Compiling Smart Contracts

Let's look at an example. I've got a very basic smart contract written in Solidity:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

contract Greeter {
    // Internal state to the contract
    string private greeting;

    // Constructor - run when contract is deployed
    constructor(string memory _greeting) {
        greeting = _greeting;

    // Read function (can be called without a transaction)
    function greet() public view returns (string memory) {
        return greeting;

    // Write function (requires a transaction to be called)
    function setGreeting(string memory _greeting) public {
        greeting = _greeting;

        emit GreetingChanged(_greeting);

    event GreetingChanged(string newGreeting);

But, I have a problem. The EVM can't understand this Solidity! It needs bytecode... So, I'm going to use a compiler to help me out here; more specifically, for example, I can use the built-in compiler in the Remix IDE to help me.

After I compile it, I can inspect the opcodes that get compiled, reflecting the contents of my smart contract.


I can also inspect the bytecode that gets compiled from those opcodes.



The final bytecode you see there is what would be understood by the EVM. So you can imagine why developers want to use high-level languages rather than write the bytecode themselves.

What we can do now is actually match up exactly what's happening in the bytecode to the opcodes. Let's take a look at the first three opcodes, PUSH 80, PUSH 40, and MSTORE.

I've matched up the first 10 hexadecimal values of the byte code to the first 3 opcodes we see below:

  1. PUSH 80 / 6080: Pushes the value 80 (hexadecimal) onto the stack.

  2. PUSH 40 / 6040: Pushes the value 40 (hexadecimal) onto the stack.

  3. MSTORE / 52: Stores the second stack item (40) at the memory location specified by the first stack item (80).

We can do this matching process because Ethereum documents each opcode corresponding to its bytecode value:

Why do Opcodes or the EVM Matter?

The challenge of building an EVM-compatible rollup is that Ethereum was not originally designed around ZK-friendliness, so there are many parts of the Ethereum protocol that take a large amount of computation to ZK-prove.

Specific opcodes executed by an EVM are more "ZK-unfriendly" than others, and this had led to variance in the level of EVM-compatibility companies have chosen to adopt in their ZK-EVM products.

For example, some have opted for full equivalency for every EVM opcode, some have slight modifications to some opcodes, and some actually embrace producing completely different bytecode while maintaining high-level-language equivalence.

Below, I'll explore the different approaches each ZK-EVM has made and the tradeoffs each of them makes between performance (how fast they can generate ZK proofs) and their level of EVM compatibility.

Different Kinds of ZK-EVMs

Vitalik made a blog post in August 2022 categorizing ZK-EVMs into four (and a half) different categories, titled "The different kinds of zkEVMs". I'll be summarising this information below, but I'd recommend reading the full post too.

In the post, Vitalik charts the different types according to their EVM compatibility and ZK-proof generation times (performance). View original image:

To simplify this, in my opinion, we can place ZK-EVMs on a single line:

According to Vitalik, ZK-EVMs come in 4.5 different categorizations:

  • Type 1: Fully Ethereum equivalent, i.e. they do not change any part of the Ethereum system to make it easier to generate proofs. ZK proofs take several hours to generate in this kind of system.

  • Type 2: Fully EVM-equivalent, but changes some different internal representations like how they store the state of the chain, for the purpose of improving ZK-proof generation times.

  • Type 2.5: Fully EVM-equivalent, except they increase the gas costs of some operations to "significantly improve worst-case prover times".

  • Type 3: Almost EVM-equivalent ZK-EVMs make sacrifices in exact equivalence to further enhance prover times and simplify EVM development.

  • Type 4: High-level language equivalent ZK-EVMs compile smart contract source code written in a high-level language to a ZK-SNARK-friendly language, resulting in faster prover times but potentially introducing incompatibilities and limitations.

Companies Building ZK-EVMs

Below, I'll show you the current landscape of all the different types of ZK-EVMs being built by various companies; including Taiko, Linea, Polygon, Scroll, and zkSync (plus a special bonus guest). I'll also be trying out each of the ZK-EVMs from a developer perspective.

For a fair test, I'll be using EVM Kit as my starting point for each; a full-stack boilerplate setup for creating web3 applications (learn more), including both smart contracts and web apps.

From a new EVM Kit project, I'll first deploy a basic smart contract written in Solidity & built in a Hardhat environment to each of the different ZK-EVMs we have explored to review the developer experience of writing smart contracts.

Second, I'll deploy an NFT collection smart contract to each, and mint some NFTs to see the gas price of interacting with common EIP standards on each chain as well.

We'll go through each in order of their "type", starting from type 1 and working our way up to type 4.

First up, we have Taiko at type 1.


Note: Taiko is currently in the testnet phase.

Taiko is a type 1 ZK-EVM, meaning it replicates all of Ethereum's behaviour exactly the same, using the same hash functions, gas prices, encryption algorithms, etc. except Taiko currently handles two Ethereum Improvement Proposals differently.

The primary benefits of this architecture are:

  • The development process is identical to using Ethereum.

  • Taiko re-uses a lot of Ethereum infrastructure such as the node client software, making it very familiar to Ethereum users.

  • There are no changes to hash functions or gas costs.

Taiko's approach is to fully optimize for EVM-equivalency, which comes at the cost of a large time to generate ZK proofs that get posted back to Ethereum.

The Taiko L2 uses the same rules of Ethereum to determine the new state of the rollup; immediately and deterministically after a block is proposed.

The drawback to this is slow ZK proof generation time, which is required to give certainty about the execution that happened on the rollup to bridges; meaning you won't be able to bridge funds back to Ethereum L1 until that proof is generated.

Deploying to Taiko ZK-EVM (Testnet)

Since Taiko has not released their mainnet at the time of writing this, I'll be using the "Taiko Alpha-3 Testnet" throughout this process. First, I got myself some testnet funds from the Sepolia Faucet and sent funds over to Taiko using Taiko's Bridge:

Once I'm across the bridge, the development process is identical to using Ethereum. The only change I make is to transform my RPC from an Ethereum RPC to the Taiko RPC. Below is the information about the chain ID and RPC I used for this process:

I was easily able to deploy my basic Solidity smart contract and perform both read and write operations on it as seen from the Taiko block explorer:

The process of deploying an NFT collection and minting my first Taiko NFT was also seamless, and worked exactly as I'd expect when working with Ethereum:

As I mentioned previously, the drawback of Taiko is that the ZK proof generation process is very lengthy, which means I am unable to bridge my ETH back to Ethereum L1 for several hours:

Your asset [my ETH] is not ready to be claimed. Taiko => Sepolia bridging can take several hours before being ready.

Below is the summary of my interactions with Taiko:

Contract InteractionGas Fee (in ETH)Gas Fee (USD)
Deploy Greeter Solidity smart contract0.000413119500275413$0.78
Run "setGreeting" function0.00004744500003163$0.089
Deploy NFT Collection (via proxy)0.001124796000749864$2.12
Mint NFT0.00030571500020381$0.58
Bridge funds back to ETH L10.000194796000129864$0.37

Keep in mind, these were all on Taiko's testnet and these numbers will be different than when Taiko launches their mainnet in the future.

Linea (Consensys ZK-EVM)

Note: Linea is in the process of releasing their mainnet later this month (July 2023)!

Linea's original announcement planned to release a type 2 ZK-EVM, however, their current release is a type 3 ZK-EVM working towards a type 2. As outlined in their risk disclosure documentation, Linea does not currently prove all EVM opcodes:

The validity proofs used to verify the computation of Linea batches do not prove 100% of EVM opcodes and precompiles

In addition, their documentation states the "proofs of computation for all EVM opcodes and precompiles" will come in phase 1 of their improvements as outlined in their roadmap:

Linea also deviates from Ethereum in how it represents the state of the chain. For instance, keccak, a hashing function used by Ethereum has been changed for a more ZK-friendly alternative. Despite this internal difference, you're deploying the same smart contract bytecode to Linea that you would deploy if you were using Ethereum.

Deploying to Linea (Testnet)

Once I secured myself some Goerli testnet ETH (which was a slight mission), I headed over to the Linea bridge to bridge ETH to the Linea testnet.

Once I successfully bridge my funds across, the development process is the same as Ethereum. Everything worked as expected with my Greeter smart contract:

And I was easily able to deploy the NFT collection and mint my first Linea NFT:

The bridge UI from Linea isn't currently available, but you can call the sendMessage function on their bridge smart contract directly. According to the Linea documentation, the bridging of ETH from L2 back to L1 takes ~15 minutes, but it took several hours during my testing.

Below is the summary of my interactions with Linea:

Contract InteractionGas Fee (in ETH)Gas Fee (USD)
Deploy Greeter Solidity smart contract0.000466914075602163$0.87
Run "setGreeting" function0.000053601071759034$0.10
Deploy NFT Collection (via proxy)0.001233567189763232$2.31
Mint NFT0.000096778500451633$0.61
Bridge funds back to ETH L10.000096778500451633$0.18

These were all very similar prices to Taiko; but again, are testnet values!

Polygon zkEVM

Disclaimer: At the time of writing this, I currently work at Polygon Labs. There may be some unconscious bias for this reason.

Polygon zkEVM is currently a type 3 ZK-EVM, with a small number of changes required before transforming into a type 2 ZK-EVM. You can view the opcodes, precompiled contracts, and other minor differences Polygon zkEVM transparently lists in the documentation that are different from the EVM's behaviour.

With these tradeoffs in equivalency to the EVM's behaviour, Polygon zkEVM is able to generate and post ZK proofs in just a few minutes; allowing you to bridge your funds back to Ethereum L1 in around ~30 minutes after interacting with the zkEVM.

With Polygon zkEVM, you're deploying the same bytecode as you would with Ethereum, however, the interpretation process of this bytecode is slightly different.

EVM Bytecodes are interpreted using a zero-knowledge Assembly language (zkASM), specially developed by the Polygon zkEVM team. In Vitalik's blog post, he mentions this:

Polygon has a unique design where they are ZK-verifying their own internal language called zkASM, and they interpret ZK-EVM code using the zkASM implementation. Despite this implementation detail, I would still call this a genuine Type 3 ZK-EVM; it can still verify EVM code, it just uses some different internal logic to do it.

The polygon engineering team has also stated they'll complete the remaining precompiles to become type 2 in addition to improving the proof generation & withdrawal times very soon.

Deploying to Polygon zkEVM

Polygon zkEVM mainnet beta launched in March 2023, so I'll be using mainnet funds for this process. First, I used the Polygon bridge to ship my funds across to the zkEVM. The gas fee for this process was around $5 USD and took ~5 minutes for the funds to arrive on the L2.

Just like the previous two chains, it's seamless and behaves as I'd expect Ethereum to. I was easily able to deploy my Greeter smart contract and call the functions on it as well as deploy an NFT Collection & mint an NFT:

The bridging process back to Ethereum L1 took ~60 minutes and was another $5 to claim my funds back into Ethereum mainnet.

If you're looking to build on the Polygon zkEVM, consider checking out my guide below taking you through the full process in less than 10 minutes:


Note: Scroll is currently in the testnet phase.

Similar to Polygon zkEVM, Scroll is currently a type 3 ZK-EVM working towards being a type 2, with transparent documentation on the opcodes, precompiles, and other differences from Ethereum defined in their documentation. The scroll docs outline:

For the average Solidity developer, these details won't affect your development experience.

On the Scroll blog, they have also listed the exact work remaining to release their mainnet - see Scroll zkEVM next steps.

Deploying to Scroll

Just like the previous chains we've explored so far, Scroll is able to execute the same native bytecode that you would deploy to Ethereum, meaning there's no change in the development process.

I'm easily able to bridge funds to the Scroll testnet via their bridge UI:

The estimated time to bridge funds to the L2 is between 8-14 minutes, and it took 10 minutes for me to be able to use them on the L2.

Everything acts the same way as I'd expect Ethereum to, and I was easily able to deploy my Greeter smart contract and my NFT collection again:

Bridging funds back to Ethereum L1 is estimated to take between 10 minutes to a few hours according to the Scroll documentation (unfortunately I missed exactly how long it took in my case).

Below is the summary of my transactions on the Scroll testnet. During my testing, the gas fees seemed to be significantly cheaper than the previous chains we explored, although I'm not sure of the exact reason for why this was the case:

Contract InteractionGas Fee (in ETH)Gas Fee (USD)
Deploy Greeter Solidity smart contract0.000000275329$0.00051
Run "setGreeting" function0.000000031618$0.000059
Deploy NFT Collection (via proxy)0.000000749896$0.0014
Mint NFT0.00000020381$0.00038
Bridge funds back to ETH L10.000000261872$0.00049

zkSync Era

zkSync Era is a type 4 ZK-EVM, and the experience is quite different from the other products we've explored so far.

Era embraces the fact that the smart contract bytecode that gets deployed for their zkEVM is not the same as Ethereum; and this large difference enables their product to have unique offerings, such as native account abstraction.

These base changes in capability come with differences in developer experience, but as Vitalik defined; still allows you to work with the same high-level languages such as Solidity.

Era comes with a set of best practices and considerations for where you might run into different behaviours than when interacting with Ethereum and requires some slight adjustments in your development setup.

For example, I needed to install an additional Hardhat extension and add one additional flag to my Hardhat configuration to compile the required bytecode for the chain:

In the compilation process, the bytecode that gets generated for you to work with zkSync is different than the bytecode that gets generated by default:

Existing deploy tooling like thirdweb deploy (used in EVM Kit) has support for this bytecode by providing the --zksync flag to the deploy command.

Overall, it's quite a small amount of changes required to use Solidity and my standard development environment to cooperate with zkSync.

Deploying to zkSync Era

Using zkSync's bridge, it's estimated about ~15 minutes for the funds to arrive on L2.

After this point, the process is the same! I'm able to deploy my Greeter smart contract, as well as my usual NFT collection with an NFT minted:

Currently, bridging funds back to Ethereum L1 from zkSync Era has a 24-hour delay:

To ensure security in the first days of the protocol, there is a 24 hour delay on withdrawals from zkSync Era (L2) to Ethereum (L1). See our blog post for more details.

Here's the summary of my interactions on zkSync Era:

Gas Fee (in ETH)Gas Fee (USD)
Deploy Greeter Solidity smart contract0.001839549$3.47
Run "setGreeting" function0.00007822825$0.15
Deploy NFT Collection (via proxy)0.0007640925$1.44
Mint NFT0.0002108245$0.40
Bridge funds back to ETH L10.000140764$0.27

Bonus Guest: Kakarot

Just last month, a new zkEVM called Kakarot written in Cairo was announced, which aims to bring EVM compatibility to Starknet as a type 2.5 ZK-EVM, however, is considered a type 3 in its current state.

While it does has a very cool name and landing page, it also received funding from Vitalik himself, among others including Starkware.

The testnet has not yet been released but is planned to release later in 2023, and their development team has provided the following update:

The Kakarot team achieved an impressive milestone (with the support of the Starknet Foundation and the collaborative efforts of over 40 unique contributors) by implementing 100% of EVM opcodes and 8 out of 9 EVM precompiles in just six months and with less than 5,000 lines of code!

What's the future? Will One Chain Win?

Rather than it being a battle about which chain is the best, it's overall good for the improvement of Ethereum and web3 that so many different approaches are being explored; each with their own pros and cons.

Vitalik also has explained his "multi-prover theory", where rollups could potentially collaborate together to enhance the security of the whole ecosystem, among other alternatives to the future of ZK-EVMs.

Wrapping Up

That's the landscape of ZK-EVMs today, at least as I understand it!

You can find the summary below of the companies working in the space, and the types of ZK-EVM their respective products currently are:

If you're looking to build on ZK-EVMs, consider checking out my guide to creating full-stack applications on the Polygon zkEVM below:

Thanks for reading! Follow me on Twitter for more!

Did you find this article valuable?

Support Jarrod Watts by becoming a sponsor. Any amount is appreciated!