How It Works
TOC uses a Prediction Option Protocol (POP) model where consumers request verified data through a standardized lifecycle. Every POP goes through creation, resolution, optional dispute, and finalization.
The POP Lifecycle
At its simplest, the lifecycle is four steps:
1. Create a POP
A consumer contract creates a POP by specifying:
- Resolver - Which contract will determine the answer
- Template ID - The resolver's question type (e.g., "price above threshold")
- Payload - Question-specific parameters (threshold, deadline, etc.)
- Dispute Windows - Pre-resolution and post-resolution dispute periods
- TruthKeeper - Who will adjudicate disputes (optional)
The registry validates the resolver, calls onPopCreated() to let the resolver validate the payload, and computes the accountability tier.
// Creating a POP
uint256 popId = registry.createPOP(
resolverAddress, // Which resolver to use
templateId, // Resolver's question template
payload, // ABI-encoded question parameters
disputeWindow, // Pre-resolution dispute period
postResolutionWindow, // Post-resolution dispute period
truthKeeperWindow, // Time for TK to respond
escalationWindow, // Time for escalation challenge
truthKeeper // Dispute adjudicator address
);
2. Resolution
When it's time to resolve (typically after a deadline), anyone with the appropriate permissions calls resolvePOP(). The resolver determines the answer and returns an ABI-encoded result.
The proposer posts a resolution bond - financial stake backing their answer. If their answer is later disputed and overturned, they lose this bond.
Answer Types: Results are encoded based on the answer type - BOOLEAN (true/false), NUMERIC (int256), or GENERIC (arbitrary bytes).
3. Dispute Window
After resolution, the dispute window opens. During this time, anyone can challenge the answer by posting a dispute bond and providing evidence.
Disputes trigger the two-round system:
- Round 1 - TruthKeeper (domain expert) reviews and decides
- Round 2 - If Round 1 is challenged, admin arbitration occurs
See Dispute System for full details.
4. Finalization
Once the dispute window closes without challenge (or disputes are resolved), the answer becomes final. Consumer contracts can now safely use the result.
// Reading a finalized result
ExtensiveResult memory result = registry.getExtensiveResult(popId);
require(result.isFinalized, "Not yet finalized");
if (result.answerType == AnswerType.BOOLEAN) {
bool answer = abi.decode(result.result, (bool));
// Use the answer
}
The 9-State Machine
Internally, POPs track their state through a 9-state machine:
State Transitions
Creation path:
NONE → PENDING- Resolver needs to validateNONE → ACTIVE- Auto-approved by resolverPENDING → REJECTED- Resolver rejected the payloadPENDING → ACTIVE- Resolver approved
Resolution path:
ACTIVE → RESOLVING- Answer proposed, dispute window startsRESOLVING → RESOLVED- Window closed, no disputeRESOLVING → DISPUTED_ROUND_1- Dispute filed
Dispute path:
DISPUTED_ROUND_1 → RESOLVED- TruthKeeper decided, not challengedDISPUTED_ROUND_1 → DISPUTED_ROUND_2- Decision challengedDISPUTED_ROUND_1 → ACTIVE- TOO_EARLY decision (retry later)DISPUTED_ROUND_2 → RESOLVED- Admin decidedDISPUTED_ROUND_2 → CANCELLED- Admin cancelled POP
Configurable Time Windows
Each POP has configurable time windows set at creation:
| Window | Purpose |
|---|---|
disputeWindow |
Pre-resolution dispute period (can be 0 for instant finality) |
postResolutionWindow |
Post-resolution dispute period (can be 0 to disable) |
truthKeeperWindow |
Time for TruthKeeper to decide Round 1 |
escalationWindow |
Time to challenge TruthKeeper's decision |
Bond requirements: If any dispute window is greater than 0, a resolution bond is required. Setting both dispute windows to 0 allows bond-free resolution (undisputable).
Answer Types
TOC supports three answer types, each with specific encoding:
BOOLEAN
True/false questions. Encoded as abi.encode(bool). When a boolean dispute is upheld, the answer is automatically flipped.
NUMERIC
Integer values (int256 supports negative). Encoded as abi.encode(int256). Disputes must provide a corrected value - can't auto-flip.
GENERIC
Arbitrary bytes data. No automatic encoding. The resolver defines the format. Disputes must provide corrected bytes.
// Decoding results based on type
if (result.answerType == AnswerType.BOOLEAN) {
bool answer = abi.decode(result.result, (bool));
} else if (result.answerType == AnswerType.NUMERIC) {
int256 answer = abi.decode(result.result, (int256));
} else {
bytes memory answer = result.result; // Raw bytes
}
Post-Resolution Disputes
TOC supports disputing results after finalization if a postResolutionWindow was set. This allows catching errors even after the main dispute window closes.
If a post-resolution dispute is upheld, the result is corrected - the new answer is stored alongside the original. Consumers can check hasCorrectedResult() and getCorrectedResult() to handle corrections.