Skip to main content
Binary and neg-risk markets follow the same propose → challenge → finalize lifecycle, on different oracle contracts. The binary timeline is below; Neg-risk markets lists what differs.

Binary markets — propose → challenge → finalize

Step 1 — Oracle source check

The market is configured with one or more data sources when it is listed. When the configured source(s) yield an unambiguous outcome, the market becomes ready to propose. When data is unavailable or ambiguous (no strict majority across configured sources), the market moves to DELAYED. See Void & delayed.

Step 2 — On-chain proposal

Step 3 — Challenge window

The challenger first approves the oracle for the bond (10 USDC), which it pulls with transferFrom. The amount is fixed when the outcome is proposed (getProposal(conditionId).bondSnapshot). Only the first challenge is accepted. See Challenges.

Step 4a — No challenge, finalise

Permissionless: anyone may call it once the window has closed without a challenge. Internally calls ConditionalTokens.reportPayouts, unlocking redemption.

Step 4b — Challenge, re-verify

Neg-risk markets

Neg-risk markets follow the same propose → challenge → finalize lifecycle, with the same 10 USDC bond and 2-hour default window, through the neg-risk oracle (negRiskOracle in GET /api/platform/contracts). Differences:
  1. Keyed by the group. Calls take the market’s negRiskMarketId, not a conditionId: proposeOutcome(negRiskMarketId, winnerIndex), challengeOutcome(negRiskMarketId), finalizeOutcome(negRiskMarketId).
  2. One proposal settles every outcome. On finalisation the winning outcome’s market pays YES ([1, 0]) and every sibling pays NO ([0, 1]), in one transaction. In exceptional cases the platform proposes a fair-price split instead of a single winner (resolution.proposalKind: "FAIR_PRICE" while pending, resolution.resolutionKind: "FAIR_PRICE" once resolved); it goes through the same challenge window.
  3. Redemption goes through the vault like any other market — see Redemption.
Void, delay and emergency proposal clear exist on the neg-risk oracle too; a neg-risk void is queued before it executes and is not available once any position in the group has been converted — see Void & delayed.

Crypto markets

Crypto markets are binary markets, so they follow the propose → challenge → finalize path above. Two things differ: The evidence is a price rule, not a match result. The outcome is derived from Chainlink price data evaluated against the immutable rule captured on the market at listing time (crypto.productType / direction / targetPrice / deadlineAt / month). Touch products (THRESHOLD_BY_DEADLINE, THRESHOLD_DURING_MONTH) lock YES on the first touch — equality counts — but still settle after the observation window ends. Up/Down windows propose within a minute or two of closing. A 15-minute window closes, and the oracle proposes the direction against the frozen open price (cryptoWindow.openPrice) — typically 1–2 minutes later, versus the ~15 minutes a sports market waits on its data source. What follows is the ordinary challenge window: the platform default unless the window was armed for the fast on-chain path, which gives it a per-market window of zero and finalizes immediately after the proposal. Payouts are [1, 0] for Up (outcome index 0) and [0, 1] for Down (index 1), and cryptoWindow.outcome flips to "UP" / "DOWN" only once the market is RESOLVED — a proposed direction is not a settled one.
Don’t hardcode a settlement delay for crypto. Read resolution.proposedAt and resolution.challengeWindowClosesAt off the market payload — that timestamp is the exact instant finalize becomes legal, and it differs per market.

What you observe as a partner

market_lifecycle is subscribed on /ws/market with the market symbol as the id. Every status flip, including DISPUTED and RESOLVED, also arrives as a status_changed frame.

Redemption

Outcome tokens are held by your vault, so redemption always goes through the vault — calling ConditionalTokens.redeemPositions from your own address redeems nothing. Auto-redeem does it for you by default; anything still redeemable you can claim yourself.

Auto-redeem

Auto-redeem is on for every account unless you turn it off. Once a market is RESOLVED or VOIDED, the platform redeems the positions your vault still holds on it, pays the gas, and credits the payout to your vault: The platform looks for redeemable positions every minute, longest-settled markets first, so a position is normally redeemed within a few minutes of the market’s final status; when many markets settle at once it can take longer. An account that is paused or under another restriction is skipped until the restriction is lifted. Read the setting with GET /api/me/auto-redeem (portfolio:read) and change it with PUT /api/me/auto-redeem, body { "enabled": false } or { "enabled": true } (vault:write, at most 10 calls per 24 hours). Turning it off stops new automatic redeems; a redeem already queued for your account still completes. Claiming yourself works whether auto-redeem is on or off. An automatic redeem shows up like any other: the payout lands in the USDC row’s available in GET /api/me/balances, the position stops being redeemable and then reads redeemed: true (list it with GET /api/me/positions?status=redeemed), and a vault_position_redeemed frame arrives on the vault_positions WebSocket channel. redeemed: true can trail the end of redeemable by several minutes; in between, the position is listed under neither status=redeemable nor status=redeemed.

Claiming yourself

Check redeemable on GET /api/me/positions?status=redeemable first (see Positions).
  1. Gasless — POST /api/me/positions/redeem with { "marketId": "...", "outcomeIndex": 0 }. API-key callers need the vault:write scope and must include auth, an owner-signed RedeemAuthorization. Returns { "jobId": "...", "status": "pending" }; poll GET /api/me/positions/redeem/{jobId} until confirmed.
  2. Self-submitted — the vault owner calls Vault.redeemPositions(conditionId, indexSets) (binary) or Vault.redeemPositionsNegRisk(negRisk.marketId, negRisk.questionIndex, amount) (neg-risk) and pays the gas.
Request and signing details are on the ConditionalTokens page. POST /api/me/redemptions is not enabled yet (503 redeem_not_yet_enabled). Every path credits the payout to your vault, where it shows up in the USDC row’s available in GET /api/me/balances — no further call is needed to “claim” winnings.