# ForceCloseSteps

The ForceCloseSteps Facet provides a step-based force close flow that supports both normal (isolated) and cross-mode PartyBs. It was introduced because the legacy single-transaction `forceClosePosition` on the `ForceActionsFacet` cannot handle cross-mode PartyBs, where settlement requires a `UnifiedSettlementSig` covering multiple PartyAs and the settlement + close must use consistent price snapshots.&#x20;

***

### Overview

The ForceCloseSteps Facet provides two ways to execute a force close:

* **3-Step Flow:** `initializeForceClose` → `settleUpnlForForceClose` (optional) → `finalizeForceClose`. Each step is a separate transaction, allowing large settlements to be broken across multiple calls.
* **Convenience Function:** `forceCloseAndSettlePositionsUnified` combines all three steps into a single transaction when the settlement data is small enough to fit within gas limits.

#### Behavioral Differences by PartyB Mode

| Behavior            | Normal (Isolated) PartyB                    | Cross-Mode PartyB                                   |
| ------------------- | ------------------------------------------- | --------------------------------------------------- |
| Insolvency handling | Reserve vault fallback → PartyB liquidation | Close with uPnL ignored → `CLOSED_INSOLVENT` status |
| Settlement scope    | Same PartyB, same PartyA only               | Same PartyB, any PartyA (shared pool)               |
| On insolvency event | `LiquidatePartyB`                           | `ForceClosePosition` + `ForceClosePartyBInsolvent`  |
| Reserve vault used  | Yes                                         | No                                                  |

Cross-mode PartyBs are **never liquidated** during force close. If PartyB is insolvent, the position either closes (marked `CLOSED_INSOLVENT`) or the transaction reverts with insufficient balance.&#x20;

***

### initializeForceClose()

**Step 1.** Validates force-close conditions and computes the close price. The quote must be in `CLOSE_PENDING` status with a LIMIT order type, the force-close cooldown must have elapsed, and the close price must have been reached within the signature's high/low price range. Checks that PartyA remains solvent after the close.

Stores a `ForceCloseDetail` snapshot containing the computed close price, PartyB's uPnL, and the current market price. This snapshot is used by subsequent steps and does not change after initialization.

**Function Signature:**

```solidity
function initializeForceClose(uint256 quoteId, HighLowPriceSig memory sig) external notLiquidated(quoteId) whenNotPartyAActionsPaused;
```

**Parameters:**

* `quoteId`: The ID of the quote to force-close.
* `sig`: The Muon `HighLowPriceSig` containing high/low prices used to calculate the close price with penalty.

**Example:**

```solidity
symmioDiamond.initializeForceClose(quoteId, highLowPriceSig);
```

**Events Emitted:**

* `ForceCloseInitialized(address caller, address partyB, uint256 quoteId, bytes reqId, uint256 closePrice, uint256 timestamp)`

***

### settleUpnlForForceClose()

**Step 2.** Settles unrealized PnL using `UnifiedSettlementSig` to fund the close when either party lacks sufficient available balance. This step can be called multiple times if the settlement needs to be spread across several transactions.

Which positions can be settled depends on the scenario:

* **PartyA lacks funds:** Settle PartyA's profitable positions with any other PartyB (not the force-close quote's PartyB). This increases PartyA's allocated balance.
* **PartyB (non-cross) lacks funds:** Settle the same PartyB's profitable positions, but only with the force-close quote's PartyA. In isolated mode, each per-PartyA bucket is separate, so the contract enforces that only one PartyA is included in the settlement.
* **PartyB (cross) lacks funds:** Settle the same PartyB's positions with any PartyA. Everything goes to the shared `address(0)` pool.

