# Redeem fee is incorrectly factored into bad debt mapping due to incorrect logic

Redeem fee deducted from debtTokenAmt made market.redeem return less than ftReserve, inflating bad-debt mapping and diluting collateral payouts in dealBadDebt.

- Payout: Contest reward
- Severity: medium
- Program: Term Structure
- Credited: 0xJoos
- Disclosed: 2025-03-20
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_term_structure_february2025.pdf#3.2.8
- Library page: https://bugbountylibrary.com/r/cantina%3Aterm_structure_february2025%3A3.2.8
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** TermMaxVault/OrderManager redeem and bad-debt accounting
**Root cause:** OrderManager::_redeemFromMarket treats any totalRedeem < ftReserve as bad debt, but TermMaxMarket::_redeem subtracts the redeem fee from the returned amount, so the fee is misclassified as bad debt.
**Steps:**
1. Set `redeemFeeRatio` to `1000000` in testdata.json
2. Curator creates order in market2 and deposits debt tokens into vault
3. Taker swaps debt for xt to create a redeemable position
4. Warp time, record `vault.badDebtMapping(collateral)` before
5. Curator calls `vault.redeemOrder(order2)`
6. Observe `badDebtAfter - badDebtBefore == 10050000000` (the feeAmt), proving fee counted as bad debt
**How found:** Code reading of _redeemFromMarket's `totalRedeem < ftReserve` bad-debt branch against _redeem's fee subtraction, noticing the fee makes the condition always true when redeemFeeRatio>0.
**Exploitation:** Curator redeems an order in a market with nonzero redeemFeeRatio; the fee reduces totalRedeem below ftReserve, writing an inflated _badDebtMapping[collateral]. Later dealBadDebt distributes collateral proportionally against the inflated badDebtAmt, diluting each recipient's collateralOut.
**Insight:** When a downstream function returns a net amount after fees, any caller comparing it to the gross input as a 'shortfall' will misclassify fees as losses; check every `returned < expected` branch for fee-adjusted returns.
