# LiquidityPool can be DoS when a complete withdrawal is performed

LiquidityPool.getTokenPrice() divides by zero when totalSupply hits zero but rounding leaves totalFunds non-zero, bricking deposits after full withdrawal.

- Payout: Contest reward
- Severity: medium
- Program: Polynomial Protocol
- Credited: peakbolt
- Disclosed: 2023-08-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-03-polynomial-findings/issues/157
- Library page: https://bugbountylibrary.com/r/c4%3A2023-03-polynomial%3AM-08
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:dos

**Target:** LiquidityPool.getTokenPrice() / deposit flow
**Root cause:** getTokenPrice() only guards totalFunds==0, not totalSupply==0; rounding during partial withdrawals leaves a small non-zero totalFunds after totalSupply reaches zero, causing divWadDown by zero.
**Steps:**
1. Open and close a long to accrue fees so totalFunds is non-zero.
2. `pool.queueWithdraw(1_000_000e18, address(this))` then `skip(14500)`.
3. `pool.processWithdraws(1)` triggers partial withdrawal with rounding.
4. `pool.rebalanceMargin(-70567200000000000000000)` to return perp margin.
5. `pool.processWithdraws(1)` completes withdrawal, totalSupply=0 but totalFunds>0.
6. `pool.deposit(1_000_000e18, address(this))` reverts in getTokenPrice() division by zero.
**How found:** Code review of getTokenPrice() noting the totalFunds==0 guard but no totalSupply==0 guard, then reasoning that withdrawal rounding leaves residual totalFunds after full exit.
**Exploitation:** Any LP performing a full withdrawal leaves dust totalFunds with zero totalSupply; subsequent deposit calls revert, permanently bricking the pool and requiring redeployment.
**Insight:** When a share-price function guards one zero denominator input, check every path that can zero the other (totalSupply) while leaving value non-zero; withdrawal rounding dust is a classic trigger.
