# Inconsistent balance accounting in stETH deposits leads to DOS of core functions and reward loss

Distributor.supply records the requested stETH amount instead of the actual rounded-down transfer, so lastUnderlyingBalance exceeds real balance and distributeRewards underflows, DoSing stake/withdraw/claim and losing yield.

- Payout: Contest reward
- Severity: medium
- Program: Morpheus
- Credited: KlosMitSoss
- Disclosed: 2025-09-11
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-08-morpheus#m-02
- Library page: https://bugbountylibrary.com/r/c4%3A2025-08-morpheus%3AM-02
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:dos vuln:precision-loss

**Target:** Distributor.supply / DepositPool._stake stETH accounting
**Root cause:** DepositPool._stake stores the actual balance delta, but Distributor.supply increments deposited and lastUnderlyingBalance by the requested amount_, which can exceed the real stETH received due to Lido share rounding down.
**Steps:**
1. Deploy DepositPool with stETH as depositToken and Strategy.NONE, migrate it, set reward pool timestamp.
2. Set stETH totalPooledEther to a value that triggers share rounding (e.g. 123456.789123456789).
3. Alice mints a fractional stETH amount (e.g. 1.000000001), approves the pool, and calls `stake(publicRewardPoolId, amount, 0, ZERO_ADDR)`.
4. Observe `depositPools(...).lastUnderlyingBalance > stETH.balanceOf(distributor)` because supply() added amount_ not the rounded-down delta.
5. Call `distributor.distributeRewards(publicRewardPoolId)`; it reverts with panic 0x11 (underflow) on `balance_ - lastUnderlyingBalance`.
6. Subsequent `stake()`/`withdraw()`/`lockClaim()`/claim calls that route through distributeRewards also revert until accrued yield exceeds the gap.
**How found:** Read the Lido integration guide comment in _stake noting stETH transfers can round down and leave 1-2 wei; noticed _stake correctly uses balance delta but Distributor.supply stores the requested amount_, then traced the mismatch into distributeRewards' subtraction.
**Exploitation:** Any staker triggers the desync by staking an amount whose stETH transfer rounds down; the inflated lastUnderlyingBalance then makes distributeRewards underflow, blocking stake/withdraw/lockClaim/claim for the pool until enough yield accrues, and that yield is silently excluded from rewards.
**Insight:** When a protocol mixes two accounting styles for the same token (balance-delta vs requested-amount), grep every place that increments stored balances for that token; rebasing/share tokens like stETH make the two diverge by wei and turn a subtraction into a permanent underflow DoS.
