# Partial withdrawals by AssetManager lead to user funds freeze

AssetManager.withdraw() returns true despite partial withdrawal from liquidity-squeezed adapters, while callers debit full amount, permanently freezing the unretrieved remainder.

- Payout: Contest reward
- Severity: medium
- Program: Union Finance
- Credited: hyh
- Disclosed: 2022-11-04
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2022-10-union-finance-judging/issues/50
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-10-union-finance%3AM-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** AssetManager.withdraw() and its callers (UserManager.unstake, UToken.borrow/redeem/removeReserves)
**Root cause:** withdraw() iterates money market adapters and silently leaves a `remaining` shortfall when adapters lack liquidity, yet returns true; callers update accounting by the full requested amount regardless.
**Steps:**
1. Call UserManager.unstake(amount) or UToken.borrow/redeem while a strategy adapter (Aave/Compound) has a liquidity squeeze
2. AssetManager.withdraw() transfers selfBalance plus whatever adapters can supply, leaving `remaining > 0`
3. withdraw() still returns true and only reduces balances by `amount - remaining`
4. Caller (unstake/borrow/redeem) debits/burns the full `amount` from user accounting
5. The `remaining` shortfall later becomes available but is unclaimable — excess unallocated strategy funds
**How found:** Code review of withdraw()'s return value and `remaining` handling versus how unstake/borrow/redeem/removeReserves consume the boolean and update accounting by full `amount`.
**Exploitation:** No attacker action; triggered by normal user unstake/borrow/redeem during a lending pool liquidity shortage, causing the shortfall to be written off from the user's accounting permanently.
**Insight:** When a withdraw-style function can partially succeed, check every caller: a boolean success return hides the shortfall, and callers that debit the requested amount rather than the actual transferred amount silently write off the difference.
