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:

POP Lifecycle
Create
Request data from resolver
Resolve
Answer submitted + bonded
Dispute
Challenge window open
Finalize
Result locked forever

1. Create a POP

A consumer contract creates a POP by specifying:

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:

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:

POP States
NONE Doesn't exist
PENDING Awaiting approval
REJECTED Resolver rejected
ACTIVE Ready for resolution
RESOLVING Dispute window open
DISPUTED_R1 TruthKeeper review
DISPUTED_R2 Admin escalation
RESOLVED Answer finalized
CANCELLED Voided

State Transitions

Creation path:

Resolution path:

Dispute path:

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.