> For the complete documentation index, see [llms.txt](https://gitbook.hashpower.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gitbook.hashpower.io/05.delivery-settlement.md).

# Settlement

This document explains how futures positions are cash-settled at maturity in HPDX Hashprice Futures.

> **Cash settlement model.** As of contract version `2.15.0`, HPDX Hashprice Futures are purely **cash-settled**. There is no physical hashrate delivery, no escrow, no validator role, no stratum destination URL, and no breach penalties. At maturity, a single **settlement price** is pinned for each expiration, every position on that expiration is marked to that one price, and PnL is routed through the insurance fund.

***

## Settlement Lifecycle

```
┌──────────────────────────────────────────────────────────────────────┐
│                      POSITION SETTLEMENT LIFECYCLE                    │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ORDER                MATCH                POSITION       SETTLEMENT  │
│  ─────                ─────                ────────       ──────────  │
│                                                                      │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐  ┌───────────┐  │
│  │ Order       │   │ Orders      │   │ Position    │  │ Mark-to-  │  │
│  │ Placed      │   │ Matched     │   │ Held        │  │ Market    │  │
│  │             │   │             │   │             │  │ Settled   │  │
│  │ • Buy/Sell  │──►│ • FIFO grid │──►│ • Margin    │─►│ • At      │  │
│  │ • Price/day │   │ • Opposite  │   │ • Offset    │  │   maturity │  │
│  │ • Maturity  │   │   matched   │   │ • Liquidate │  │ • Anyone  │  │
│  └─────────────┘   └─────────────┘   └─────────────┘  └───────────┘  │
│                                                                      │
│  │◄──────────────────────────────────────►│◄────────────────────►│  │
│        Anytime before maturity (deliveryAt)   At/after maturity      │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
```

***

## Before Maturity

### Position Trading

Before a position matures, it can be:

* **Exit**: Close by placing an opposite order and capture the PnL
* **Liquidated**: Permissionlessly closed if undercollateralized

No payment, escrow, or destination endpoint is required at any point. A position simply waits until its maturity timestamp (`deliveryAt`) and is then cash-settled.

***

## Settlement at Maturity

Once `block.timestamp >= deliveryAt`, a matured position can be settled at the expiration's **pinned settlement price** (see [Settlement Price Pinning](#settlement-price-pinning)).

### Who Can Settle

Settlement is **permissionless**. Anyone — typically an off-chain keeper, but any address — can call:

```solidity
function settlePosition(bytes32 positionId) public;
```

To settle several matured positions in one transaction:

```solidity
function settlePositions(bytes32[] calldata positionIds) external;
```

`settlePositions` reverts atomically if any id is unknown or not yet matured, so keepers should pre-filter to positions where `block.timestamp >= deliveryAt`.

### Settlement Price Pinning

All positions sharing the same `deliveryAt` settle against **one** price, recorded the first time anyone settles (or explicitly pins) that expiration at/after maturity. This removes two problems of marking each position to a live `getMarketPrice()` at settlement time:

* **Price drift / unfairness** — without pinning, the first position settled could use a different mark than the last, so otherwise-identical positions on the same expiration would realize different PnL purely based on settlement ordering.
* **Settlement-time gaming** — a single fixed reference price removes any incentive to time the exact block at which a position is settled.

The price is pinned **lazily**: the first call to `settlePosition` / `settlePositions` for an expiration reads `getMarketPrice()`, stores it, and emits `SettlementPriceRecorded`. Every subsequent settlement for that expiration reuses the stored price.

Anyone can also pin the price explicitly without settling a position:

```solidity
function recordSettlementPrice(uint256 deliveryAt) external;
```

`recordSettlementPrice` reverts with `SettlementDateNotReached` before maturity and is a no-op (idempotent) once the price is already set. The pinned price is readable on-chain via the `settlementPrice(uint256 deliveryAt)` mapping (returns `0` until recorded).

```solidity
event SettlementPriceRecorded(uint256 indexed deliveryAt, uint256 price, address recordedBy);
```

### Collateral Is Held Until Settlement

A matured-but-unsettled position still carries margin: its initial-margin requirement is held until it is actually settled. This prevents a losing party from withdrawing collateral after maturity but before settlement, which would otherwise create bad debt for the insurance fund. Once the settlement price is pinned, the position no longer carries market risk (its PnL is frozen at the pinned price), and on settlement the held collateral funds the realized PnL.

### How PnL Is Computed

The **entire** position notional is marked to the expiration's pinned `settlementPrice`. The notional spans `deliveryDurationDays` (the price-per-day × days multiplier):

```
mult = deliveryDurationDays

sellerPnl = (sellPricePerDay − settlementPrice) × mult
buyerPnl  = (settlementPrice − buyPricePerDay)  × mult
```

PnL is routed through the **insurance fund**: the fund pays the winning side and is credited by the losing side. After settlement the position is removed from the book.

**Example:**

```
Sell Entry:        4.10 USDC/day
Buy Entry:         4.10 USDC/day
Settlement Price:  4.30 USDC/day (pinned for this expiration at maturity)
Duration:          7 days

sellerPnl = (4.10 − 4.30) × 7 = −1.40 USDC  (short loses)
buyerPnl  = (4.30 − 4.10) × 7 = +1.40 USDC  (long gains)

→ insurance fund pays the buyer 1.40 USDC, debits the seller 1.40 USDC
```

### Settlement Event

Settlement removes the position and emits:

```solidity
LotClosed(lotId, seller, buyer, sellerPnl, buyerPnl, closedBy, LotCloseReason.SETTLED)
```

`reason = SETTLED` (the `LotCloseReason` enum ordinal `3`) is the canonical signal that a position has been cash-settled at maturity. `closedBy` is the address that called `settlePosition` (the keeper or whoever triggered settlement).

### There Is No Settlement Window

A matured position can be settled **any time** after `deliveryAt`. There is no expiry window, no breach deadline, and no penalty for late settlement — the position stays open (with collateral held) until someone settles it, and it always settles at the expiration's pinned price regardless of how late settlement happens.

***

## Complete Settlement Flow

```
┌─────────────────────────────────────────────────────────────────────┐
│                        CASH SETTLEMENT FLOW                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  T-7 days: Position Created                                         │
│  │         Buyer: 0xAlice, Seller: 0xBob                            │
│  │         Price: 4.10 USDC/day, Duration: 7 days                   │
│  │         Maturity (deliveryAt): T+0                               │
│  │                                                                   │
│  Pre-maturity: Either party may exit by offsetting, or be           │
│  │             liquidated if undercollateralized                    │
│  │                                                                   │
│  T+0:      Maturity reached (block.timestamp >= deliveryAt)         │
│  │         Position now settleable by anyone; collateral held       │
│  │                                                                   │
│  T+0 or later: Settlement                                           │
│  │         Keeper calls settlePosition(positionId)                  │
│  │         First call pins settlementPrice = getMarketPrice()       │
│  │           and emits SettlementPriceRecorded                      │
│  │         Full notional marked to the pinned settlementPrice       │
│  │         PnL routed via insurance fund                            │
│  │         LotClosed(..., reason=SETTLED) emitted                   │
│  │                                                                   │
│  ✓ Position removed                                                 │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
```
