How UMA Optimistic Oracle Resolution Actually Works
A senior engineer's breakdown of polymarket uma oracle resolution: proposal, liveness, disputes, DVM votes, and how bots price resolution risk.
Every Polymarket market that says "resolved" got there because a human posted a bond, nobody disputed it inside a fixed window, and a smart contract finalized the result. That human is the UMA proposer, the window is called liveness, and if you run bots on Polymarket you should treat that window as a live, tradeable state — not a formality. Most of the money left on the table around resolution comes from people who model the outcome and forget to model the machine that decides the outcome.
Here's what actually happens, step by step, and where the risk hides.
The lifecycle of a single resolution
Polymarket doesn't resolve markets itself. When a market's end date passes, the resolution question gets pushed to UMA's Optimistic Oracle (specifically the OOv2 contract on Polygon, wired through Polymarket's UmaCtfAdapter). The oracle assumes any answer is correct unless challenged — that's the "optimistic" part.
The sequence looks like this:
- Request. The adapter asks the oracle "what's the outcome of question X?" with an
ancillaryDatablob that encodes the market question, the resolution sources, and the allowed answers (usuallyp1=No,p2=Yes,p3=50/50unknown). - Proposal. Someone — often a bot run by an arbitrageur or by UMA-aligned actors — posts a proposed answer plus a bond (commonly 750 USDC on Polymarket markets, though the reward and bond are set per-request). This starts the liveness clock.
- Liveness window. For a fixed period (Polymarket typically uses a 2-hour liveness on standard markets, longer on some), anyone can dispute by matching the bond. If nobody does, the proposal is accepted and the market settles at that value.
- Dispute. If someone disputes, the optimistic path is dead. The question escalates to UMA's Data Verification Mechanism (DVM), where UMA token holders vote.
- DVM vote. Holders commit and reveal votes over a multi-day cycle (commit phase, then reveal, tied to UMA's ~48-hour voting rounds). The median of revealed votes wins. The correct side gets its bond back plus half the loser's bond; the wrong side eats the loss.
The uncomfortable truth: a market that looks "obvious" to you is only settled when that liveness timer hits zero with no dispute. Until then it's a probabilistic object.
Why liveness is the only clock that matters
Traders anchor on the event end date. Bots should anchor on the proposal timestamp, because liveness starts when the answer is proposed, not when the event ends. A market can end Sunday night and sit unproposed until Monday morning because no bot bothered to post the bond — there's no automatic proposer. That gap is pure carry: you're holding a position that's economically decided but not yet redeemable.
If you're building anything that touches settlement — an arbitrage bot that closes positions the moment a market becomes redeemable, or a strategy that fades mispriced "already decided" markets — you need to poll the oracle state directly, not the Polymarket UI. The states you care about on the UmaCtfAdapter and OOv2:
initializedbut no proposal → market ended, no clock running yet.proposedwith aproposalTimestamp→ liveness clock is live;expirationTimestamp = proposalTimestamp + liveness.disputed→ headed to the DVM; redemption is now days away, not hours.settled→ redeemable.
A minimal read to figure out where you stand:
req = oo_v2.functions.getRequest(requester, identifier, timestamp, ancillary_data).call()
# req.proposedPrice, req.expirationTime, req.disputer (zero addr = undisputed)
now = w3.eth.get_block("latest")["timestamp"]
if req.disputer != ZERO_ADDR:
state = "DVM_PENDING" # settlement in ~2-4 days
elif req.expirationTime and now >= req.expirationTime:
state = "SETTLEABLE" # push settle(), then redeem
elif req.expirationTime:
secs_left = req.expirationTime - now
state = f"LIVE_{secs_left}s" # dispute still possible
else:
state = "UNPROPOSED" # no clock, pure carry risk
The SETTLEABLE state is worth flagging: after liveness expires the outcome is locked, but tokens aren't redeemable until someone calls settle(). That's often you. Bots that assume settlement is automatic sit on dead capital.
Modeling dispute risk
Not every disputable market gets disputed, and knowing which ones will is where the edge lives. Disputes cluster around a few patterns:
- Ambiguous resolution sources. If
ancillaryDatanames a source that's vague or that went offline, expect a dispute. Read the actual bytes; don't trust the pretty question text. - Proposals that contradict obvious reality. Someone proposing "No" on a market that clearly resolved "Yes" is either fat-fingering or farming a dispute bond. Either way, liveness will not run clean.
- Close calls near the boundary condition. "Above 70,000 by Friday close" when the price is at 69,980 — those get contested.
For pricing, I treat each live proposal as a small probability of dispute p_d and a dispute cost c_d in delayed settlement (days of locked capital plus DVM tail risk). Expected redemption time becomes (1 - p_d) * liveness_remaining + p_d * dvm_delay. If your strategy needs capital velocity, a market with even a 5% dispute probability and a 3-day DVM delay has a materially worse carry profile than the headline "2 hours to settle" suggests. This math is exactly what separates a naive close-out from a good one, and it's the backbone of any resolution and dispute trading strategy that actually books the edge.
The DVM tail
Once a market hits the DVM, you're no longer trading the event — you're forecasting how UMA token holders will vote. The median mechanism means you care about the distribution of voters, not the objectively correct answer. In practice the DVM has been reliable on clear questions and messy on genuinely ambiguous ones (the several high-profile Polymarket disputes over the years all lived in that ambiguity). If you hold a position into a DVM vote, size it like you're holding a governance bet, because you are.
Practical build notes
A few things that bite people when they wire this into production:
- Watch proposal and dispute events, not just prices. Subscribe to
ProposePriceandDisputePricelogs on the OOv2 contract filtered to Polymarket's requester. Your fill logic should react within seconds of a dispute, because the momentDisputePricefires, your effective settlement date jumps by days. Any market maker running two-sided quotes needs to pull or widen quotes on a disputed outcome instantly. - Decode ancillaryData at ingest. The resolution rules live there. Parsing them into structured fields at market-onboarding time lets you flag dispute-prone markets before you ever take a position.
- Separate "priced" from "redeemable." Your PnL model should carry a market as economically settled the second liveness expires clean, but your capital model should only free the collateral once
settle()succeeds and tokens redeem. Conflating the two overstates your available margin. - Latency matters at the edges. Getting a proposal in first, or disputing a bad proposal in time, is a mempool game on Polygon. The same order-signing and low-latency plumbing that matters on the CLOB matters here.
For higher-frequency strategies, the resolution layer interacts with your quoting layer in ways that aren't obvious until you've run into them — the CLOB market-making mechanics and your resolution-risk model have to share the same view of market state, or you'll quote into outcomes you can't cleanly exit. A spread bot that ignores which markets are near proposal will happily accumulate inventory it can't unwind for three days.
The short version: liveness windows are the real clock, disputes are a modelable event, and the DVM is a governance bet you should size accordingly. Treat resolution as a state machine you poll, not a footnote you assume.
If you want this resolution-state logic built into a system that trades and manages inventory around it correctly, that's exactly what our Polymarket arbitrage bot work is designed for.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article