# Incorrect handling of PnL during liquidation

Flatmoney liquidation double-counts PnL: updateStableCollateralTotal already accounts for profitLoss, then updateGlobalPositionData applies -profitLossTotal again, inflating LP stable collateral and desyncing tracked vs actual balance.

- Payout: Contest reward
- Severity: high
- Program: Flatmoney
- Credited: santipu_
- Disclosed: 2023-12-11
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2023-12-flatmoney-judging/issues/180
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-12-flatmoney%3AH-3
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** LiquidationModule.liquidate / FlatcoinVault.updateGlobalPositionData (Solidity perp DEX)
**Root cause:** liquidate() adjusts stableCollateralTotal by (remainingMargin - profitLoss) and then calls updateGlobalPositionData, which again applies -profitLossTotal to stableCollateralTotal, double-counting the position's PnL.
**Steps:**
1. Set up a long position with margin, accrued funding and PnL such that settledMargin > 0 but < expectedLiquidationFee (e.g. margin +20, funding -100, PnL +100, liqMargin 30, liqFee 25).
2. Call `liquidate(tokenId)`; settleFundingFees() moves funding between marginDepositedTotal and stableCollateralTotal.
3. Observe `vault.updateStableCollateralTotal(int256(remainingMargin) - positionSummary.profitLoss)` at LiquidationModule.sol#L133.
4. Observe subsequent `vault.updateGlobalPositionData(...)` at #L159 which internally calls `_updateStableCollateralTotal(-profitLossTotal)` at FlatcoinVault.sol#L205.
5. Compare resulting stableCollateralTotal and marginDepositedTotal against actual vault balance; they diverge (e.g. tracked 125 vs actual 120 ETH).
**How found:** Code-reading of the liquidation path: noticed profitLoss is subtracted in updateStableCollateralTotal inside liquidate() and again via -profitLossTotal inside updateGlobalPositionData, then validated with three numeric worked examples.
**Exploitation:** Any liquidation of a position with nonzero PnL triggers the double adjustment; LPs' stableCollateralTotal is inflated (or deflated) relative to real collateral, letting users withdraw more than the vault holds and desyncing accounting.
**Escalation:** Repeated liquidations across positions compound the accounting drift, potentially making the vault insolvent for withdrawals.
**Insight:** When a close/liquidation path both directly adjusts a global accounting aggregate and calls a shared updater that recomputes the same PnL, check for double application; trace every write to stableCollateralTotal/marginDepositedTotal across the call graph and reconcile against actual token balance.
