OddMaki
Operations

Resolution

How markets resolve through UMA's Optimistic Oracle — assertion, challenge, settlement, and redemption.

Markets resolve through UMA's Optimistic Oracle V3. Anyone can assert an outcome by posting a bond, and if no one disputes it within a liveness window, the assertion is accepted.

Resolution Lifecycle

PhaseDescriptionWho Acts
ACTIVE_NO_ASSERTIONMarket is active, no assertion existsAnyone can assert
ASSERTION_PENDINGAssertion posted, liveness window runningAnyone can dispute via UMA
ASSERTION_EXPIREDLiveness expired without disputeAnyone can settle
SETTLED_NOT_REPORTEDUMA confirmed the assertionAnyone can report to CTF
RESOLVEDPayouts recorded in CTFWinning 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 sufficient bond tokens (USDC)
  • USDC approved to the Diamond contract

The outcome string must exactly match one of the market's defined outcomes (e.g., "Yes" or "No").

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.

  1. Disputer calls disputeAssertion() on the UMA Oracle (not the Diamond)
  2. UMA escalates the dispute to its DVM for human arbitration
  3. DVM voters resolve the dispute
  4. If upheld: assertion accepted, disputer loses bond
  5. 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 disputed

Settlement

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 AsPayout VectorMeaning
"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.

Redemption

After resolution is reported, 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 (e.g., USDC). Partial redemptions are supported — you don't have to redeem all at once.

You can 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]

Dispute Handling

If an 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; 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