Documentation Index Fetch the complete documentation index at: https://mintlify.com/kuestcom/prediction-market/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Kuest uses the UMA Protocol for decentralized market resolution. This system allows anyone to propose outcomes, with a dispute period for challenges, ensuring fair and accurate resolutions.
Resolution Process
Market Closes
When a market reaches its end date, it stops accepting new trades. The market enters the resolution phase. // Market becomes inactive when end_at date passes
const isActive = market . is_active && new Date ( market . end_at ) > new Date ()
Outcome Proposal
Anyone can propose a resolution outcome by:
Visiting the UMA proposal interface
Posting a bond (typically in USDC)
Submitting the proposed outcome (Yes/No/Invalid)
The proposer URL is automatically generated: // From src/lib/uma.ts
const query = new URLSearchParams ()
query . set ( 'project' , 'Your Platform Name' )
query . set ( 'transactionHash' , requestTxHash )
query . set ( 'eventIndex' , String ( logIndex ))
const proposeUrl = `https://oracle.uma.xyz/propose? ${ query . toString () } `
The proposer who correctly resolves the market receives the USDC reward configured during market creation (typically 5 USDC per market).
Liveness Period
After a proposal is submitted, there’s a liveness period (default: 1 hour) during which the proposal can be disputed. Resolution Status Values:
posed: Initial state, no proposal yet
proposed: First proposal submitted
reproposed: New proposal after a dispute
challenged: Proposal is being disputed
disputed: Formal dispute initiated
resolved: Final outcome confirmed
// From resolution sync route
const RESOLUTION_LIVENESS_DEFAULT_SECONDS = 60 * 60 // 1 hour
function computeResolutionDeadline (
status : string ,
flagged : boolean ,
lastUpdateTimestamp : number ,
livenessSeconds : number | null ,
negRisk : boolean
) : string | null {
if ( status === 'resolved' ) {
return null // Already finalized
}
if ( flagged ) {
// Extended safety period for flagged proposals
const safetyPeriod = negRisk ? 3600 : 3600
return new Date (( lastUpdateTimestamp + safetyPeriod ) * 1000 ). toISOString ()
}
if ( status === 'posed' || status === 'proposed' || status === 'reproposed' ) {
const effectiveLiveness = livenessSeconds ?? RESOLUTION_LIVENESS_DEFAULT_SECONDS
return new Date (( lastUpdateTimestamp + effectiveLiveness ) * 1000 ). toISOString ()
}
return null
}
Dispute Window
During the liveness period, anyone can dispute an incorrect proposal by:
Posting a dispute bond (larger than the proposal bond)
Providing evidence for the correct outcome
Initiating the dispute process
The market status updates to challenged or disputed. // Resolution timeline builder checks dispute status
const isDisputed = Boolean (
condition ?. resolution_was_disputed ||
status === 'challenged' ||
status === 'disputed'
)
Resolution Settlement
If no disputes occur during the liveness period, the proposal is automatically accepted. If disputed:
UMA token holders vote on the correct outcome
Voting period lasts 48-96 hours
Winning side receives the losing side’s bond
Correct outcome is finalized on-chain
The resolution price is normalized: // Resolution price values
const RESOLUTION_PRICE_YES = 1000000000000000000 n // 1e18 = 100% YES
const RESOLUTION_PRICE_INVALID = 500000000000000000 n // 0.5e18 = 50/50 invalid
const RESOLUTION_UNPROPOSED_PRICE_SENTINEL = 69 n // Not yet proposed
function normalizeResolutionPrice ( rawValue : string | null ) : number | null {
if ( ! rawValue ) return null
const value = BigInt ( rawValue )
if ( value === RESOLUTION_UNPROPOSED_PRICE_SENTINEL ) return null
if ( value === 0 n ) return 0 // NO
if ( value === RESOLUTION_PRICE_YES ) return 1 // YES
if ( value === RESOLUTION_PRICE_INVALID ) return 0.5 // INVALID
return null // Intermediate value
}
Payout Distribution
Once resolved, the system calculates payouts for each outcome: // From resolution sync route
async function updateOutcomePayouts ( conditionId : string , price : number ) {
const payoutYes = price >= 1 ? 1 : price <= 0 ? 0 : price
const payoutNo = price <= 0 ? 1 : price >= 1 ? 0 : price
const updates = [
{ index: 0 , payout: payoutYes },
{ index: 1 , payout: payoutNo }
]
for ( const update of updates ) {
const isWinningOutcome = update . payout > 0
await db
. update ( outcomesTable )
. set ({
is_winning_outcome: isWinningOutcome ,
payout_value: String ( update . payout )
})
. where ( and (
eq ( outcomesTable . condition_id , conditionId ),
eq ( outcomesTable . outcome_index , update . index )
))
}
}
Users who hold winning outcome shares can redeem them for USDC.
UMA Integration
Oracle Contracts
Kuest integrates with UMA’s Optimistic Oracle contracts:
// Contract addresses from src/lib/contracts.ts
const UMA_CTF_ADAPTER_ADDRESS = '0x20088f6aa9D8D5947c9f002167355Cb332134bf8'
const UMA_NEG_RISK_ADAPTER_ADDRESS = '0x724259Fe88100FE18C134324C4853975FBDa4d76'
// Polymarket compatibility
const UMA_CTF_ADAPTER_POLYMARKET_ADDRESS = '0x65070BE91477460D8A7AeEb94ef92fe056C2f2A7'
const UMA_NEG_RISK_ADAPTER_POLYMARKET_ADDRESS = '0x2F5e3684cb1F318ec51b00Edba38d79Ac2c0aA9d'
Resolution Data Structure
interface SubgraphResolution {
id : string // Question ID / request ID
status : string // posed, proposed, disputed, resolved
flagged : boolean // Safety flag for admin review
paused : boolean // Temporarily halted
wasDisputed : boolean // Had at least one dispute
approved : boolean | null // Admin approval status
lastUpdateTimestamp : string // Unix timestamp
price : string | null // Resolution outcome (1e18 = YES)
liveness : string | null // Dispute period in seconds
}
Proposal Links
The system automatically generates proposal and settlement URLs:
// Propose a new outcome
const proposeUrl = buildUmaProposeUrl ({
uma_request_tx_hash: '0x123...' ,
uma_request_log_index: 42
}, 'Kuest' )
// => https://oracle.uma.xyz/propose?project=Kuest&transactionHash=0x123...&eventIndex=42
// View settled outcome
const settledUrl = buildUmaSettledUrl ({
uma_request_tx_hash: '0x123...' ,
uma_request_log_index: 42
}, 'Kuest' )
// => https://oracle.uma.xyz/settled?project=Kuest&transactionHash=0x123...&eventIndex=42
Resolution Timeline
The frontend displays a visual timeline showing the resolution progress:
// Timeline phases
export type ResolutionTimelineItemType =
| 'outcomeProposed' // Initial proposal submitted
| 'noDispute' // No disputes raised
| 'disputed' // Dispute in progress
| 'disputeWindow' // Awaiting dispute period end
| 'finalReview' // Admin or system review
| 'finalOutcome' // Resolution complete
export interface ResolutionTimelineItem {
id : string
type : ResolutionTimelineItemType
icon : 'check' | 'gavel' | 'open'
state : 'done' | 'active' | 'pending'
outcome : 'yes' | 'no' | 'invalid' | null
timestampMs : number | null
deadlineMs : number | null
remainingSeconds : number | null
}
Example timeline for a resolved market:
[
{
id: 'outcome-proposed' ,
type: 'outcomeProposed' ,
icon: 'open' ,
state: 'done' ,
outcome: 'yes' ,
timestampMs: 1704067200000 ,
deadlineMs: null ,
remainingSeconds: null
},
{
id: 'no-dispute' ,
type: 'noDispute' ,
icon: 'check' ,
state: 'done' ,
outcome: 'yes' ,
timestampMs: 1704070800000 ,
deadlineMs: null ,
remainingSeconds: null
},
{
id: 'final-outcome' ,
type: 'finalOutcome' ,
icon: 'gavel' ,
state: 'done' ,
outcome: 'yes' ,
timestampMs: 1704070800000 ,
deadlineMs: null ,
remainingSeconds: null
}
]
Dispute Handling
When to Dispute
Dispute a proposal if:
The proposed outcome is factually incorrect
Resolution rules were not followed
The outcome source was misinterpreted
The proposal violates market integrity
Dispute Process
Identify Incorrect Proposal
Monitor newly proposed markets and verify outcomes against resolution rules.
Prepare Evidence
Gather proof of the correct outcome:
Screenshots of authoritative sources
Archived web pages
Official announcements
Data from trusted APIs
Post Dispute Bond
Visit the UMA dispute interface and post the required bond (typically 2x the proposal bond).
Submit Dispute
Explain why the proposal is incorrect and provide your evidence.
Await Vote
UMA token holders review the dispute and vote on the correct outcome.
Resolution
If your dispute is correct, you receive:
Your original bond back
The incorrect proposer’s bond
Resolution rewards (if applicable)
Dispute Tracking
The system tracks dispute history:
// Database fields
interface Condition {
resolved : boolean
resolution_status : string
resolution_flagged : boolean
resolution_paused : boolean
resolution_was_disputed : boolean // Ever had a dispute
resolution_approved : boolean | null
resolution_deadline_at : Date | null
resolution_price : string | null
resolution_liveness_seconds : number | null
}
Automated Resolution Sync
The platform automatically syncs resolution data from the UMA subgraph:
// Cron job endpoint: /api/sync/resolution
export const maxDuration = 300 // 5 minutes
const RESOLUTION_SUBGRAPH_URL =
'https://api.goldsky.com/api/public/project_cmkeqj653po3801t6ajbv1wcv/subgraphs/resolution-subgraph/1.0.0/gn'
const SYNC_TIME_LIMIT_MS = 250_000 // 4 minutes
const RESOLUTION_PAGE_SIZE = 200
Sync Process
Acquire Sync Lock
Prevent concurrent syncs using database locking: const lockAcquired = await tryAcquireSyncLock ()
if ( ! lockAcquired ) {
return { skipped: true , message: 'Sync already running' }
}
Fetch Resolution Updates
Query the UMA subgraph in pages: {
marketResolutions (
first : 200 ,
orderBy : lastUpdateTimestamp ,
orderDirection : asc
) {
id
status
flagged
paused
wasDisputed
approved
lastUpdateTimestamp
price
liveness
}
}
Update Database
For each resolution, update:
Condition resolution status
Market active/resolved flags
Outcome payout values
Event status (if all markets resolved)
await db . update ( conditionsTable )
. set ({
resolved: isResolved ,
resolution_status: status ,
resolution_flagged: flagged ,
resolution_price: String ( resolutionPrice ),
resolution_was_disputed: wasDisputed ,
resolution_deadline_at: deadlineAt
})
. where ( eq ( conditionsTable . id , conditionId ))
Update Cursor
Save sync progress for incremental updates: await updateResolutionCursor ({
lastUpdateTimestamp: 1704070800 ,
id: '0x123...'
})
Event Status Updates
When markets are resolved, event status automatically updates:
const nextStatus : 'draft' | 'active' | 'resolved' | 'archived' =
! hasMarkets
? 'draft'
: ! hasUnresolvedMarket
? 'resolved'
: hasActiveMarket
? 'active'
: 'archived'
Settlement
After resolution, users can settle their positions:
Merging Shares
Users holding shares in both outcomes can merge them to recover USDC:
// Merge YES and NO shares to get back collateral
const mergeTx = {
to: isNegRiskMarket
? UMA_NEG_RISK_ADAPTER_ADDRESS
: CONDITIONAL_TOKENS_CONTRACT ,
data: encodeFunctionData ({
abi: ConditionalTokensABI ,
functionName: 'mergePositions' ,
args: [
collateralToken ,
parentCollectionId ,
conditionId ,
partition ,
amount
]
})
}
Redeeming Winning Shares
Users with winning shares redeem them for USDC:
// Redeem winning outcome shares
const redeemTx = {
to: isNegRiskMarket
? UMA_NEG_RISK_ADAPTER_ADDRESS
: CONDITIONAL_TOKENS_CONTRACT ,
data: encodeFunctionData ({
abi: ConditionalTokensABI ,
functionName: 'redeemPositions' ,
args: [
collateralToken ,
parentCollectionId ,
conditionId ,
indexSets // Array of outcome indices to redeem
]
})
}
Best Practices
Monitor Proposals Set up alerts for new proposals on your markets to quickly verify correctness.
Document Sources Keep records of outcome sources during market creation for easy resolution.
Incentivize Proposals Set appropriate reward amounts to attract proposers (typically 5+ USDC).
Clear Resolution Rules Write unambiguous rules to minimize disputes and ensure fair outcomes.
Troubleshooting
No Proposal Submitted
Issue : Market closed but no one proposed an outcome.
Solution :
Increase the resolution reward for future markets
Manually propose using the UMA interface
Contact your community to encourage participation
Incorrect Proposal
Issue : Someone proposed the wrong outcome.
Solution :
Gather evidence of the correct outcome
Submit a dispute within the liveness period
Engage the UMA community for voting support
Sync Delays
Issue : Resolution status not updating on your platform.
Solution :
Check cron job execution logs
Verify CRON_SECRET is configured correctly
Manually trigger sync: GET /api/sync/resolution with authorization header
Check Sentry for sync errors
Settlement Fails
Issue : Users can’t redeem winning shares.
Solution :
Verify the market is fully resolved (resolution_status = 'resolved')
Check that payout values are set in the database
Ensure users are calling the correct contract (CTF vs NegRisk adapter)
Verify sufficient POL gas balance
Market Creation Create markets with proper resolution configuration
UMA Documentation Learn more about UMA’s Optimistic Oracle
Admin Panel Monitor market resolution status
API Reference Resolution sync API endpoint