# A comparison of zkEVMs
With the "zkEVM wars" heating up, there has been a lot of public discussion about the merits of different zkEVMs. There has been some misinformation as well, so we’d like to clarify some facts about Polygon zkEVM and how it compares to others.
I’m biased, being that I work at Polygon, but I’ll try to keep the comparison fair. I’ll focus mainly on Polygon’s zkEVM and zkSync Era, since they are in production, and since I don’t know as much about other zkEVM projects.
zkSync's zkEVM and prover make up over 100k lines of code. I did my best to give an accurate summary, but if there are any inaccuracies, please let me know and I will correct them.
## EVM compatibility
Polygon zkEVM directly executes EVM bytecode. By Vitalik’s [classifications](https://vitalik.ca/general/2022/08/04/zkevm.html), it is a type-3 zkEVM. It should become type-2 fairly soon; at the moment we're missing four precompiles. Scroll is also working toward a type-2 zkEVM.
By contrast, zkSync Era uses a different bytecode format, with support for Solidity through a compiler they provide. This makes it a type-4 zkEVM: it supports Solidity, but not EVM bytecode itself. Tools such as Hardhat do not work out of the box, though it can be used with zkSync’s plugins.
zkSync argues that their zkVM is more future-proof, i.e. it could work better with languages other than Solidity. However, their VM does seem to inherit many of the EVM’s performance characteristics, such as its 256-bit word size. A zkVM like [Miden](https://github.com/0xPolygonMiden/miden-vm) may be more future-proof, since it was designed for general purpose computing, not focused on Solidity.
## Performance
Performance has always been a priority for Polygon, and our zkEVM is remarkably efficient. The cost of running our prover on a CPU is around $0.000084 per transaction.
While we haven’t found any working benchmarks for zkSync’s zkEVM, we suspect that there is a large performance gap due to our very different choices of ZK technology.
### Field choice
After [researching](https://github.com/mir-protocol/plonky2/issues/1) several alternatives, we selected what has become known as the Goldilocks field, a prime field of order `2^64 - 2^32 + 1`. Its small size and its beautiful binary structure lead to extremely fast field operations, with multiplication taking less than two cycles on modern CPUs.
zkSync takes a more traditional approach, with a SNARK based on the alt-bn128 curve. The underlying field is ~254 bits large, and field multiplication takes around 80 cycles on a CPU.
To get a sense of the massive difference this can make, we can look to Celer’s [SHA2 benchmark](https://twitter.com/CelerNetwork/status/1631143870057816066). There, our STARK prover was 5-50x faster than provers based on elliptic curves.
The advantage of alt-bn128 is that the EVM natively supports it, so submitting proofs to Ethereum is simpler. At Polygon, we instead "wrap" our final aggregation proof with a fflonk proof over alt-bn128. Our approach takes a bit more work, but we consider it well worth it for the incredible performance gains.
### Arithmetization
The differences don’t end there. Our zkEVM is built on [STARKs](https://eprint.iacr.org/2018/046), but with a modern twist. We have a main STARK for the CPU (with one row per cycle), and other STARKs for arithmetic, hashing, and so forth. These tables can then be connected, as we described in [RapidUp](https://eprint.iacr.org/2022/1050). This is analogous to physical CPUs, which often have coprocessors to accelerate intensive operations like rendering, encryption, or ML inference.
Consider Keccak for example. Since it’s heavily used by EVM applications, we designed a dedicated STARK for it, using some new tricks which we documented [here](https://polygon.technology/blog/zk-white-paper-efficient-zk-proofs-for-keccak). It takes a lot of work to design such custom arithmetizations, but it pays off, allowing us to prove hundreds of Keccak permutations per second.
zkSync takes what I would call a more traditional approach. They use a PLONK-based prover, and while it does support custom gates, their zkEVM doesn’t make much use of them; most computations are done with a general-purpose gate called `SelectorOptimizedWidth4MainGateWithDNext`. It seems slightly more powerful than the vanilla PLONK gate, but it is still limited to simple operations such as mul-adds.
To their credit, zkSync does use lookup arguments (as do we), a more modern technique which can help improve the efficiency of things like Keccak. Still, without custom arithmetizations, things like 256-bit math, Keccak, and so forth are a lot less efficient.
## Security
Polygon takes security very seriously, and our zkEVM has gone through two separate audits: one by Spearbit and another by Hexens. Both reports are publicly available [here](https://github.com/0xPolygonHermez/zkevm-rom/tree/main/audits). We have also published instructions for [verifying the deployment](https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/VERIFY.md).
We are not aware of any published audits of zkSync’s zkEVM. zkSync’s [website](https://docs.zksync.io/updates/security-audits/) lists audits of the bridge contracts, but not the zkEVM itself.
Besides audits, both projects have various "training wheels" to provide backup layers of security, but that’s a rich topic which I won’t get into here.
## L1 data
Polygon zkEVM posts all transaction data to the L1. There was some confusion on Twitter about the gas costs associated with this; please see Edu's [post](https://twitter.com/eduadiez/status/1641518965720489986) for the correct numbers. Currently the average transaction size is around 120 bytes, so each transaction costs around `120 * 16 = 1920` gas.
zkSync posts state diffs instead. A malicious sequencer could withhold transaction data, but zkSync argues that having the current state trie suffices for security. This seems somewhat debatable, since transaction data is generally expected to be available, and certain applications rely on this.
Looking at the corrected data, there is essentially no difference in the per-transaction gas costs of our zkEVM and zkSync's. These numbers could change over time based on the blend of transactions that are happening on each chain, but as of today, state diffs don't appear to be saving any gas; both systems are sending around 120 bytes per transaction to the L1.
We are planning some optimizations here, but not with state diffs. Transactions themselves can be compressed, reducing gas costs while still guaranteeing the availability of transaction data. More on this soon!