# Incorrect bad debt accounting can lead to a state where the claimFeesBeneficial function is permanently bricked and no new incentives can be distributed, potentially locking pending and future protocol fees in the FeeManager contract

Partial liquidations increment global bad debt but overwrite per-position bad debt, leaving phantom totalBadDebtETH that permanently bricks claimFeesBeneficial and locks protocol fees.

- Payout: Contest reward
- Severity: high
- Program: Wise Lending
- Credited: JCN
- Disclosed: 2024-05-03
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2024-02-wise-lending-findings/issues/74
- Library page: https://bugbountylibrary.com/r/c4%3A2024-02-wise-lending%3AH-03
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:incomplete-fix technique:source-review vuln:accounting-error vuln:business-logic vuln:dos

**Target:** FeeManager / WiseSecurity bad debt accounting in lending protocol
**Root cause:** checkBadDebtLiquidation increments totalBadDebtETH by the full diff each partial liquidation while setBadDebtUserLiquidation overwrites badDebtPosition[_nftId] with only the latest diff, so global bad debt is inflated relative to per-position debt and cannot be fully paid back.
**Steps:**
1. Deploy WiseLending with mock tokens; set collateral token price 1:1 with ETH.
2. Lender deposits 10 ETH; borrower deposits 10 collateral tokens and borrows 5 ETH.
3. Crash collateral price to 0.3 ETH via mock Chainlink.
4. Lender calls `liquidatePartiallyFromTokens(nftIdLiquidatee, nftIdLiquidator, WETH, ERC20_2, debtShares*2e16/1e18+1)`; totalBadDebtETH and badDebtPosition both increase equally.
5. Lender calls `liquidatePartiallyFromTokens(...)` again with small amount; totalBadDebtETH += new diff while badDebtPosition is overwritten to new diff.
6. Lender calls `paybackBadDebtNoReward(nftIdLiquidatee, WETH, paybackShares)`; badDebtPosition resets to 0 but totalBadDebtETH stays > 0.
7. Call `claimFeesBeneficial(address(0),0)` reverts with `ExistingBadDebt()`.
**How found:** Code review of FeeManager bad debt gating (totalBadDebtETH == 0) and comparison of the increment-only update in checkBadDebtLiquidation against the delta-based update in _updateUserBadDebt, spotting the inconsistency between global increment and per-position overwrite.
**Exploitation:** No attacker capital needed; the state arises naturally from partial liquidations after a market crash. Once totalBadDebtETH is permanently > 0, beneficials cannot claim fees and claimWiseFees stops distributing incentives, while anyone can still permissionlessly call claimWiseFees to lock pending/future fees in FeeManager.
**Escalation:** Permissionless claimWiseFees lets any actor continue funneling fees into the bricked FeeManager, burning protocol value until admin sets fees to 0.
**Insight:** When a global accumulator is gated on zero and a per-entity value is overwritten rather than delta-adjusted, partial operations (partial liquidations, partial repayments) can desync them permanently; audit every place a global counter is incremented vs. set and check whether the corresponding decrement path can ever reach zero.
