# LP tokens are not sent back to withdrawing user

IchiVaultSpell.withdrawInternal omits refunding the LP tokens held back for the user, permanently stranding them in the Spell contract.

- Payout: Contest reward
- Severity: high
- Program: Blueberry
- Credited: rvierdiiev
- Disclosed: 2023-02-01
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-02-blueberry-judging/issues/151
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-blueberry%3AH-3
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** IchiVaultSpell.sol withdrawInternal (ERC1155 LP collateral)
**Root cause:** withdrawInternal computes amtLPToRemove = vault.balanceOf(this) - amountLpWithdraw to hold back LP tokens for the user, but only calls doRefund(borrowToken) and doRefund(collToken), never refunding the vault LP token.
**Steps:**
1. User calls closePosition() or closePositionFarm() with amountLpWithdraw > 0.
2. withdrawInternal runs doTakeCollateral(vault, lpTakeAmt) to pull LP from ERC1155 collateral.
3. amtLPToRemove = vault.balanceOf(address(this)) - amountLpWithdraw; vault.withdraw(amtLPToRemove, address(this)).
4. Contract swaps non-borrow token, doWithdraw(collToken, amountShareWithdraw), doRepay(borrowToken, amountRepay), _validateMaxLTV.
5. Only doRefund(borrowToken) and doRefund(collToken) execute; the amountLpWithdraw LP tokens remain in the Spell.
6. Fix: add `doRefund(address(vault));` after the existing refunds.
**How found:** Code review of withdrawInternal's refund sequence: noticed the function deliberately computes a held-back LP amount but the refund calls only cover borrowToken and collToken, revealing the missing transfer.
**Insight:** When a function computes a 'remainder' or 'held-back' amount, trace every branch that should return it to the user; refund helpers are easy to forget for the collateral token itself.
