Quick Start

Integrate TOC into your smart contracts in three steps: create a POP, wait for resolution, read the result.

Installation

Install the TOC contracts package for your development environment:

# Install via forge
forge install toc-protocol/toc-core

# Add remapping to foundry.toml
@toc/=lib/toc-core/src/

# Import in your contract
import {IPOPRegistry} from "@toc/interfaces/IPOPRegistry.sol";
# Install via npm
npm install @toc-protocol/contracts

# Or using yarn
yarn add @toc-protocol/contracts

# Import in your contract
import "@toc-protocol/contracts/interfaces/IPOPRegistry.sol";
// In Remix IDE:
// 1. Go to Plugin Manager
// 2. Search for "DGIT"
// 3. Clone: github.com/toc-protocol/toc-core

// Or import directly:
import "https://github.com/toc-protocol/toc-core/blob/main/src/interfaces/IPOPRegistry.sol";

Basic Integration

1. Import the Interface

import {IPOPRegistry} from "@toc/interfaces/IPOPRegistry.sol";
import {POPTypes} from "@toc/POPTypes.sol";

contract YourProtocol {
    IPOPRegistry public registry;

    constructor(address _registry) {
        registry = IPOPRegistry(_registry);
    }
}

2. Create a POP

function createPrediction(
    address resolver,
    uint32 templateId,
    bytes calldata payload
) external returns (uint256) {
    uint256 popId = registry.createPOP(
        resolver,           // Resolver contract address
        templateId,         // Template ID from resolver
        payload,            // ABI-encoded question params
        1 hours,           // Pre-resolution dispute window
        0,                  // Post-resolution window (0 = none)
        24 hours,          // TruthKeeper decision window
        12 hours,          // Escalation challenge window
        truthKeeperAddress  // TruthKeeper for disputes
    );

    return popId;
}

3. Read Results

function settlePosition(uint256 popId) external {
    // Get result with full context
    ExtensiveResult memory result = registry.getExtensiveResult(popId);

    // Verify it's finalized
    require(result.isFinalized, "Not yet finalized");

    // Decode based on answer type
    if (result.answerType == AnswerType.BOOLEAN) {
        bool answer = abi.decode(result.result, (bool));
        _settleBooleanOutcome(answer);

    } else if (result.answerType == AnswerType.NUMERIC) {
        int256 answer = abi.decode(result.result, (int256));
        _settleNumericOutcome(answer);

    } else {
        // GENERIC - raw bytes
        _settleGenericOutcome(result.result);
    }
}

The ExtensiveResult Struct

The getExtensiveResult() function returns comprehensive information:

struct ExtensiveResult {
    AnswerType answerType;      // BOOLEAN, NUMERIC, or GENERIC
    bytes result;               // ABI-encoded answer
    bool isFinalized;           // Safe to use?
    bool wasDisputed;           // Was there a dispute?
    bool wasCorrected;          // Was result changed after dispute?
    AccountabilityTier tier;    // SYSTEM, TK_GUARANTEED, PERMISSIONLESS
    ResolverTrust resolverTrust;// Resolver's trust level
}

Common Patterns

Enforce Minimum Tier

function settleHighValue(uint256 popId) external {
    ExtensiveResult memory result = registry.getExtensiveResult(popId);

    // Require SYSTEM tier for high-value settlements
    require(
        result.tier == AccountabilityTier.SYSTEM,
        "High-value requires SYSTEM tier"
    );

    require(result.isFinalized, "Not finalized");
    // ... settle
}

Handle Corrections

function getLatestResult(uint256 popId) public view returns (bytes memory) {
    // Check if result was corrected after post-resolution dispute
    if (registry.hasCorrectedResult(popId)) {
        return registry.getCorrectedResult(popId);
    }
    return registry.getResult(popId);
}

Wait for Finalization

function canSettle(uint256 popId) public view returns (bool) {
    // Check if fully finalized (all dispute windows closed)
    return registry.isFullyFinalized(popId);
}

function isContested(uint256 popId) public view returns (bool) {
    // Check if there's an active dispute
    return registry.isContested(popId);
}

Working with Resolvers

Pyth Price Resolver Example

For price threshold questions using Pyth oracle:

// Template 0: Is price above threshold at deadline?
bytes memory payload = abi.encode(
    pythPriceFeedId,  // bytes32 - Pyth price feed ID
    threshold,        // int64 - price threshold
    deadline          // uint64 - when to check
);

uint256 popId = registry.createPOP(
    pythResolverAddress,
    0,  // Template 0 = price above threshold
    payload,
    // ... time windows
);

Optimistic Resolver Example

For subjective outcomes requiring human judgment:

// Template 0: Arbitrary YES/NO question
bytes memory payload = abi.encode(
    "Will ETH flip BTC by end of 2025?",  // question
    resolutionDeadline                      // when to resolve
);

uint256 popId = registry.createPOP(
    optimisticResolverAddress,
    0,  // Template 0 = arbitrary question
    payload,
    // ... time windows
);

Gas Optimization Tips

Simple vs Extensive: Use getResult(popId) for gas-efficient raw result access. Use getExtensiveResult(popId) when you need the full context (tier, dispute status, etc.).

Error Handling

Common error scenarios to handle:

function safeSettle(uint256 popId) external {
    // Check POP exists and is in valid state
    POPInfo memory info = registry.getPopInfo(popId);
    require(info.state != POPState.NONE, "POP doesn't exist");
    require(info.state != POPState.CANCELLED, "POP was cancelled");
    require(info.state == POPState.RESOLVED, "Not resolved yet");

    // Check fully finalized (all windows closed)
    require(registry.isFullyFinalized(popId), "Dispute window open");

    // Safe to use result
    ExtensiveResult memory result = registry.getExtensiveResult(popId);
    _settle(result);
}