# A liquidated position possibly cannot be closed

Rubicon Position.closePosition assumes full original position size, so a Compound-liquidated leveraged position reverts on close, leaving cTokens and debt stuck.

- Payout: Contest reward
- Severity: medium
- Program: Rubicon
- Credited: thekmj
- Disclosed: 2025-02-17
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-04-rubicon-findings/issues/683
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-rubicon%3AM-19
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:liquidation

**Target:** Position.sol leverage wrapper over Compound V2
**Root cause:** closePosition() un-loops using the full amount recorded at open time and never accounts for partial liquidation, so it tries to undo collateral/debt that no longer exists and reverts.
**Steps:**
1. Open 2x leveraged long via `buyAllAmountWithLeverage(testCoin, testStableCoin, TEST_AMOUNT, x2_332)`.
2. Drop cToken price: `priceOracle.addCtoken(cToken, 0.5e18)`; set `_setCloseFactor(0.5e18)` and `_setLiquidationIncentive(0.1e18)`.
3. Liquidator calls `cTokenStable.liquidateBorrow(Position.address, 12e6, cToken)`.
4. Owner calls `Position.closePosition(1)` — reverts.
5. Assert `cToken.balanceOf(Position) > 0` and `cTokenStable.borrowBalanceStored(Position) > 0`.
**How found:** Code reading of closePosition's un-looping logic against Compound liquidation semantics; hypothesized that liquidation changes collateral/debt balances the contract never tracks, then confirmed with a Hardhat POC.
**Exploitation:** Not attacker-profitable directly; a liquidation event (normal protocol behavior) bricks the position's close path, forcing users to manually repay via Compound repayBehalf and withdraw cTokens, risking mismanagement across cross-margin positions.
**Insight:** When a wrapper contract mirrors an external lending position, check every state-mutating path the underlying protocol can take (liquidation, interest accrual) against the wrapper's cached assumptions — close/redeem functions that replay the open amount will revert after any external balance change.