After settlement, the stored `upnlPartyB` snapshot is adjusted by the settlement delta (only when the settlement targets the force-close quote's PartyB), ensuring consistency for the finalize step.

**Function Signature:**

```solidity
function settleUpnlForForceClose(
    uint256 quoteId,
    UnifiedSettlementSig memory settlementSig,
    uint256[] memory updatedPrices
) external notLiquidated(quoteId) whenNotPartyAActionsPaused;
```

**Parameters:**

* `quoteId`: The ID of the quote in the force-close workflow.
* `settlementSig`: The `UnifiedSettlementSig` containing uPnL data and pricing for settlement.
* `updatedPrices`: New prices to set as `openedPrice` for the settled quotes.

**Events Emitted:**

* `SettleUpnlUnified(bytes reqId, QuotesSettlementsData[] data, uint256[] updatedPrices, address partyB, address[] partyAs, uint256[] newPartyAsAllocatedBalances, uint256 newPartyBAllocatedBalance)`

***

### finalizeForceClose()

**Step 3.** Refreshes the uPnL and current price snapshot with a fresh Muon signature (ensuring PartyA solvency at the latest prices), then closes the position.

The close behavior differs by PartyB mode:

* **Normal PartyB:** If PartyB is solvent after close, emits `ForceClosePosition`. If insolvent, attempts the reserve vault fallback. If the reserve vault covers the deficit, the position closes. If not, triggers PartyB liquidation and emits `LiquidatePartyB`.
* **Cross-Mode PartyB:** Always emits `ForceClosePosition`. If PartyB is insolvent, the position still closes (marked `CLOSED_INSOLVENT`) and additionally emits `ForceClosePartyBInsolvent`. If even the "ignore uPnL" fallback is insufficient, the transaction reverts.

**Function Signature:**

```solidity
function finalizeForceClose(uint256 quoteId, PairUpnlAndPriceSig memory sig) external notLiquidated(quoteId) whenNotPartyAActionsPaused;
```

**Parameters:**

* `quoteId`: The ID of the quote to finalize the force close for.
* `sig`: A fresh Muon `PairUpnlAndPriceSig` to refresh the uPnL and current price snapshot.

**Events Emitted (normal PartyB, solvent):**

* `ForceClosePosition(uint256 quoteId, address partyA, address partyB, uint256 filledAmount, uint256 closePrice, QuoteStatus status, uint256 closeId)`

**Events Emitted (normal PartyB, insolvent):**

* `LiquidatePartyB(address liquidator, address partyB, address partyA, uint256 partyBAllocatedBalance, int256 upnlPartyB)`

**Events Emitted (cross PartyB, solvent):**

* `ForceClosePosition(...)`

**Events Emitted (cross PartyB, insolvent):**

* `ForceClosePosition(...)`
* `ForceClosePartyBInsolvent(uint256 quoteId, address partyA, address partyB, uint256 closePrice, uint256 currentPrice, int256 upnlPartyB, int256 partyBAvailableAfterClose)`

***

### forceCloseAndSettlePositionsUnified()

Convenience function that combines all three steps — initialize, settle (optional), and finalize — into a single transaction. Unlike the step-by-step flow, the finalize here does **not** take a fresh `PairUpnlAndPriceSig`. It uses the uPnL and current price values from the init signature directly (adjusted by any settlement delta).

Settlement is skipped if `updatedPrices` is empty.

**Function Signature:**

```solidity
function forceCloseAndSettlePositionsUnified(
    uint256 quoteId,
    HighLowPriceSig memory sig,
    UnifiedSettlementSig memory settlementSig,
    uint256[] memory updatedPrices
) external notLiquidated(quoteId) whenNotPartyAActionsPaused;
```

**Parameters:**

* `quoteId`: The ID of the quote to force-close.
* `sig`: The Muon `HighLowPriceSig` for calculating the close price.
* `settlementSig`: The `UnifiedSettlementSig` for settlement. Ignored if `updatedPrices` is empty.
* `updatedPrices`: New prices for settled quotes. Pass an empty array to skip settlement.

**Events Emitted:**

* `ForceCloseInitialized(...)` — from the init step
* `SettleUpnlUnified(...)` — from the settle step (only if `updatedPrices` is non-empty)
* `ForceClosePosition(...)` and/or `ForceClosePartyBInsolvent(...)` or `LiquidatePartyB(...)` — from the finalize step
