# Improper handling of cases when withdrawable assets = 0

PoolTogether Vault's _currentExchangeRate returns 1:1 when yieldVault withdrawable assets are 0, falsely marking an empty vault as collateralized and enabling deposits/liquidations.

- Payout: Contest reward
- Severity: medium
- Program: PoolTogether
- Credited: ktg
- Disclosed: 2023-09-27
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-07-pooltogether-findings/issues/180
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-20
- 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:** Vault/YieldVault exchange-rate and collateralization logic (Solidity)
**Root cause:** _currentExchangeRate returns _assetUnit when _withdrawableAssets == 0, so _isVaultCollateralized() reports true for a vault with no withdrawable assets, unlike the _withdrawableAssets == 1 case which yields rate 0 and under-collateralized.
**Steps:**
1. Deploy Vault with YieldVault; alice deposits 1000e18 underlying.
2. Burn entire underlying balance held by yieldVault so maxWithdraw(vault) == 0.
3. Observe vault.exchangeRate() == 10**18 and vault.isVaultCollateralized() == true.
4. Contrast: burn all but 1 token; maxWithdraw == 1, exchangeRate == 0, isVaultCollateralized() == false.
5. Run via `forge test --match-test testZeroWithdrawableAmount -vvvv`.
**How found:** Code review of _currentExchangeRate/_isVaultCollateralized; noticed the special-case branch returning _assetUnit when _withdrawableAssets == 0 and compared against the _withdrawableAssets == 1 edge case to expose the inconsistency.
**Exploitation:** Withdrawable assets = 0 makes maxDeposit return type(uint96).max and liquidate/mintYieldFee pass the collateralized check, so users can deposit or liquidate into an under-collateralized vault based on a misleading 1:1 exchange rate, suffering economic loss.
**Insight:** When auditing exchange-rate math, test boundary values 0 and 1 separately: a guard that special-cases zero (returning a neutral 1:1 rate) can invert the collateralization verdict versus the near-zero case, and downstream maxDeposit/liquidate gates trust that verdict.
