# Vulnerabilities in Deposit Limit Enforcement and the Impact on Failed Deposits

zkSync L1ERC20Bridge deposit limit check underflows on claimFailedDeposit when limit enabled after failed deposits, blocking claims and allowing limit bypass via pre-limit failed deposits.

- 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/425
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-zksync%3AM-11
- Tags: domain:web3 impact:dos impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:race-window technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** L1ERC20Bridge.sol _verifyDepositLimit and claimFailedDeposit
**Root cause:** _verifyDepositLimit only tracks totalDepositedAmountPerUser when a deposit limitation is active; claiming a failed deposit made before the limit was set subtracts from a zero balance (underflow revert) or resets the counter, enabling bypass.
**Steps:**
1. Deploy tokenX with no deposit limitation (limitData.depositLimitation = false).
2. Alice deposits 100 tokenX via L1ERC20Bridge; transaction fails on L2.
3. Owner calls AllowList.setDepositLimit to enable a cap for tokenX.
4. Alice calls claimFailedDeposit; _verifyDepositLimit executes `totalDepositedAmountPerUser[tokenX][Alice] -= 100` on a zero value, reverting with underflow.
5. For bypass: Bob observes owner's setDepositLimit tx for tokenY (cap=100).
6. Bob makes three deposits of 100 tokenY each with low _l2TxGasLimit so they fail on L2, before the limit is active.
7. After limit is set, Bob deposits 100 tokenY (counter=100).
8. Bob claims one pre-limit failed deposit, decreasing counter to 0.
9. Bob deposits another 100 tokenY, repeating claims to deposit 400 tokenY total against a cap of 100.
**How found:** Code review of _verifyDepositLimit and claimFailedDeposit revealed that the per-user deposit counter is only updated when a limitation exists, creating an inconsistency when limits are toggled on after failed deposits.
**Exploitation:** Attacker front-runs the setDepositLimit transaction by creating multiple failed deposits (low L2 gas) while no limit is enforced. After the cap is active, each claimFailedDeposit call subtracts the failed amount from totalDepositedAmountPerUser, resetting the counter and allowing repeated deposits beyond the cap.
**Insight:** When a cap or counter is conditionally updated, check state transitions: deposits made before the cap is enabled are not counted, so later claims can underflow or reset the counter. Look for asymmetric accounting in claim/refund paths.
