# DoSed liquidations as `PrizeVault::liquidatableBalanceOf()` does not take into account the `mintLimit` when the token out is the asset

PrizeVault.liquidatableBalanceOf overestimates liquidatable yield when tokenOut is the asset, ignoring the mint limit, so maxAmountOut-based liquidations revert near the TWAB supply cap.

- Payout: Contest reward
- Severity: medium
- Program: PoolTogether
- Credited: 0x73696d616f
- Disclosed: 2024-05-10
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-05-pooltogether-judging/issues/88
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AM-9
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:business-logic vuln:dos vuln:liquidation

**Target:** PrizeVault / TpdaLiquidationPair liquidation flow (Solidity)
**Root cause:** liquidatableBalanceOf() caps _maxAmountOut at maxYieldVaultWithdraw + latent balance when _tokenOut == _asset, but does not cap it so the yield fee minted in transferTokensOut() stays within the mint limit; the fee is enforced via _enforceMintLimit after the fact.
**Steps:**
1. Note liquidatableBalanceOf() returns _liquidYield.mulDiv(FEE_PRECISION - yieldFeePercentage, FEE_PRECISION) with _maxAmountOut = maxYieldVaultWithdraw + asset.balanceOf(this) when _tokenOut == _asset.
2. Observe transferTokensOut() mints _yieldFee = (_amountOut * FEE_PRECISION)/(FEE_PRECISION - yieldFeePercentage) - _amountOut and calls _enforceMintLimit(_totalDebtBefore, _yieldFee).
3. Set vault near TWAB_SUPPLY_LIMIT so remaining mintLimit is small (e.g. 10 shares, 1% fee, 10000 yield available).
4. Call maxAmountOut() -> returns ~9900; call swapExactAmountOut with that amount -> fee 100 > mintLimit 10 -> _enforceMintLimit reverts.
5. Correct cap: mintLimitDueToFee = (FEE_PRECISION - yieldFeePercentage) * _mintLimit(_totalDebt) / yieldFeePercentage; min with _maxAmountOut.
**How found:** Code reading of liquidatableBalanceOf() vs transferTokensOut(): noticed the _tokenOut == _asset branch omits the mint-limit cap that the address(this) branch applies, while a yield fee is still minted and limit enforced downstream.
**Exploitation:** Liquidation bots follow docs: call maxAmountOut() then swapExactAmountOut() with that value. Near the mint limit the tx reverts in _enforceMintLimit, wasting gas and delaying liquidation; the TPDA price decays with elapsed time, so vault users receive less prize-token contribution.
**Escalation:** Time-sensitive TPDA auction: each failed/reverted liquidation lowers the price, reducing prize pool contribution for all vault users; first bot loses the race/profit.
**Insight:** When a view function computes a 'max' that feeds a state-changing path enforcing a separate cap (mint limit, debt ceiling, supply cap), check whether the view inverts the fee/limit formula; here the fee formula must be algebraically inverted to derive the true max input.
