> ## Documentation Index
> Fetch the complete documentation index at: https://docs.convallax.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Settlement & Resolution

> How Convallax options are settled at expiry — TWAP resolution pricing, EIP-712 attestation, and on-chain payout claims.

When an option series reaches its expiry timestamp, a **resolution price** is computed off-chain and submitted on-chain via a signed attestation. Holders and writers then claim their respective payouts.

<Info>
  The **writer** is whoever posted collateral at fill time, recorded by `ConvallaxCore.mintFor` during the atomic fill — the **maker** for a long trade (`makerSelling = true`) and the **taker** for a short trade (`makerSelling = false`). The **holder** is whoever received the freshly minted option tokens.
</Info>

## Resolution Price

The resolution price **S** determines payoffs for all options in a series. It is expressed as `resolutionBps` — an integer from 0 to 100 representing the YES token price at expiry.

| `resolutionBps` | Meaning                        |
| --------------- | ------------------------------ |
| `0`             | NO wins — YES is worth \$0.00  |
| `100`           | YES wins — YES is worth \$1.00 |
| `59`            | YES price at expiry ≈ \$0.59   |

## Resolution Methods

The on-chain contract accepts any `resolutionBps` in `[0, 100]`. The backend determines the value with a **tiered waterfall** — it tries the most authoritative source first and falls back to a market-price signal only when needed. The first tier that produces a price wins, and the chosen method is recorded in the response `meta.method`.

<Steps>
  <Step title="1. Official resolution — meta.method: official">
    If the underlying Polymarket market has **resolved** (closed), read the YES token's settled price directly. The closed market's `outcomePrices` is mapped to the option's YES token via the market's `clobTokenIds`, so the result is independent of Yes/No ordering:

    * YES wins → `resolutionBps = 100`
    * NO wins → `resolutionBps = 0`
    * Rare UMA 50/50 draw → `resolutionBps = 50`

    A market is only flagged `closed` once its UMA proposal has cleared the dispute window, so this is authoritative ground truth. Used whenever the market resolves at or before expiry.
  </Step>

  <Step title="2. Price TWAP — meta.method: twap">
    For options that expire **before** the market resolves, use the **time-weighted average price** of the YES token over the 30 minutes before expiry:

    1. Fetch the YES token's price history from the Polymarket CLOB `prices-history` endpoint over `[expiry − 1800s, expiry]` at 1-minute fidelity
    2. Drop the trailing current-time datapoint the endpoint appends, keeping only samples inside the window
    3. Compute the time-weighted average, holding each sample until the next: `TWAP = Σ pᵢ·(tᵢ₊₁ − tᵢ) / (t_last − t_first)`
    4. Map to basis points: `resolutionBps = clamp(round(TWAP × 100), 0, 100)`

    The `prices-history` series is the order-book **midpoint** when the bid/ask spread is tight (≤ \$0.10) and automatically falls back to the **last-traded price** when the spread is wider, so a single source covers both regimes. Time-weighting (rather than volume-weighting) resists manipulation from large or wash trades, since moving the average requires holding the book displaced for the whole window.
  </Step>
</Steps>

<Note>
  **Manual review for thin windows.** If the 30-minute TWAP window contains **no price points** (an illiquid or inactive market with no continuous data), the backend does **not** auto-resolve from a single stale trade — doing so would be a cheap manipulation vector. Instead it refuses to sign and the series is escalated to **manual review**, then settled via the owner-only break-glass `settle()`.

  Manual review generally follows the same logic the automated tier would have used, but with human approval: locate the most recent YES trade at or before expiry, then compute a TWAP over the 30-minute window ending at that trade's timestamp (again using `prices-history` midpoints where available, falling back to traded prices). A reviewer confirms the window is representative and not the product of a manipulative print before settling. This approach was recommended by the protocol's auditor to eliminate the single-trade manipulation surface.
</Note>

## Settlement Attestation (EIP-712)

The resolution signer creates a signed attestation that anyone can relay on-chain:

```solidity theme={null}
struct SettlementAttestation {
    uint256 seriesId;      // The option series being settled
    uint16  resolutionBps; // Resolution price (0–100)
    uint256 validUntil;    // Attestation expiry (Unix timestamp)
}
```

**EIP-712 domain:**

| Field               | Value                                        |
| ------------------- | -------------------------------------------- |
| `name`              | `ConvallaxCore`                              |
| `version`           | `1`                                          |
| `chainId`           | `80002`                                      |
| `verifyingContract` | `0x76c41a03e0993F0261bB3B1949320cA693f76faE` |

The attestation is valid for **15 minutes** after creation.

