> ## 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.

# Trade Lifecycle

> Complete Convallax trade lifecycle — from live quote request through real-time market maker competition over the three-channel transport, commitment, and atomic on-chain settlement via fill().

All trading on Convallax flows through a **live quote request** system. Traders open a quote request, independent market makers compete in real time, the trader commits to the best quote, the winning maker signs its own settlement order, and the taker fills it atomically on-chain.

Maker transport is split across **three channels**: quote requests are pushed to makers over **SSE**, quotes and confirmation signatures are submitted over **REST**, and post-trade win/loss notifications are pushed over a **WebSocket**. The state machine is unchanged — only the transport that drives each transition differs.

## State Machine

Every trade moves through six states:

```
open → quoted → accepted → confirmed → executed → settled
```

| State         | What happens                                                                                                       | Transport         |
| ------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------- |
| **open**      | A taker `POST`s `/quote-requests`; it is broadcast to makers as an SSE `quote_request`                             | REST in → SSE out |
| **quoted**    | Makers `POST /v1/mm/quotes`; the user sees live best-quote pricing over SSE                                        | REST in → SSE out |
| **accepted**  | Taker `POST`s `/quote-requests/:id/commit`; backend emits WS `quote:accepted` to the winning maker                 | REST in → WS out  |
| **confirmed** | The winning maker signs the `Order` and `POST`s the signature to `/v1/mm/quotes/:quoteId/confirm`                  | REST in → WS out  |
| **executed**  | The taker calls `fill()` on-chain — collateral is locked, premium is paid, and option tokens are minted atomically | on-chain          |
| **settled**   | At expiry, settlement happens via the resolution attestation (unchanged)                                           | on-chain          |

```
open ── quoted ── accepted ── confirmed ── executed ── settled
                     │
                     └── maker misses deadline → next-best maker tried
                                              └── none confirm → back to open (retry)
```

## Transport at a Glance

| Channel                    | Transport | Direction      | Used by states      |
| -------------------------- | --------- | -------------- | ------------------- |
| MM quote-request stream    | SSE       | server → maker | open, quoted        |
| Quote submission + confirm | REST      | maker → server | quoted, confirmed   |
| Post-trade notifications   | WebSocket | server → maker | accepted, confirmed |
| User live pricing          | SSE       | server → user  | quoted              |

## Step-by-Step Flow

