Skip to main content
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

1

Read oracle

The settlement engine queries the XLS-47 oracle at oracleAccount / oracleDocumentId for the baseAsset/quoteAsset pair.
2

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

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

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

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.

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

Family B: Manual (optimistic) resolution

Family B markets use an optimistic resolution model: the operator proposes an outcome, a dispute window opens, and the multisig committee finalizes if no valid challenge arrives.

What happens at resolution time

1

Operator publishes proposal

After resolutionTime, the operator posts a signed on-chain ResolutionRecord with status: "proposed" to the pool account. The proposal includes winningOutcome, the evidence source URL, and an evidence hash. The bondDrops field sets the symmetric bond size any disputer must match.
2

Dispute window opens

The dispute window lasts disputeWindowHours (default: 48 hours). Any account that holds a stake in the market can post a challenge bond to the pool account during this period. A dispute memo must reference the marketId and include the disputer’s reason.
3

No challenge: committee signs

If the dispute window closes without a valid bond being posted, the multisig committee cosigns the proposed outcome. Settlement proceeds identically to Family A: a final ResolutionRecord is written on-chain, then payouts are distributed.
4

Challenged: committee reviews

If one or more valid bonds are posted, the committee reviews the evidence and proposal independently. The committee signs the final outcome, which may or may not agree with the operator’s proposal. The final ResolutionRecord with status: "final" supersedes the proposal regardless of which side the committee supports.
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.

Void policy

The following conditions result in a full refund with no fee deducted:
ConditionTrigger
Oracle value not freshNo oracle value exists within freshnessSeconds + 120s grace of resolutionTime (Family A only)
One-sided poolAll public stakes are on a single outcome; there is no opposing pool to distribute among winners
Event cancelled or unmappableThe underlying event is cancelled, indefinitely postponed, or produces a result that does not map to the defined outcomes
No public stakesThe 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:

Master key disabled

After the pool account is created, the master key is disabled on-chain. There is no signing key that can authorize a unilateral Payment.

n-of-m SignerList

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