Overview
When a price proposal on UMA’s Optimistic Oracle is disputed, the UMA CTF Adapter automatically handles the dispute through thepriceDisputed() callback. This mechanism ensures market integrity by resetting questions and requesting new price proposals when disputes occur.
The priceDisputed Callback
ThepriceDisputed() function is an Optimistic Oracle callback defined at UmaCtfAdapter.sol:163. It’s triggered automatically when:
- Someone proposes an answer to the market question
- A disputer challenges that proposal by posting a bond
- The Optimistic Oracle invokes the callback on this contract
Function Signature
onlyOptimisticOracle modifier).
Dispute Flow
Case 1: First Dispute (Normal Reset)
When a question is disputed for the first time:- Derive questionID:
keccak256(ancillaryData)at line 164 - Load question data: Retrieve from storage mapping
- Check if already resolved: If
questionData.resolved == true, refund reward and exit (lines 170-173) - Check reset flag: If not previously reset, proceed with reset
- Call
_reset(): Resets the question at line 182
_reset() does (UmaCtfAdapter.sol:390):
- Updates
requestTimestampto currentblock.timestamp - Sets
reset = trueflag - Submits a new price request to the Optimistic Oracle
- Emits
QuestionResetevent
Case 2: Second Dispute (Refund Mode)
If a question that has already been reset is disputed again:- Sets
refund = trueflag - Does not submit another price request
- Returns immediately
- The
refundflag ensures the creator gets their reward back - An admin must call
reset(questionID)to send a new price request - This prevents infinite dispute loops
Case 3: Already Resolved
If a dispute callback arrives after the question was already resolved (e.g., viaresolveManually()):
- Refunds the reward to the question creator immediately
- Does not modify any state
- Returns without further action
Reset Behavior Details
New Price Request Parameters
When_reset() is called (lines 390-411), it:
- Updates timestamp:
questionData.requestTimestamp = block.timestamp - Marks as reset:
questionData.reset = true - Conditionally resets refund: If
resetRefund == true, setsrefund = false - Requests new price: Calls
_requestPrice()with:- Same ancillary data: The question itself doesn’t change
- Same reward:
questionData.reward - Same proposal bond:
questionData.proposalBond - Same liveness:
questionData.liveness - New timestamp:
block.timestamp
Who Pays for Reset?
Therequestor parameter determines who funds the new price request:
- Automatic reset (dispute callback):
requestor = address(this)- the adapter pays from refunded rewards - Admin manual reset:
requestor = msg.sender- the admin pays
_requestPrice() logic at lines 350-361.
Admin Manual Reset
If thepriceDisputed() callback fails or a question needs resetting after two disputes:
- Question must be initialized
- Question must not be resolved
- Check if refund is needed
- If
refund == true, refund the reward to creator - Call
_reset()withresetRefund = trueto clear the refund flag - Admin (caller) pays for the new price request
Refund Mechanics
Therefund flag controls when rewards should be returned to the question creator:
When Refund is Set
- Second dispute: Set in
priceDisputed()at line 176 - Ignored price: Set during
_reset()when Oracle returns ignore price (line 423)
When Refund is Executed
Refund happens at resolution:- Automatic resolution:
_resolve()checks and refunds at lines 430-431 - Manual resolution:
resolveManually()checks and refunds at line 267 - Manual reset:
reset()checks and refunds at line 247
Events
QuestionReset event is emitted when a question is reset:Edge Cases
Race Condition: Manual Resolution During Dispute
If an admin manually resolves a question while a dispute is being processed:resolveManually()setsresolved = true- Later,
priceDisputed()callback executes - Callback detects
resolved == true - Reward is refunded, no state changes
Ignore Price After Dispute
If the Optimistic Oracle returns the “ignore price” (type(int256).min) after a reset:
refund = true is set.
Integration Guidelines
- Listen for QuestionReset events: Track when markets are disputed
- Monitor the reset flag: Check if
getQuestion(questionID).reset == true - Handle refund scenarios: If
refund == true, expect reward to be returned on resolution - Multiple resets require admin: After automatic reset, only admins can reset again
- New timestamp after reset: Use updated
requestTimestampto query the Optimistic Oracle
Code References
- priceDisputed callback:
UmaCtfAdapter.sol:163 - _reset function:
UmaCtfAdapter.sol:390 - Admin reset:
UmaCtfAdapter.sol:241 - Refund logic:
UmaCtfAdapter.sol:447 - Reset limit check:
UmaCtfAdapter.sol:175