Resolution
How markets resolve — UMA's Optimistic Oracle for discretionary markets and Pyth for price markets. Assertion, dispute, settlement, reporting, and redemption.
OddMaki has two resolution paths:
- UMA Optimistic Oracle V3 — used for binary markets and market groups. Anyone can assert an outcome; anyone can dispute; disputes escalate to UMA's DVM.
- Pyth — used for price markets. Anyone can trigger resolution after the close time, and the outcome is deterministic from the Pyth historical price.
Both paths end the same way: a payout vector is reported to the CTF and winning token holders redeem for collateral.
UMA Resolution Lifecycle
Under the hood, UMA resolution progresses through these phases. They are inferred from activeAssertionId, AssertionData.settled, and the market's resolved flag rather than an explicit enum:
| Phase | Description | Who Acts |
|---|---|---|
ACTIVE_NO_ASSERTION | Market is active, no assertion exists | Anyone can assert |
ASSERTION_PENDING | Assertion posted, liveness window running | Anyone can dispute via UMA |
ASSERTION_EXPIRED | Liveness expired without dispute | Anyone can settle |
SETTLED_NOT_REPORTED | UMA confirmed the assertion | Anyone can report to CTF |
RESOLVED | Payouts recorded in CTF | Winning token holders can redeem |
Making an Assertion
Anyone can assert a market's outcome by calling assertMarketOutcome. The asserter posts a bond — if the assertion goes undisputed, the bond is returned plus any reward. If disputed and wrong, the bond is lost.
const tx = await client.uma.assertMarketOutcome({
marketId: 42n,
outcome: 'Yes',
autoApprove: true, // auto-approves bond token if needed
});Preconditions:
- No active assertion on this market
- Caller must hold the effective bond (USDC)
- USDC approved to the Diamond contract (
autoApprove: truehandles this)
The outcome string must exactly match one of the market's defined outcomes ("Yes", "No", or the market group's specific outcome labels).
Challenge Period
During the liveness window, anyone can dispute an assertion by posting a counter-bond directly through UMA's Optimistic Oracle V3. Disputes are not handled by OddMaki contracts — they go through UMA's standard dispute mechanism.
- Disputer calls
disputeAssertion()on the UMA Oracle (not the Diamond) - UMA escalates the dispute to its DVM for human arbitration
- DVM voters resolve the dispute
- If upheld: assertion accepted, disputer loses bond
- If rejected: assertion rejected, asserter loses bond, a new assertion can be submitted
You can check assertion status at any time:
const details = await client.uma.getAssertionDetails(assertionId);
console.log(details.expirationTime); // Unix timestamp when liveness expires
console.log(details.canSettle); // true if liveness expired
console.log(details.isDisputed); // true if disputedSettlement
After the liveness window expires without dispute, anyone can settle the assertion:
const tx = await client.uma.settleAssertion(assertionId);Settlement triggers UMA to finalize the result. UMA synchronously calls back into the Diamond to record whether the assertion was truthful. If the assertion was rejected (via dispute), the oracle lock is cleared and a new assertion can be made.
Reporting
After settlement, the resolved outcome must be reported to the Conditional Token Framework. This records the payout vector and enables redemption.
const tx = await client.uma.reportResolution({
marketId: 42n,
outcome: 'Yes',
});The outcome parameter must exactly match the settled assertion's outcome.
Payout vectors:
| Resolved As | Payout Vector | Meaning |
|---|---|---|
"Yes" | [1, 0] | YES holders win |
"No" | [0, 1] | NO holders win |
"Invalid" | [1, 1] | Equal split — both sides redeem proportionally |
Market group cascade: If a market in a group resolves YES, all sibling markets automatically resolve as NO in the same transaction — no separate assertion needed for the others.
Pyth Resolution
Price markets skip UMA entirely. After the close time plus the resolution window, anyone calls:
const tx = await client.priceMarket.resolvePyth(marketId);The market is resolved deterministically from the Pyth historical price at closeTime:
- Up/Down — YES if
finalPrice > capturedOpenPrice, otherwise NO - Strike — YES if
finalPrice >= strikePrice, otherwise NO
There is no bond, no liveness, no dispute path. Once resolved, the payout vector is set on the CTF and winners redeem. See Price Markets for the full mechanics.
Redemption
After resolution is reported (by either path), holders of winning outcome tokens can redeem them for collateral:
const tx = await client.uma.redeemWinnings(42n);The CTF burns winning tokens and returns collateral (USDC). Partial redemptions are supported — you don't have to redeem all at once.
Check resolution status before redeeming:
const status = await client.uma.getResolutionStatus(42n);
console.log(status.resolved); // boolean
console.log(status.winningOutcome); // "YES" | "NO" | "INVALID" | null
console.log(status.payouts); // [1n, 0n]Cancelling Orders After Resolution
Orders resting on a resolved market can be cancelled in bulk to refund their locked collateral:
await client.trade.cancelOrdersOnResolvedMarket(42n, [orderId1, orderId2, …]);Dispute & Edge Cases (UMA)
If a UMA assertion is disputed and rejected by UMA's DVM, the oracle lock is cleared. A new assertion can then be submitted — the market doesn't get stuck. There is no admin override or emergency resolution mechanism; UMA resolution is fully governed by the UMA Oracle lifecycle.
Key edge cases:
- Expired but unsettled: An expired assertion stays pending until someone calls
settleAssertion()— there's no auto-settlement - No assertion: A market with no assertion stays active indefinitely until someone posts one
- Double assertion prevention: Only one assertion can be active per market at a time
What's Next
- Oracle Settings → — configure bond, reward, and liveness
- Price Markets → — Pyth-powered auto-resolved markets
- Token Mechanics → — how outcome tokens and redemption work
- Creating Markets → — market creation parameters