This document defines the deterministic oracle-resolution policy for predictify-hybrid markets and events.
The policy applies to:
PredictifyHybrid::create_marketPredictifyHybrid::create_eventPredictifyHybrid::fetch_oracle_resultPredictifyHybrid::refund_on_oracle_failure
It also mirrors the intended behavior of the internal contracts/predictify-hybrid/src/resolution.rs flow.
Each market and event stores:
oracle_config: the primary oracle configurationhas_fallback: whether a fallback oracle is enabledfallback_oracle_config: the backup oracle configuration when enabledresolution_timeout: the per-market/per-event deadline, in seconds, measured fromend_time
has_fallback = false means fallback_oracle_config is the reserved OracleConfig::none_sentinel() value and must never be used for live resolution.
Automatic resolution uses a fixed order:
- Attempt the primary oracle once.
- If and only if
has_fallbackistrue, attempt the fallback oracle once after the primary attempt fails. - If both attempts fail, stop and require manual resolution.
There is no dynamic reordering, no provider scoring, and no implicit retry loop in this path. Auditors and integrators can therefore reason about a single, stable resolution sequence.
The automatic resolution deadline is:
resolution_deadline = end_time + resolution_timeout
Behavior is:
- Before
end_time, oracle resolution is rejected because the market is still open. - From
end_timeup to but not includingresolution_deadline, automatic oracle resolution may be attempted. - At or after
resolution_deadline, automatic oracle resolution stops immediately withRESOLUTION_TIMEOUT_REACHED.
Timeout handling is deliberately fail-closed:
- the contract does not continue trying alternate providers after the deadline
- the contract does not silently extend deadlines
- non-admin refund callers become authorized only once the same per-market timeout has elapsed
The active contract path uses the following deterministic outcomes:
- Primary failure with no fallback configured:
ORACLE_UNAVAILABLE - Primary failure followed by fallback failure:
FALLBACK_ORACLE_UNAVAILABLE - Any automatic resolution attempt at or after the deadline:
RESOLUTION_TIMEOUT_REACHED
The contract emits events to make the resolution path observable:
man_res: manual resolution required after automatic attempts are exhaustedfbk_used: emitted only when a fallback oracle succeedsres_tmo: emitted when the market/event reaches the automatic resolution deadlineref_oracl: emitted when the refund path is used after oracle failure/timeout
For failure cases, the manual-resolution reason string is stable:
oracle_resolution_failed_primary_onlyoracle_resolution_failed_primary_then_fallback
These reason codes are intended for off-chain monitors, audit tooling, and incident playbooks.
Threat model and enforced invariants:
- Fixed ordering prevents an attacker from influencing provider selection at runtime.
- A per-market timeout prevents indefinite oracle polling and makes the refund path predictable.
- The fallback sentinel stays outside the valid oracle-config domain, so
has_fallback = falsecannot collide with a live backup configuration. - Non-admin callers cannot force early cancellation before the stored market timeout expires.
Explicit non-goals:
- This policy does not define provider-specific trust assumptions.
- This policy does not add weighted consensus across multiple oracle providers.
- This policy does not auto-resolve subjective markets without an oracle result.