Ethereum & Merlin Differences

A number of technical details differ between Ethereum mainnet's EVM and Merlin's zkEVM.

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

EVM Opcodes

OpcodeStatusMerlin Behavior



Replaced by SENDALL. It sends remaining value to address, without any other status change.



It returns the hash of the contract bytecode from the zkEVM state tree without checking if the account is empty.



It returns "0" instead of a random number as in the EVM.



It returns the number of processable transactions, which is actually the number of transactions processed on the rollup, instead of current block's number.



It is the state root at the end of a processable transaction and is stored on the system smart contract. It returns all previous block hashes instead of just the last 256 blocks.



It is allowed in PUSH bytes to avoid runtime bytecode analysis.



Disabled. If the opcode is encountered, the transaction will be reverted.



Disabled. If the opcode is encountered, the transaction will be reverted.



Disabled. If the opcode is encountered, the transaction will be reverted.



Disabled. If the opcode is encountered, the transaction will be reverted.



Disabled. If the opcode is encountered, the transaction will be reverted.



Disabled. If the opcode is encountered, the transaction will be reverted.

MCOPY, TSTORE, TLOAD, BLOBHASH and BLOBBASEFEE are Opcodes from the Cancun upgrade. We recommend using shanghai as your EVM target and avoiding using a Solidity version higher than 0.8.23. Otherwise make sure you're not using those invalid opcodes.

BASEFEE is invalid because EIP-1559 is not supported. Make sure you're not using BASEFEE or block.basefee in solidity.

EVM Precompiles

The RIPEMD-160 (address 0x3), blake2f (address 0x9) are currently not supported. Calls to unsupported precompiled contracts will revert. These precompiles are rarely used—RIPEMD-160, for example, has been called a total of ~2,600 times since the inception of Ethereum.

The other EVM precompiles are well supported.

zkEVM State Trie

There are some differences between the Merlin zkEVM Merkle tree and EVM Merkle tree.

A zkEVM state is stored in the form of a Sparse Merkle Tree (SMT), which is a binary tree. Instead of Keccak-256, the POSEIDON Hash Function is used to build the SMTs, mainly due to its STARK-friendliness.

It is important to note that unlike the EVM tree, the zkEVM SMT does not add all the parameters in one leaf of the Merkle tree.

For convenience and achieving faster computations, each of the parameters (the nonce, balance, storage root and code hash) is stored in its respective leaf.

An additional parameter called codeHashLen is used to store the length of the Code hash. A fifth leaf-type is used for this zkEVM-specific parameter.

Also, each value in a leaf is an array of eight values, [V0,V1,V2,…,V7], where each Vi is a 32-bit element. That field is the Goldilocks prime field 𝐹𝑝 where 𝑝=2^64−2^32+1.

The 32-bit field elements are 8 in number, so as to be compatible with the 256-bit EVM words.

So although in the EVM context computation work with 256-bit words, internally the zkEVM uses 8 times 32-bit field elements.

Each of the values; V0,V1,V2,…,V7; is composed of the 32 less significant bits of the 63.99-bit Goldilocks prime field elements.

The figure below depicts the 5 leaf-types together with the corresponding keys:

Block Interval

Merlin aims for a constant block interval of 3 seconds. While it's 12 seconds in Ethereum under ideal conditions. Having a faster, constant block interval results in quicker feedback and a better user experience.

When the load is high, the block interval may slightly exceed 3 seconds, for example, 4 seconds.

When the last block of the previous batch contains fewer transactions, the timestamp of the next block may be the same, resulting in a block interval of 0 seconds.

Otherwise, a constant block interval of 3 seconds is ensured.

Transaction Fees

The fee structure in Merlin doesn't support EIP-1559.

In Merlin, developers had to specify a gasPrice value to send a transaction. After EIP-1559, the gasPrice parameter is replaced by maxFeePerGas and maxPriorityFeePerGas.

  • maxFeePerGas is the maximum amount of gas fee a user is willing to pay per unit of gas for a transaction.

  • maxPriorityFeePerGas is an optional fee that is set by the user. Using this variable, users may pay a premium for a high-priority transaction. Whenever a block reaches 100% capacity, this parameter determines transaction priority, the same as before EIP-1559.

Consider changing to a gasPrice value when maxFeePerGas and maxPriorityFeePerGas were used.

Sending Native Token (transfer, send, call)

There's no difference between Ethereum and Merlin sending native token(ETH in Ethereum or BTC in Merlin). However we strongly recommend not using some methods supported.

You can send native token to others by

  • transfer (2300 gas, throws error)

  • send (2300 gas, returns bool)

  • call (forward all gas or set gas, returns bool)

call in combination with re-entrancy guard is the only recommended method to use.

Though supported, transfer and send are not recommended to use. 2300 gas will cause serious problems like upgradeable contract cannot be receiver.

Last updated