Prediction markets
Settlement and refunds
How an outcome is read from the chain, who may settle, and the cases that refund everyone at par.
Status#
How the outcome is read#
LaunchOutcomeOracle.settle(marketId) reads one metric view on the launchpad’s own contracts, compares it with the question’s threshold and records the result. The caller supplies only the market id: nobody signs anything, so nobody can sign something false.
observedValue = <metric view on the subject, read at the settling block>
outcome = compare(observedValue, comparator, threshold) // true = YES| Field | Value |
|---|---|
outcome | YES when true |
observedValue | The value the outcome was judged on |
settledAt / settledAtBlock | When the value was read — not the deadline |
settler | The address that called it |
| Metric | Rule |
|---|---|
GRADUATED | 1 when graduated; 0 when never completed or when its refund hatch is open. A complete curve inside its graduation grace period refuses with GraduationPending |
| Creator metrics | Need the creator’s aggregate committed at or after the deadline and covering every indexed launch (CreatorStatsStale). syncAndSettle brings it up to date and settles in one transaction |
PROTOCOL_FEES_METERED_WEI | Reads the splitter pinned when the market was registered |
Who may settle, and when#
Settlement is permissionless. It happens once, inside the window that opens at the deadline.
| Setting | Production |
|---|---|
| Opens | At the deadline (earlier only for a locked outcome) |
| Closes | deadline + settlementWindow |
| Minimum window | 15 minutes — MIN_SETTLEMENT_WINDOW |
| Maximum window | 7 days — MAX_SETTLEMENT_WINDOW |
| Default | 6 hours — DEFAULT_SETTLEMENT_WINDOW, when the creator passes 0 |
| After the window | settle refuses with SettlementWindowClosed; the market can only void |
A settlement keeper settles markets still unsettled late in their window. It has the same power as any address — settle takes only a market id — and its settlements earn it nothing. See Settler fees.
Settling before the deadline#
Before the deadline settle accepts only a locked outcome — one no later read can change. Otherwise it refuses with TooEarlyToSettle.
| Comparator | Locked when | Outcome |
|---|---|---|
GT / GTE | Already true | YES |
LT / LTE | Already false | NO |
NEQ | Value above the threshold | YES |
EQ | Only at the metric’s ceiling | — |
Once settled or voided, a market stops taking stakes even before its deadline. Creator markets never settle early.
Voids and refunds at par#
| Case | Trigger | Fees |
|---|---|---|
| Expired | voidExpired(marketId), by anyone, after deadline + settlementWindow | None |
| Emergency void | PredictionMarketFactory.emergencyVoid(marketId), owner only | None |
| One side empty at settlement | A normal settlement — not a void | None |
A void can only refund; it cannot redirect funds or choose an outcome. It cannot be undone. A void refunds the side that was right too, which is why settling inside the window matters.
reason | From |
|---|---|
"EXPIRED" | voidExpired |
"FACTORY" | emergencyVoid |
Claiming#
| Market state | Claim pays |
|---|---|
| Settled, both sides staked | Winners: stake × payout pool / winning pot. Losers: nothing (NothingToClaim) |
| Settled, one side empty | Every stake back at par |
| Voided | Every stake back at par |
| Not resolved | Refused — MarketNotResolved |
| Rule | Value |
|---|---|
| Deadline | None. Unclaimed funds are never swept |
| Pause | Never blocks a claim |
| Once per address | AlreadyClaimed |
| Claim operator | claimAsOperator(user) — only the operator the user named on the factory; paid to the operator |
The fees are swept separately by anyone after resolution: sweepFee() and sweepMarketingFee(). See Settler fees.