# Funds locked due to missing transfer check

PrizeVault burns all shares then transfers assets with unchecked ERC-20 transfer; a lossy ERC-4626 redeem plus non-reverting token silently locks user funds.

- Payout: Contest reward
- Severity: medium
- Program: PoolTogether
- Credited: CodeWasp
- Disclosed: 2024-04-04
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-03-pooltogether-findings/issues/235
- Library page: https://bugbountylibrary.com/r/c4%3A2024-03-pooltogether%3AM-06
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** PrizeVault.sol _withdraw / ERC-4626 yield vault redemption
**Root cause:** _withdraw redeems previewWithdraw(assets) shares from the yield vault, which may return fewer assets than requested, then calls _asset.transfer without checking the boolean return; a non-reverting ERC-20 leaves the shortfall silently untransferred after shares are burned.
**Steps:**
1. Deploy a non-reverting ERC-20 (transfer returns false on insufficient balance) and an ERC-4626 yield vault whose redeem(previewWithdraw(assets)) yields fewer assets than assets.
2. Alice approves and calls `vault.deposit(1e18, alice)`; all assets move to yield vault, shares minted.
3. Alice calls `vault.withdraw(1e18, alice, alice)`.
4. Contract computes `_yieldVaultShares = yieldVault.previewWithdraw(assets)`, calls `yieldVault.redeem(...)` receiving < assets, then `_asset.transfer(receiver, assets)` returns false without reverting.
5. Prize vault shares burned, assets stranded in PrizeVault; subsequent `withdraw(1)`/`redeem(1)` revert.
**How found:** Reasoned from ERC-20 spec (transfer need not revert on insufficient balance) and ERC-4626 spec (redeem only guarantees burning shares, not delivering assets), then combined both corner cases into a PoC with a mock non-reverting token and lossy yield vault.
**Exploitation:** Precondition: a yield vault that under-delivers on redeem (or malicious) and an ERC-20 that returns false instead of reverting. User's shares are burned while the transfer silently fails, permanently locking the assets in the PrizeVault with no recovery path.
**Insight:** When a contract burns shares before transferring assets, check both the ERC-20 return value and whether the upstream vault actually delivered the requested assets; ERC-4626 redeem(previewWithdraw(x)) is not guaranteed to yield x, so use withdraw(x) or verify balance deltas.