<Steps>
  <Step title="Open a quote request">
    The frontend creates a live quote request by calling the backend. No wallet signature is required — the quote request is unsigned.

    ```bash theme={null}
    POST /quote-requests
    {
      "market": {
        "conditionId": "0xa4ddc188...",
        "yesTokenId": "51508280778...",
        "question": "Will there be a Hantavirus outbreak in 2026?"
      },
      "option": {
        "strikeBps": 50,
        "expiryMs": 1735689600000,
        "optionType": "call"
      },
      "trade": {
        "side": "buy",
        "budgetUsd": 100
      }
    }
    ```

    <Note>
      On a **buy** the taker submits a USDC budget (`trade.budgetUsd`); makers quote a per-option `price` and the taker receives `floor(budgetUsd / price)` whole options. On a **sell** the taker submits a contract count (`trade.size`).
    </Note>

    The backend broadcasts the trade parameters to all subscribed market makers as an SSE `quote_request` event and returns a `requestId` and `expiresAt` timestamp. Quote requests are long-lived (5 minutes by default, configurable via `QUOTE_REQUEST_TTL_MS`).
  </Step>

  <Step title="Market makers submit live quotes (quoted)">
    Makers subscribed to the [quote-request stream](/guides/quote-stream) receive the `quote_request` event and submit quotes over REST:

    ```bash theme={null}
    POST /v1/mm/quotes
    X-API-Key: <maker-key>
    {
      "requestId": "req-789",
      "quote": {
        "maker": "0xMakerWallet...",
        "side": "buy",
        "price": 0.12,
        "size": 1000
      }
    }
    ```

    The endpoint returns a server-generated `quoteId`. Makers can **update their quotes** at any time by re-POSTing for the same `requestId` — only the latest quote per maker is kept. The `side` echoes the taker's requested side; the `price` is the ask (for taker buys) or bid (for taker sells).
  </Step>

  <Step title="Frontend streams live pricing">
    While the quote request is open, the frontend subscribes to a user-facing SSE stream to display the live best quote as it changes:

    ```bash theme={null}
    GET /quote-requests/:id/stream
    ```

    The stream emits `best_quote` events (with the current best quote, maker, and quote count) and a `closed` event when the request ends. This replaces the old \~1s polling of `GET /quote-requests/:id/quotes`, which still exists as a [fallback](/api-reference/trading/poll-quotes).
  </Step>

  <Step title="Trader commits (accepted)">
    When the trader is ready, they commit by providing their wallet address:

    ```bash theme={null}
    POST /quote-requests/:id/commit
    {
      "wallet": "0xTrader..."
    }
    ```

    The backend selects the best valid quote, resolves the `seriesId` from the option parameters, builds the exact EIP-712 `Order` struct (with `maker` set to the winning quote's wallet and `taker` set to the committing wallet), and emits a **WebSocket** `quote:accepted` message to the **winning maker**, starting a confirmation deadline. The backend does **not** post collateral, approve, or sign on the maker's behalf — makers are independent and manage their own USDC approvals.
  </Step>

  <Step title="Maker confirms (confirmed)">
    The winning maker signs the `Order` with **its own wallet key** and POSTs the signature to the confirm endpoint before the `confirmationDeadline` (`MAKER_CONFIRMATION_MS`, default 10s):

    ```bash theme={null}
    POST /v1/mm/quotes/:quoteId/confirm
    X-API-Key: <maker-key>
    { "signature": "0x..." }
    ```

    The backend verifies the signature recovers to `order.maker`, returns the signed order to the trader, and emits WS `quote:confirmed` to the winner and `quote:rejected` to the other makers.

    **Fallback:** if the winning maker misses the deadline, the backend falls back to the **next-best** maker's quote and emits `quote:accepted` to them. If no maker confirms, the request stays **open** and the taker can retry — the commit returns HTTP `503`.
  </Step>

  <Step title="Taker calls fill() on-chain (executed)">
    The taker submits the maker's signed order to `ConvallaxRFQSettlement.fill()`, which calls `ConvallaxCore.mintFor(...)` to execute everything in one transaction — **atomic mint-on-fill**. Option tokens are **minted fresh** to the holder (never transferred from inventory), and the writer's USDC collateral is locked at the same time:

    * **Taker buying (long, `makerSelling = true`):** the **maker** posts collateral to the Core vault (maker becomes the writer), option tokens are minted to the **taker** (the holder), and premium flows **taker → maker**.
    * **Taker selling (short, `makerSelling = false`):** the **taker** posts collateral to the Core vault (taker becomes the writer), option tokens are minted to the **maker** (the holder), and premium flows **maker → taker**.

    The fill is **atomic** — collateral, premium, and minting all execute in a single transaction or the entire trade reverts. Collateral is locked only when a trade actually executes; there is no pre-minting or inventory. The nonce is consumed to prevent replay.
  </Step>

  <Step title="Settlement at expiry (settled)">
    At expiry, a resolution price is computed off-chain and submitted on-chain via a signed attestation. Holders and writers then claim their respective payouts. This flow is unchanged — see [Settlement](/guides/settlement).
  </Step>
</Steps>

## Deadlines

* **Maker misses deadline within `expires_at`:** the request stays open (the missed maker's quote is withdrawn) and the backend falls back to the next-best maker.
* **Past `expires_at`:** the request is expired and a `quote_request_expired` SSE event is emitted to makers.

## Winner Selection

The backend selects the best valid quote at commit time:

| Taker action        | Winner             |
| ------------------- | ------------------ |
| **Buying** options  | Lowest price wins  |
| **Selling** options | Highest price wins |

**Validation rules:**

* `side` must match the taker's requested side (echoes `trade.side`)
* `price` is per one option, in `(0, 1)`, and within no-arbitrage bounds
* On a **buy**, `size` is the max whole options you'll fill (≥ 1); the actual fill is `min(floor(budgetUsd / price), size)`
* On a **sell**, `size` must be ≥ 50% of the requested `trade.size`

## On-Chain Order (EIP-712)

The `Order` struct signed by the market maker for on-chain settlement:

```solidity theme={null}
struct Order {
    address maker;       // Market maker wallet
    uint256 seriesId;    // Option series (= ERC-1155 token ID)
    uint256 optionAmount;// Option tokens (6 decimals)
    uint256 premiumAmount;// USDC total premium (6 decimals)
    bool    makerSelling;// true = MM sells options to taker
    address taker;       // Restricted to this taker address
    uint256 validUntil;  // Quote expiry (Unix timestamp)
    uint256 nonce;       // Replay protection
}
```

**EIP-712 domain:**

| Field               | Value                                        |
| ------------------- | -------------------------------------------- |
| `name`              | `ConvallaxRFQSettlement`                     |
| `version`           | `1`                                          |
| `chainId`           | `80002` (Polygon Amoy)                       |
| `verifyingContract` | `0x3E583BC44157c91F8c534372A3e91c371841107A` |

## Two Signature Domains

Convallax uses two distinct EIP-712 domains:

| Domain                   | Purpose                | Who signs         |
| ------------------------ | ---------------------- | ----------------- |
| `ConvallaxRFQSettlement` | On-chain fill order    | Market maker      |
| `ConvallaxCore`          | Settlement attestation | Resolution signer |

<Info>
  Unlike the previous architecture, traders no longer sign an EIP-712 message to initiate a trade. The trader's authorization is the on-chain `fill()` call itself — only the wallet that is designated as the `taker` in the signed order can execute the fill.
</Info>

## Premium Calculation

```
premiumAmount = price × size × 1,000,000   (USDC raw, 6 decimals)
optionAmount  = size × 1,000,000           (option raw, 6 decimals)
```

For example, buying 10 options at price \$0.12:

* `premiumAmount` = 0.12 × 10 × 1,000,000 = **1,200,000** raw (1.20 USDC)
* `optionAmount` = 10 × 1,000,000 = **10,000,000** raw
