# Outstanding Amount Of A Pool Reduced Although Tokens Are Not Repaid

Scrubbing a strategist trade zeroes BathToken outstandingAmount without repayment, corrupting underlyingBalance and causing withdrawers to lose ~40% of funds.

- Payout: Contest reward
- Severity: medium
- Program: Rubicon
- Credited: xiaoming90
- Disclosed: 2022-08-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-05-rubicon-findings/issues/221
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-rubicon%3AM-15
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** BathPair/BathToken pool accounting (Solidity contracts)
**Root cause:** handleStratOrderAtID calls BathToken.removeFilledTradeAmount on any fill delta, decrementing outstandingAmount even though the underlying asset was never repaid to the pool; repayment only happens later at rebalance.
**Steps:**
1. Deposit 50 WETH + 50 DAI each from Alice and Bob into BathWETH/BathDAI (underlyingBalance=100, outstanding=0).
2. Strategist places ask/bid orders escrowing 40 WETH and 40 DAI; balanceOf drops to 60, outstanding rises to 40.
3. Charles fills the 40 WETH ask paying 800 DAI; 800 DAI sits on BathWETH pending rebalance, outstanding still 40.
4. Strategist calls `BathPair.scrubStrategistTrade()`; `handleStratOrderAtID` computes askDelta=40>0 and calls `BathToken.removeFilledTradeAmount(40)`.
5. BathWETH outstandingAmount drops 40->0 and underlyingBalance 100->60 despite no WETH repaid.
6. Alice withdraws her 50 bathWETH shares and receives ~29.99 WETH instead of ~49 WETH.
**How found:** Reading BathToken accounting invariant underlyingBalance = outstandingAmount + balanceOf and noticing removeFilledTradeAmount is invoked on fill during scrub, before any rebalance repays the underlying asset; confirmed with a Foundry-style test script.
**Exploitation:** No attacker needed; a routine strategist scrub (or a malicious/MIA strategist) triggers the accounting corruption. Withdrawers and depositors get mispriced shares, losing funds to the pool; a strategist could scrub to manipulate share pricing.
**Insight:** When an accounting invariant is split across two contracts (outstanding vs balanceOf), trace every mutation site of the debt counter and check it only fires on actual repayment, not on order-lifecycle events like cancel/scrub/fill.
