# User can avoid bankrupting by calling PositionManager.moveLiquidity where to index is bankrupted index

PositionManager.moveLiquidity never checks the destination bucket for bankruptcy, letting an attacker reset a bankrupt bucket's depositTime and redeem zeroed LP shares.

- Payout: Contest reward
- Severity: high
- Program: Ajna Protocol
- Credited: rvierdiiev
- Disclosed: 2023-06-29
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-05-ajna-findings/issues/179
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-ajna%3AH-09
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** PositionManager.moveLiquidity / Pool.moveQuoteToken
**Root cause:** moveLiquidity validates bankruptcy only on the fromIndex bucket; the toIndex bucket's bankruptcyTime is never compared against the incoming depositTime, so moving liquidity into a bankrupt bucket overwrites its depositTime and makes previously-zeroed LP redeemable.
**Steps:**
1. Attacker holds LP in a bucket that later goes bankrupt (LP zeroed, bankruptcyTime set).
2. Mint a small amount of LP in a fresh non-bankrupt bucket and memorialize that index to the tokenId.
3. Call `PositionManager.moveLiquidity({pool, tokenId, fromIndex: freshIndex, toIndex: bankruptIndex, expiry})`.
4. moveQuoteToken moves quote into bankrupt bucket; toPosition.depositTime is set to fromPosition.depositTime (> bankruptcyTime).
5. Call `redeemPositions` on the bankrupt index; the depositTime > bankruptcyTime check now passes and attacker redeems LP shares belonging to other users.
**How found:** Code review of moveLiquidity: noticed fromIndex bankruptcy check (`depositTime <= bankruptcyTime`) but no equivalent check on toIndex, and that toPosition.depositTime is overwritten with fromPosition.depositTime — the same field redeemPositions uses to gate bankrupt buckets.
**Exploitation:** Attacker with pre-existing LP in a bucket that went bankrupt mints minimal LP in a healthy bucket, then moves it into the bankrupt bucket via moveLiquidity. The depositTime overwrite bypasses the BucketBankrupt guard in redeemPositions, letting the attacker withdraw LP that should have been zeroed, stealing other LPs' funds held by PositionManager.
**Insight:** When a function copies a timestamp/state field (depositTime) into a destination record, check every guard that reads that field on the destination — here redeemPositions' bankruptcy check was bypassed because only the source was validated.