## Settlement Flow

```mermaid theme={null}
sequenceDiagram
  participant Cron as Resolution service
  participant API as Backend API
  participant Signer as Resolution signer
  participant Relayer as Any holder or keeper
  participant Core as ConvallaxCore

  Note over Core: block.timestamp ≥ expiry

  Cron->>API: POST /settlement/prepare { seriesId }
  API->>API: Resolve price (official → TWAP, else manual review)
  API->>Signer: signTypedData(SettlementAttestation)
  Signer-->>API: signature
  API-->>Relayer: { seriesId, resolutionBps, validUntil, signature }

  Relayer->>Core: settleWithAttestation(seriesId, resolutionBps, validUntil, signature)
  Core->>Core: Verify EIP-712 signature, compute payoffs
  Note over Core: Series marked as settled
```

### Two settlement paths

| Path                      | Who can call                        | When to use                                                |
| ------------------------- | ----------------------------------- | ---------------------------------------------------------- |
| `settleWithAttestation()` | Any option holder or contract owner | Normal path — permissionless relay with signed attestation |
| `settle()`                | Contract owner only                 | Emergency break-glass — no signature required              |

<Info>
  **Settlement is automatic.** A backend keeper continuously watches for expired-but-unsettled series, resolves the price via the waterfall above, and submits `settleWithAttestation()` on-chain itself — so holders never need to trigger settlement. The permissionless path remains available as a backstop: any holder can settle their own series by relaying an attestation from `POST /settlement/prepare`.
</Info>

## Payoff Formulas

Once a series is settled with resolution price S (`resolutionBps`), the `payoffPerWhole` (USDC raw units per 1 whole option) is computed by `OptionMath.payoffPerWhole`:

| Type     | `payoffPerWhole` (USDC raw)                  | Equivalent per option   |
| -------- | -------------------------------------------- | ----------------------- |
| **Call** | `max(0, resolutionBps − strikeBps) × 10,000` | `max(0, S − K) × $0.01` |
| **Put**  | `max(0, strikeBps − resolutionBps) × 10,000` | `max(0, K − S) × $0.01` |

<Accordion title="Worked example: call at K=50, resolution S=80">
  * `payoffPerOption = (80 − 50) × $0.01 = $0.30`
  * Holder with 100 options claims: 100 × \$0.30 = **30 USDC**
  * Writer collateral was: 100 × \$0.50 = 50 USDC
  * Writer reclaims: 50 − 30 = **20 USDC**
</Accordion>

<Accordion title="Worked example: put at K=60, binary resolution S=0 (NO wins)">
  * `payoffPerOption = (60 − 0) × $0.01 = $0.60` (maximum put payoff)
  * Holder with 50 options claims: 50 × \$0.60 = **30 USDC**
  * Writer posted 50 × \$0.60 = 30 USDC collateral → writer reclaims **0 USDC**
</Accordion>

<Accordion title="Worked example: TWAP continuous settlement">
  * 30-minute TWAP = 0.5923 → `resolutionBps = 59`
  * Call at K=50: payoff = (59 − 50) × $0.01 = **$0.09 per option\*\*
  * Put at K=65: payoff = (65 − 59) × $0.01 = **$0.06 per option\*\*
</Accordion>

## Claiming Payouts

After settlement, participants claim their payouts:

### Holders — `claimHolderPayout(seriesId, amount)`

Burns the specified amount of option tokens and transfers the USDC payout:

```
payout = amount × payoffPerOption / 1,000,000
```

If the option expired worthless (`payoffPerOption = 0`), the tokens are burned but no USDC is transferred.

### Writers — `claimWriterCollateral(seriesId)`

The writer is the party that posted collateral at fill (the maker on long trades, the taker on short trades). This returns the writer's remaining collateral after holder payouts are deducted:

```
writerPayout = writerCollateral × writerPoolRemaining / totalCollateral
```

This is a **one-shot** operation — the writer's recorded collateral is zeroed after claiming.

## Trust Model

| Component         | Trust assumption                                                        |
| ----------------- | ----------------------------------------------------------------------- |
| Resolution signer | Trusted to sign accurate TWAP / binary attestations                     |
| On-chain contract | Verifies EIP-712 signature but does **not** verify the TWAP computation |
| Contract owner    | Can break-glass settle and rotate the resolution signer                 |
| Holders           | Can always claim after settlement — never blocked by pause              |

<Warning>
  The resolution signer is an operational trust assumption. The on-chain contract verifies the signature's authenticity but cannot independently verify the TWAP computation. This is analogous to oracle trust in DeFi protocols.
</Warning>
