# Ineffective ReserveRatio Enforcement

Rubicon BathPair checked reserve ratio before placing market-making orders but not after, letting strategists deploy nearly all pool liquidity and leave LPs illiquid.

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

**Target:** BathPair.placeMarketMakingTrades / enforceReserveRatio (Solidity pool contract)
**Root cause:** enforceReserveRatio is called only at the start of placeMarketMakingTrades; the subsequent placeOffer calls withdraw underlying funds with no post-execution re-check, so the reserve ratio invariant is never validated after liquidity is deployed.
**Steps:**
1. Deposit 1 WETH and 100 DAI into BathWETH/BathDAI pools; set reserveRatio to 80 (20% utilizable).
2. As approved strategist call `placeMarketMakingTrades([WETH,DAI], askNum, askDen, bidNum, bidDen)` with ask pay_amt 0.9 WETH and bid pay_amt 90 DAI.
3. Pre-check passes since balances still equal underlyingBalance.
4. placeOffer moves 0.9 WETH and 90 DAI to the market; no post-check runs.
5. Observe balanceOf = 0.1 WETH / 10 DAI while underlyingBalance stays 1 WETH / 100 DAI: 90% utilized vs 20% limit.
**How found:** Read the whitepaper reserve-ratio invariant, then code-read placeMarketMakingTrades and noticed enforceReserveRatio is invoked before placeOffer with no second call after funds leave the pool.
**Exploitation:** A malicious or careless approved strategist places large ask/bid orders in one call, pulling nearly all pool liquidity into the Rubicon Market. LPs cannot withdraw and bear full market risk on deployed capital.
**Insight:** For invariant-style guards (reserve ratios, collateralization, caps), verify the check runs both before and after state-changing external calls; a pre-check alone is bypassable whenever the guarded action itself moves the funds.
