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.).
- Cache the registry address in an immutable variable
- Use
getResult()for simple read-only queries - Use
getExtensiveResultStrict()if you want to revert on non-finalized results - Batch POP reads when possible
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);
}