# Incorrect Margin Calculation Due to Inconsistent Realized and Unrealized Balances

Hmx liquidation handler applies collateral factors to realized and unrealized settlement balances separately, so shifting PnL between states changes margin and can trigger wrongful liquidation.

- Payout: Contest reward
- Severity: high
- Program: Hmx
- Credited: Tripathi
- Disclosed: 2025-02-17
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_hmx_january2025.pdf#3.1.1
- Library page: https://bugbountylibrary.com/r/cantina%3Ahmx_january2025%3A3.1.1
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:cantina sev:high tech:evm tech:solidity technique:source-review vuln:business-logic vuln:liquidation vuln:oracle-manipulation

**Target:** LiquidationHandler margin calculation (Solidity perp/asset service)
**Root cause:** Collateral factor is applied only to the positive unsettled (unrealized) balance while realized margin is factored separately, so identical economic positions with different realized/unrealized splits yield different total margin.
**Steps:**
1. Set USDC collateral factor to 9000 bps via `assetService.setCollateralFactor(address(USDC), 9000)`.
2. Scenario A: `adjustCollateral(aliceSubId, USDC, 600e6)` realized +600, mock `getSubaccountTotalUnrealizedPNLAndFundingFee` to return -500e18 → margin = 600*0.9 - 500 = 40.
3. Scenario B: `adjustCollateral(aliceSubId, USDC, -100e6)` realized +500, mock unrealized to -400e18 → margin = 500*0.9 - 400 = 50.
4. Observe collateral1 != collateral2 despite identical net position, proving margin depends on realized/unrealized split.
**How found:** Code reading of `_totalMarginWithUnsettledE18` in LiquidationHandler revealed collateral factor applied only when `_unsettledE18 > 0`, prompting a hypothesis that moving loss between realized and unrealized states changes margin; confirmed with a mock-based unit test.
**Insight:** When auditing margin/health-factor math, check whether collateral factors are applied uniformly across realized vs unrealized components; any asymmetric haircut on one bucket lets users (or liquidators) shift value between buckets to move the health score.
