# Divergences in the Simulation of the extcodehash EVM Opcode

zkSync Era's getCodeHash returns bytes32(0) for empty-balance accounts and keccak256("") for precompiles regardless of balance, diverging from EIP-1052 extcodehash semantics.

- Payout: Contest reward
- Severity: medium
- Program: zkSync
- Credited: anon
- Disclosed: 2024-02-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-10-zksync-findings/issues/133
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-zksync%3AM-19
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** AccountCodeStorage.getCodeHash system contract emulating extcodehash opcode
**Root cause:** getCodeHash ignores account balance when deciding emptiness and treats all precompile addresses uniformly, whereas EIP-1052/EIP-161 define empty accounts by code+nonce+balance and precompiles by balance.
**Steps:**
1. Read AccountCodeStorage.sol getCodeHash at L89.
2. Compare against EIP-1052: empty account (no code, nonce 0, balance 0) => bytes32(0); account with balance but no code => keccak256("").
3. Observe zkSync returns bytes32(0) for a balance-bearing account with no code/nonce.
4. Observe zkSync returns keccak256("") for precompile addresses (<= CURRENT_MAX_PRECOMPILE_ADDRESS) regardless of balance.
5. Contrast with EVM where extcodehash(0x02) with nonzero balance is keccak256("") but zero-balance precompile is bytes32(0).
**How found:** Code-reading of the getCodeHash implementation against the EIP-1052 spec, spotting that balance is not consulted in the emptiness check and precompile handling is address-range based.
**Insight:** When auditing EVM-equivalence layers, diff each opcode's implementation against its EIP line-by-line, especially edge cases involving balance/nonce in account-emptiness checks and precompile address ranges.
