# TransactionValidator checks intrinsic costs against wrong value

zkSync TransactionValidator compared intrinsic gas costs against total gasLimit instead of body gas after overhead, letting L1->L2 txs underpay and grief the operator.

- 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/1108
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-zksync%3AM-01
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm technique:source-review vuln:business-logic vuln:fee-bypass

**Target:** L1->L2 transaction gas validation in TransactionValidator
**Root cause:** validateL1ToL2Transaction checks getMinimalPriorityTransactionGasLimit against _transaction.gasLimit (total, including overhead) rather than the body gas limit remaining after overhead is subtracted, so overhead and intrinsic costs are validated separately instead of as a sum.
**Steps:**
1. Read TransactionValidator.validateL1ToL2Transaction: `require(getMinimalPriorityTransactionGasLimit(...) <= _transaction.gasLimit, "up")`.
2. Note getTransactionBodyGasLimit already subtracts overhead from totalGasLimit and returns actualGasLimit.
3. Observe the intrinsic-cost check uses totalGasLimit, not the body limit, so a tx with gasLimit = max(overhead, intrinsicCosts) passes.
4. Submit an L1->L2 tx covering only max(overhead, intrinsicCosts); it passes validation but lacks gas to execute on L2.
5. Bootloader processes it, incurs overhead and preparation costs, then the tx fails — operator bears the cost.
**How found:** Code reading of the fee model: compared the documented formula totalGasLimit = overhead + intrinsicCosts + executionCosts against the actual require in validateL1ToL2Transaction, noticing totalGasLimit was checked twice rather than the post-overhead body limit, and cross-checked the bootloader's correct subtraction order.
**Exploitation:** Attacker submits L1->L2 priority transactions whose gasLimit only covers max(overhead, intrinsicCosts). Because the priority queue forces the operator to process them, the operator pays overhead and intrinsic costs while the tx reverts on L2, enabling cheap spam/griefing.
**Insight:** When a fee/gas model documents a sum of components, verify each require compares against the correct derived sub-value (post-subtraction remainder), not the aggregate — mismatches let one component's payment mask another's shortfall.
