zkSync Era RPC: What's Different from Ethereum

zkSync Era is EVM-compatible, so you can point ethers or viem at it and eth_blockNumber, eth_call, and eth_getLogs just work. That lulls people into treating it like any other EVM chain — and then a contract deployment fails, a gas estimate is wildly off, or a transaction reverts for reasons that make no sense on Ethereum. zkSync is a zkEVM, not an EVM clone, and the differences show up exactly where you interact with it over RPC. Here's the map.

It's compatible, not equivalent

Ethereum L2s come in two flavors: EVM-equivalent (run standard Ethereum bytecode as-is, like the OP-stack chains) and EVM-compatible (support Solidity but compile to a different VM). zkSync Era is the latter — Solidity/Vyper compile through zksolc/zkvyper to zkSync's own bytecode. For reading state, this is invisible; for deploying and low-level work, it matters a lot.

Mainnet chain ID is 324. Standard read RPC works:

import { createPublicClient, http } from "viem";
import { zksync } from "viem/chains";

const client = createPublicClient({
  chain: zksync,
  transport: http("https://rpc.swiftnodes.io/rpc/zksync?key=YOUR_API_KEY"),
});
await client.getBlockNumber();   // just works

The four things that actually differ

1. Native account abstraction — every account is a smart account. On Ethereum, EOAs and contracts are different beasts. On zkSync, account abstraction is built into the protocol: accounts can carry validation logic, and transactions can be sponsored by paymasters. This introduces a zkSync-specific transaction type (EIP-712 / type 0x71) for AA and paymaster flows. Standard type-2 transactions still work for basic transfers, but if you want AA or gasless UX, you're building type-113 transactions — which the standard ethers sendTransaction won't construct for you. That's what the zksync-ethers SDK is for.

2. The zks_ RPC namespace. Alongside eth_*, zkSync exposes L2-specific methods under zks_. The ones you'll actually reach for:

Method What it gives you
zks_L1BatchNumber current L1 batch (see finality below)
zks_estimateFee accurate fee incl. pubdata (see gas below)
zks_getL2ToL1LogProof proof for L2→L1 messages (withdrawals)
zks_getAllAccountBalances all token balances for an address in one call
zks_getBridgeContracts canonical bridge addresses
zks_getBlockDetails L2 block + which L1 batch it's in

If your provider only proxies eth_* and strips zks_, half of zkSync-native development breaks. A zkSync endpoint needs to pass the zks_ namespace through.

3. The gas model includes pubdata. This is the single biggest "why is my estimate wrong" gotcha. zkSync's cost has two parts: L2 execution gas and the cost of the pubdata it posts to Ethereum L1 (gasPerPubdataByteLimit). A plain eth_estimateGas doesn't capture the pubdata dimension well, so estimates can be off — sometimes a lot. Use zks_estimateFee (or let zksync-ethers handle it) for transactions that write meaningful state:

curl -s -X POST https://rpc.swiftnodes.io/rpc/zksync?key=YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"zks_estimateFee","params":[{"from":"0x…","to":"0x…","data":"0x"}],"id":1}'

4. Deployment is not standard. You can't take EVM bytecode and eth_sendRawTransaction a deployment the Ethereum way. Contracts are deployed through a system contract (ContractDeployer), the bytecode must be zksolc-compiled, and CREATE/CREATE2 address derivation differs from Ethereum — so an address you precompute with standard CREATE2 math will be wrong. Use hardhat-zksync / zksync-ethers for deploys; don't expect a vanilla Hardhat/Foundry deploy script to work unchanged.

Blocks, batches, and finality

zkSync has two levels you'll see over RPC:

  • L2 blocks — produced by the sequencer every ~1 second. eth_blockNumber returns these. This is your fast, soft-confirmed view (see What Is a Sequencer?).
  • L1 batches — many L2 blocks are rolled into a batch that gets committed, proven, and executed on Ethereum. zks_L1BatchNumber and zks_getBlockDetails tell you which batch a block belongs to and its status.

Finality mirrors the L2 pattern: the sequencer gives you near-instant soft confirmation, but hard finality only arrives once the batch's validity proof is verified and executed on L1 — which can take hours. If you're doing anything settlement-sensitive (large withdrawals, bridging), check batch execution status via zks_, don't trust the L2 block alone. This is the same soft-vs-hard distinction we covered in Soft vs Hard Finality on L2s — and the pubdata that gets posted is part of Ethereum's blob data-availability story.

What carries over unchanged

Don't overcorrect — most of your read-side tooling is fine:

  • eth_call, eth_getBalance, eth_getLogs, eth_getTransactionReceipt, eth_blockNumber all behave normally.
  • WebSocket subscriptions (eth_subscribe) for new heads and logs work.
  • Event indexing works like any EVM chain (mind reorgs at the sequencer level — see Handling Chain Reorgs).
  • Reading contract state via ABI + eth_call is identical.

The rule of thumb: reading zkSync is standard EVM; writing and deploying to it is zkSync-specific. Reach for zksync-ethers and the zks_ methods the moment you go beyond reads.

The short version

zkSync Era gives you the familiar eth_* surface for reads, then diverges where it counts: native account abstraction (type-113 txs, paymasters), a zks_ namespace for L2 data and proofs, a two-part gas model where pubdata dominates (use zks_estimateFee), and non-standard deployment (zksolc, ContractDeployer, different CREATE2). Treat reads as normal EVM and writes as zkSync-native, and it stops surprising you.

For that you need an endpoint that passes through both eth_* and zks_* — many generic proxies drop the latter. Our zkSync RPC exposes the full namespace, flat-rate, alongside dozens of other chains under one key. Grab a free key and connect:

https://rpc.swiftnodes.io/rpc/zksync?key=YOUR_API_KEY

Related posts

  • Scroll RPC: The zkEVM That Runs Ethereum Bytecode Unchanged

    Scroll is a bytecode-equivalent zkEVM rollup (chain ID 534352) — it targets opcode-level equality with Ethereum, so your existing contracts, audits, and tooling work with zero changes, while ZK validity proofs give ~1-hour hard finality instead of the 7-day optimistic window. Standard eth_* (viem/ethers/foundry). Here's the developer map, including how it differs from zkSync Era.

  • Manta Pacific RPC: An OP Stack L2 Whose Data Doesn't Live on Ethereum

    Manta Pacific is an EVM OP Stack L2 (chain ID 169) that posts its data availability to Celestia instead of Ethereum — making it a validium, and making it cheap. It's standard EVM (viem/ethers/foundry work), with ETH gas and a ~7-day optimistic withdrawal window. Here's the developer map, including what modular DA changes about how you reason on security.

  • Metis RPC: The Optimistic Rollup That Decentralized Its Sequencer

    Metis is an EVM-equivalent optimistic rollup (chain ID 1088) notable for moving from a single sequencer to a sequencer pool — reducing the single-operator risk most L2s still carry. It's standard EVM (viem/ethers/foundry work), with METIS (not ETH) as gas. Here's the developer map, including what sequencer decentralization does and doesn't change for you.

Try SwiftNodes free — multi-chain RPC across 75+ networks, flat-rate pricing, pay by card or crypto, no KYC. Get an API key in 30 seconds →