Core Invariants
1. Zero-Sum PnL
Fees (trading fees, liquidation penalties, oracle fees) are extracted separately and distributed to fee destinations. They are not part of the zero-sum equation.
2. Margin Lock
3. IM Greater Than MM
Config.setMarginConfig() and cannot be violated at any time. The gap between IM and MM provides a buffer zone — traders must deposit more margin than the liquidation threshold, giving them room to absorb adverse price movements before liquidation.
4. Position Status Consistency
OPEN to CLOSED only. An open position always has NONE as its close reason. A closed position always has exactly one of the three valid close reasons. Positions cannot be reopened once closed.
5. Snapshot Immutability
Snapshotted parameters are set at position open and never modified:| Snapshotted Field | Purpose |
|---|---|
snapshotImBps | Initial margin factor at time of open |
snapshotMmBps | Maintenance margin factor at time of open |
snapshotTradingFeeBps | Trading fee rate at time of open |
snapshotLiquidationPenaltyBps | Liquidation penalty at time of open |
snapshotOracleFee | Oracle fee at time of open |
marginMode | Margin mode (ISOLATED or CROSS) at time of open |
6. Exposure Consistency
7. Margin Bounded by Notional
addPositionMargin. It prevents economically irrational positions where the collateral exceeds the exposure.
8. Global Margin Accounting
9. Fee Destination Sum
Config.setFeeDestinations(). Every fee collected is fully distributed — no portion is lost or unaccounted for.
Solvency Mechanism
The protocol uses multiple layers of defense to maintain solvency:Isolated Margin
Isolated Margin
Each position’s losses are capped at its locked margin (
imLocked). A position going deeply underwater cannot drain the trader’s free collateral or affect their other positions. This containment prevents cascading losses within a single account.Bad Debt Absorption
Bad Debt Absorption
When a position’s market PnL loss exceeds its locked margin (
|marketPnl| > imLocked), the excess becomes bad debt. The trader’s realized loss is capped at imLocked, and the remaining loss is absorbed by pool equity. A BadDebt event is emitted for tracking and monitoring.Pool Equity Floor
Pool Equity Floor
Pool equity is clamped to zero and cannot go negative. Even if cumulative bad debt exceeds pool equity, the pool’s total assets floor at zero rather than becoming negative. This prevents accounting underflows and maintains the ERC-4626 vault invariant.
Utilization Cap
Utilization Cap
The
maxUtilizationBps parameter (default: 8,000 bps = 80%) prevents the pool from becoming over-leveraged. LP withdrawals are restricted when utilization is high, ensuring the pool retains sufficient liquidity to cover open position obligations. This acts as a circuit breaker against liquidity crises.How Invariants Are Tested
The protocol’s invariant set is verified through a comprehensive Foundry test suite:550+ Tests
The full test suite includes unit tests, integration tests, and edge-case coverage across all contract modules.
95%+ Line Coverage
Production code (excluding mocks, scripts, and test helpers) has over 95% line coverage and 89%+ branch coverage.
Fuzz Testing
Foundry fuzz tests exercise invariants with randomized inputs, verifying that no combination of parameters can violate the core accounting identities.
Edge Cases
Dedicated tests cover corner cases like bad debt scenarios, zero notional, fee exceeding available balance, and simultaneous maturity with liquidation eligibility.
- Zero-sum: After every settlement and liquidation, the sum of trader PnL and pool PnL impact equals zero (excluding fees)
- Margin lock: Attempting to open a position with insufficient free collateral reverts with the appropriate error
- IM > MM: Setting
mmFactorBps >= imFactorBpsin Config reverts - Status consistency: Closed positions cannot be settled, liquidated, or modified
- Exposure tracking: Aggregate exposure counters match the sum of individual position notionals after arbitrary sequences of opens, closes, and increases