# Unit difference between transaction encoding and bootloader memory constant

zkSync bootloader overhead formula divides byte-denominated encoding length by a word-denominated memory constant, overcharging users ~32x on some transactions.

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

**Target:** bootloader.yul and TransactionValidator.sol overhead calculation
**Root cause:** EncodingLength is in bytes while BOOTLOADER_TX_ENCODING_SPACE/BOOTLOADER_MEMORY_FOR_TXS (273132) is in 32-byte words, making the denominator 32x too small and overhead 32x too large.
**Steps:**
1. Read TransactionValidator.getOverheadForTransaction: `Math.ceilDiv(_encodingLength * batchOverheadGas, BOOTLOADER_TX_ENCODING_SPACE)`
2. Read bootloader.getTransactionUpfrontOverhead: `ceilDiv(safeMul(txEncodeLen, totalBatchOverhead), BOOTLOADER_MEMORY_FOR_TXS())`
3. Note _encodingLength = abi.encode(tx).length is bytes; constant 273132 derives from word-index range [250141..523261]
4. Conclude denominator is 32x smaller than intended, inflating overhead 32x
**How found:** Traced bootloader memory slot indices to derive the constant's unit, compared against byte-denominated encoding length in the Solidity validator.
**Insight:** When a constant is derived from memory slot indices, verify its unit against the variable it divides; slot ranges are word-indexed, not byte-indexed.
