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

# Market Resolution: Oracle and Manual Resolution Paths

> How counsel resolves Family A oracle markets and Family B manual markets, what triggers a void, and how the multisig trust model protects pool funds.

Resolution is the process by which a market's winning outcome is determined and funds are distributed to the correct recipients. counsel has two resolution paths, automatic oracle evaluation for crypto-price markets and an operator-proposed optimistic process for real-world events, but both end the same way: a committee multisig releases funds from the pool account on-chain.

## Family A: Oracle resolution

Family A markets resolve automatically at `resolutionTime` by reading an XLS-47 price oracle entry on the XRPL. No human input is required for a clean resolution.

### What happens at resolution time

<Steps>
  <Step title="Read oracle">
    The settlement engine queries the XLS-47 oracle at `oracleAccount` / `oracleDocumentId` for the `baseAsset`/`quoteAsset` pair.
  </Step>

  <Step title="Check freshness">
    The oracle value's `lastUpdateTime` must satisfy:

    ```
    -120s ≤ (resolutionTime − oracle.lastUpdateTime) ≤ freshnessSeconds
    ```

    The 120-second post-close grace period absorbs publish cadence, a value published just after `resolutionTime` is still accepted. Freshness is measured against `resolutionTime`, not the wall clock at the time the engine runs, so settling late cannot use a price that did not exist at close.
  </Step>

  <Step title="Evaluate condition">
    The engine compares the oracle's reported price against `strike` using `comparison`:

    * `>= strike`, outcome 0 (Yes) wins if `price >= strike`
    * `<= strike`, outcome 0 (Yes) wins if `price <= strike`

    If the condition is not met, outcome 1 (No) wins.
  </Step>

  <Step title="Publish resolution record">
    The operator writes a signed `ResolutionRecord` memo to the pool account with `status: "final"`, the `winningOutcome` index, the raw `oracleValue`, and the `oracleLastUpdateMs` timestamp. This record is the authoritative on-chain proof of resolution.
  </Step>

  <Step title="Distribute payouts">
    The committee multisig sends a Payment from the pool account to each winning bettor, tagged with a `payout` memo. Settlement is idempotent: the engine reads already-paid accounts from the ledger before each run and skips them.
  </Step>
</Steps>

### Freshness void

If the oracle value's age relative to `resolutionTime` falls outside the freshness window (accounting for the 120-second grace period), the market voids immediately. All stakes are refunded. See [Void policy](#void-policy) below.

## Family B: Manual (optimistic) resolution

Family B markets use an optimistic resolution model adapted from UMA's Optimistic Oracle, rebuilt on native XRPL primitives with no smart contract and no token vote. The operator proposes an outcome and posts a bond, a dispute window opens, and the multisig committee finalizes. counsel says plainly where the trust lives: this is custodial-transparent, a disclosed council, not a trustless oracle. The full design is in [optimistic-oracle.md](https://github.com/gamween/counsel/blob/main/docs/optimistic-oracle.md).

### What happens at resolution time

<Steps>
  <Step title="Operator proposes and bonds">
    After `resolutionTime`, the proposer posts a signed on-chain `ResolutionRecord` with `status: "proposed"` to the pool account, including `winningOutcome`, an evidence source URL, and an evidence hash. The proposer posts the symmetric bond whose size is pre-committed in the market definition (`bondXrp`), so the bond is fixed and non-negotiable.
  </Step>

  <Step title="Dispute window opens">
    The window lasts `disputeWindowHours` (default 48). Any account can open a dispute by posting a matching bond to the pool, anchoring its own evidence (a source URL and a content hash) on-chain, so the dispute references the same canonical record as the proposal. An undisputed proposal is correct by default.
  </Step>

  <Step title="No challenge: committee signs">
    If the window closes with no dispute, the multisig committee cosigns the proposed outcome. Settlement proceeds identically to Family A: a final `ResolutionRecord` is written on-chain, then payouts are distributed, and the proposer reclaims their bond.
  </Step>

  <Step title="Challenged: committee finalizes and resolves bonds">
    If one or more bonds are posted, the committee reviews the evidence independently and signs the final outcome, which may or may not agree with the proposal. The final `status: "final"` record supersedes the proposal. The committee then resolves the bonds (see below).
  </Step>
</Steps>

<Note>
  A `proposed` record is visible in the pool account's transaction history but does not trigger payout distribution. Only a `status: "final"` record initiates settlement. A void (the "too early" or ambiguous outcome) is a first-class result that refunds every stake with no fee, the analogue of UMA's 50-50 answer, so an unanswerable market never mis-resolves.
</Note>

### Bonds and the final fee

Bonds are symmetric and pre-committed: the proposer posts `bondXrp` and a disputer must match it exactly, so neither side can take a cheaper position. A wrong side forfeits its bond to the right side, which is what turns a wrong proposal into free money for the first honest disputer and is the entire economic security of the model.

counsel also implements UMA's final fee. A configurable slice of the slashed bond, `finalFeeBps` (basis points), is skimmed to the council treasury before the winner is paid; the winner's own bond is always returned in full. The fee funds the arbiter and makes a frivolous bond cost real money even when you win. The bond math is integer-only and rounds down, so the fee plus the awards can never exceed the slashed bond, and the whole path is test-driven.

| Outcome                   | Proposer                               | Disputer                                       | Council   |
| ------------------------- | -------------------------------------- | ---------------------------------------------- | --------- |
| Undisputed                | bond returned                          | —                                              | —         |
| Upheld (dispute fails)    | bond returned + disputer bond less fee | loses bond                                     | final fee |
| Overturned (dispute wins) | loses bond                             | bond returned + proposer bond less fee (split) | final fee |

## Void policy

The following conditions result in a full refund with no fee deducted:

| Condition                     | Trigger                                                                                                                   |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Oracle value not fresh        | No oracle value exists within `freshnessSeconds` + 120s grace of `resolutionTime` (Family A only)                         |
| One-sided pool                | All public stakes are on a single outcome; there is no opposing pool to distribute among winners                          |
| Event cancelled or unmappable | The underlying event is cancelled, indefinitely postponed, or produces a result that does not map to the defined outcomes |
| No public stakes              | The gross pool consists entirely of market-maker seed stakes; no real bettors participated                                |

When any condition is met, the settlement engine distributes refunds instead of payouts. Each bettor receives their exact original stake back via a native XRP Payment. Market-maker seeds are excluded from refunds (they are house-neutral) and remain as pool residue.

## What happens on void

A void is not an error state, it is a defined contract outcome. On void:

1. The `ResolutionRecord` is written on-chain with `void: true` and `winningOutcome: null`.
2. Every non-seed stake is refunded at face value (drops in = drops out).
3. No fee is deducted from any refund.
4. The pool account's remaining balance (seeds + dust) stays under operator control.

Refunds are dispatched in the same idempotent per-recipient Payment loop used for payouts, so a partial failure mid-run is safe to retry.

## Trust model

Pool accounts are secured so that no single party, including the operator, can unilaterally move funds:

<CardGroup cols={2}>
  <Card title="Master key disabled" icon="key">
    After the pool account is created, the master key is disabled on-chain. There is no signing key that can authorize a unilateral Payment.
  </Card>

  <Card title="n-of-m SignerList" icon="users">
    A multisig `SignerList` is set on the pool account. A threshold of committee members must cosign any outgoing transaction before the XRPL will process it.
  </Card>
</CardGroup>

Every bet, proposal, dispute, and payout is a native on-chain transaction. You can verify the complete history of any pool account independently using any XRPL full history node or block explorer, no API access to counsel is required to audit a market's lifecycle.
