Documentation Index
Fetch the complete documentation index at: https://docs.outcome.xyz/llms.txt
Use this file to discover all available pages before exploring further.
- Bots listen for oracle-trigger conditions (e.g. asset price ≥ target).
- Upon trigger they call
MarketContract.resolve() passing Pyth price ID.
- Contract sets outcome ⟶ users burn winning tokens for $1 each.
/**
* @notice Push a Pyth update, evaluate the condition, and settle a market.
* @param market The prediction-market contract being settled.
* @param updateData Raw Pyth price-feed update packets.
*/
function updatePriceAndFulfill(
address market,
bytes[] calldata updateData
) external onlyRelayer payable {
require(market != address(0), "Oracle: zero market");
require(updateData.length > 0, "Oracle: empty update");
/*── 1. Pay Pyth fee and publish the update ───────────────────*/
uint256 fee = PYTH.getUpdateFee(updateData);
require(msg.value >= fee, "Oracle: fee too low");
/*── 2. Pull market parameters ────────────────────────────────*/
IMarket.TriggerCondition memory tc = IMarket(market).getMarketParams();
bytes32[] memory priceIds = new bytes32[](1);
priceIds[0] = tc.assetId;
/*── 3. Fetch and validate a fresh price ──────────────────────*/
PythStructs.PriceFeed[] memory feeds =
PYTH.parsePriceFeedUpdates{ value: fee }(
updateData,
priceIds,
0,
uint64(block.timestamp)
);
/*── 4. Use the single feed we requested ───────────────────────────────────*/
PythStructs.PriceFeed memory feed = feeds[0];
_validatePriceStruct(feed.price.price, feed.price.conf, feed.price.expo);
int256 triggerPx = int256(uint256(tc.triggerPrice));
int256 oraclePx = int256(feed.price.price);
uint256 winningToken;
if (tc.op == IMarket.Operator.LT) {
winningToken = (oraclePx <= triggerPx) ? 1 : 2;
} else { // GT
winningToken = (oraclePx >= triggerPx) ? 1 : 2;
}
/*── 5. Resolve the market ───────────────────────────────────*/
IMarket(market).resolveMarketOracle(winningToken);
/*── 6. Refund any excess ETH ────────────────────────────────*/
uint256 refund = msg.value - fee;
if (refund != 0) payable(msg.sender).transfer(refund);
}
Typical resolution timeline
- A market’s trigger condition is met and a bot submits the relevant Pyth update.
- The contract fetches a fresh price, validates it, and determines the winning side.
- The market resolves on-chain and claims open immediately for the winning token.
Most markets finalize within seconds to a few minutes after the oracle reports the triggering price.
Oracle failure contingency
- If Pyth is unavailable or returns stale data,
updatePriceAndFulfill reverts.
- Bots continually retry the call until a valid price is published.
- Until then, the market remains unresolved and funds stay locked.
The retry and escalation flow is:
SEDA Resolution (Oracle Program)
SEDA resolution uses a Data Request (DR) executed by the SEDA network. The Oracle Program in this repo fetches fixture results, outputs an ABI-encoded payload, and the EVM contract verifies a signed result before resolving the market.
What the Oracle Program returns
- Primary path (resolve_outcome): ABI
(uint8 winnerToken, string resultInfo)
winnerToken is 1 (yes/win/draw) or 2 (no/lose/not draw)
resultInfo is a human-readable string from the fixture API
Execution + Tally
- Execution phase fetches SportMonks fixture data and computes the winning token.
- Tally phase collects reveals and requires unanimous agreement. If any mismatch exists, the tally returns an error and the DR is not finalized.
Exec inputs (JSON)
operation (optional, default resolve_outcome)
fixture_id (required)
predicted_participant_id (required if draw = false)
draw (bool)
market_id (required if operation = cancel_market)
Outputs
resolve_outcome -> (uint8 winnerToken, string resultInfo)
cancel_market -> uint256 yesMarketPrice (6 decimals)
End-to-end flow
On-chain verification (fulfillAndResolve)
Failure and retry behavior
- If the tally phase detects mismatched reveals, it returns an error and no consensus is produced.
- If
result.consensus is false or exitCode != 0, fulfillAndResolve reverts.
- Relayers can re-submit the DR after the underlying data source stabilizes.
Info: Resolution is deterministic. The EVM contract only accepts results that pass SEDA consensus and FAST signature verification.
/**
* @notice Verify the FAST signature, decode the winning token, and resolve the market.
* @dev Expects the Oracle Program payload to be: (uint8 winnerToken, string resultInfo).
*/
function fulfillAndResolve(
address market,
SedaDataTypes.Result calldata result,
bytes calldata signature
) external onlyRelayer {
if (market == address(0)) revert InvalidMarket();
if (!result.consensus) revert NoConsensusYet();
if (result.exitCode != 0) revert OracleExecutionError(result.exitCode);
bytes memory rawResult = fastVerifier.verifyAndGetResult(result, signature);
(uint8 parsedToken, string memory info) = abi.decode(rawResult, (uint8, string));
if (parsedToken != 1 && parsedToken != 2) {
revert InvalidWinningToken(parsedToken);
}
IMarket(market).resolveMarketOracle(parsedToken);
lastWinningToken = parsedToken;
lastResultInfo = info;
lastUpdated = uint64(block.timestamp);
lastResolvedRequestId = result.drId;
emit OutcomeResolved(result.drId, market, parsedToken, info);
}