The writer is whoever posted collateral at fill time, recorded by
ConvallaxCore.mintFor during the atomic fill — the maker for a long trade (makerSelling = true) and the taker for a short trade (makerSelling = false). The holder is whoever received the freshly minted option tokens.Resolution Price
The resolution price S determines payoffs for all options in a series. It is expressed asresolutionBps — an integer from 0 to 100 representing the YES token price at expiry.
Resolution Methods
The on-chain contract accepts anyresolutionBps in [0, 100]. The backend determines the value with a tiered waterfall — it tries the most authoritative source first and falls back to a market-price signal only when needed. The first tier that produces a price wins, and the chosen method is recorded in the response meta.method.
1
1. Official resolution — meta.method: official
If the underlying Polymarket market has resolved (closed), read the YES token’s settled price directly. The closed market’s
outcomePrices is mapped to the option’s YES token via the market’s clobTokenIds, so the result is independent of Yes/No ordering:- YES wins →
resolutionBps = 100 - NO wins →
resolutionBps = 0 - Rare UMA 50/50 draw →
resolutionBps = 50
closed once its UMA proposal has cleared the dispute window, so this is authoritative ground truth. Used whenever the market resolves at or before expiry.2
2. Price TWAP — meta.method: twap
For options that expire before the market resolves, use the time-weighted average price of the YES token over the 30 minutes before expiry:
- Fetch the YES token’s price history from the Polymarket CLOB
prices-historyendpoint over[expiry − 1800s, expiry]at 1-minute fidelity - Drop the trailing current-time datapoint the endpoint appends, keeping only samples inside the window
- Compute the time-weighted average, holding each sample until the next:
TWAP = Σ pᵢ·(tᵢ₊₁ − tᵢ) / (t_last − t_first) - Map to basis points:
resolutionBps = clamp(round(TWAP × 100), 0, 100)
prices-history series is the order-book midpoint when the bid/ask spread is tight (≤ $0.10) and automatically falls back to the last-traded price when the spread is wider, so a single source covers both regimes. Time-weighting (rather than volume-weighting) resists manipulation from large or wash trades, since moving the average requires holding the book displaced for the whole window.Manual review for thin windows. If the 30-minute TWAP window contains no price points (an illiquid or inactive market with no continuous data), the backend does not auto-resolve from a single stale trade — doing so would be a cheap manipulation vector. Instead it refuses to sign and the series is escalated to manual review, then settled via the owner-only break-glass
settle().Manual review generally follows the same logic the automated tier would have used, but with human approval: locate the most recent YES trade at or before expiry, then compute a TWAP over the 30-minute window ending at that trade’s timestamp (again using prices-history midpoints where available, falling back to traded prices). A reviewer confirms the window is representative and not the product of a manipulative print before settling. This approach was recommended by the protocol’s auditor to eliminate the single-trade manipulation surface.Settlement Attestation (EIP-712)
The resolution signer creates a signed attestation that anyone can relay on-chain:
The attestation is valid for 15 minutes after creation.
Settlement Flow
Two settlement paths
Settlement is automatic. A backend keeper continuously watches for expired-but-unsettled series, resolves the price via the waterfall above, and submits
settleWithAttestation() on-chain itself — so holders never need to trigger settlement. The permissionless path remains available as a backstop: any holder can settle their own series by relaying an attestation from POST /settlement/prepare.Payoff Formulas
Once a series is settled with resolution price S (resolutionBps), the payoffPerWhole (USDC raw units per 1 whole option) is computed by OptionMath.payoffPerWhole:
Worked example: call at K=50, resolution S=80
Worked example: call at K=50, resolution S=80
payoffPerOption = (80 − 50) × $0.01 = $0.30- Holder with 100 options claims: 100 × $0.30 = 30 USDC
- Writer collateral was: 100 × $0.50 = 50 USDC
- Writer reclaims: 50 − 30 = 20 USDC
Worked example: put at K=60, binary resolution S=0 (NO wins)
Worked example: put at K=60, binary resolution S=0 (NO wins)
payoffPerOption = (60 − 0) × $0.01 = $0.60(maximum put payoff)- Holder with 50 options claims: 50 × $0.60 = 30 USDC
- Writer posted 50 × $0.60 = 30 USDC collateral → writer reclaims 0 USDC
Worked example: TWAP continuous settlement
Worked example: TWAP continuous settlement
- 30-minute TWAP = 0.5923 →
resolutionBps = 59 - Call at K=50: payoff = (59 − 50) × 0.09 per option**
- Put at K=65: payoff = (65 − 59) × 0.06 per option**
Claiming Payouts
After settlement, participants claim their payouts:Holders — claimHolderPayout(seriesId, amount)
Burns the specified amount of option tokens and transfers the USDC payout:
payoffPerOption = 0), the tokens are burned but no USDC is transferred.
