Resolvers
Resolvers are pluggable smart contracts that determine truth for specific question types. They implement a standard interface, allowing any data source - oracles, human judgment, on-chain state, or custom logic.
How Resolvers Work
Resolvers are "black boxes" from the registry's perspective. The registry doesn't care how truth is determined - only that the resolver follows the standard interface:
interface IPopResolver {
// Called when POP is created - validate and optionally approve
function onPopCreated(
uint256 popId,
uint32 templateId,
bytes calldata payload
) external returns (POPState initialState);
// Called to resolve - return ABI-encoded result
function resolvePop(
uint256 popId,
address caller,
bytes calldata payload
) external returns (bytes memory result);
// Metadata functions
function getTemplateAnswerType(uint32 templateId)
external view returns (AnswerType);
function getPopQuestion(uint256 popId)
external view returns (string memory);
}
Trust Levels
Resolvers have a trust classification that affects accountability tiers:
| Trust Level | Description | How to Get |
|---|---|---|
SYSTEM |
Official ecosystem resolver | Admin grants |
VERIFIED |
Admin reviewed and approved | Admin grants |
PERMISSIONLESS |
Default for registered resolvers | Anyone registers |
NONE |
Not registered | - |
Registering a Resolver
Registration is permissionless - anyone can register a resolver contract:
// Anyone can register a resolver
registry.registerResolver(myResolverAddress);
// Resolver starts at PERMISSIONLESS trust level
// Admin can upgrade trust level
registry.setResolverTrust(myResolverAddress, ResolverTrust.VERIFIED);
// Or grant SYSTEM trust
registry.setResolverTrust(myResolverAddress, ResolverTrust.SYSTEM);
Key insight: Permissionless registration enables open innovation. Anyone can build and register resolvers. Trust is managed separately - start at PERMISSIONLESS, earn upgrades through track record.
Built-in Resolvers
Pyth Price Resolver
Oracle-based resolution using Pyth Network price feeds. Best for objective price data.
Templates:
- Template 0 (Snapshot) - Is price above/below threshold at deadline?
- Template 1 (Range) - Is price within range at deadline?
- Template 2 (Reached By) - Did price reach target by deadline?
Answer Type: BOOLEAN
// Template 0: Price above threshold
bytes memory payload = abi.encode(
pythPriceFeedId, // bytes32 - e.g., ETH/USD feed
100000_00000000, // int64 - $100,000 (8 decimals)
uint64(block.timestamp + 7 days) // deadline
);
uint256 popId = registry.createPOP(
pythResolver,
0, // Template 0
payload,
// ... windows
);
Optimistic Resolver
Human judgment for subjective outcomes. Anyone can propose answers; disputes go through the two-round system.
Templates:
- Template 0 (Arbitrary) - Free-form YES/NO question
- Template 1 (Sports) - Structured sports outcomes
- Template 2 (Event) - Did specific event occur?
Answer Type: BOOLEAN
// Template 0: Arbitrary question
bytes memory payload = abi.encode(
"Will the US pass a crypto regulatory framework in 2025?",
uint64(block.timestamp + 365 days) // resolution deadline
);
uint256 popId = registry.createPOP(
optimisticResolver,
0, // Template 0
payload,
// ... windows
);
Building Custom Resolvers
Implement the IPopResolver interface to create custom resolution logic:
contract MyCustomResolver is IPopResolver {
// Store POP-specific data
mapping(uint256 => MyPopData) private _popData;
function onPopCreated(
uint256 popId,
uint32 templateId,
bytes calldata payload
) external override returns (POPState) {
// Decode and validate payload
(param1, param2) = abi.decode(payload, (Type1, Type2));
require(isValidParams(param1, param2), "Invalid");
// Store for later resolution
_popData[popId] = MyPopData(param1, param2);
// Return ACTIVE to auto-approve, PENDING if manual approval needed
return POPState.ACTIVE;
}
function resolvePop(
uint256 popId,
address caller,
bytes calldata payload
) external override returns (bytes memory) {
MyPopData storage data = _popData[popId];
// Your custom resolution logic
bool result = _determineOutcome(data, payload);
// Return ABI-encoded result
return abi.encode(result);
}
function getTemplateAnswerType(uint32)
external pure override returns (AnswerType) {
return AnswerType.BOOLEAN;
}
}
Resolver Design Guidelines
- Validate thoroughly in
onPopCreated()- reject invalid payloads early - Store minimal data - only what's needed for resolution
- Return consistent types - ABI-encode results matching your answer type
- Handle edge cases - what if the data source is unavailable?
- Document templates - clear payload format and answer semantics
Resolver Use Cases
| Use Case | Data Source | Answer Type |
|---|---|---|
| Price predictions | Pyth, Chainlink | BOOLEAN |
| Sports outcomes | Optimistic (human) | BOOLEAN |
| Election results | Optimistic (human) | BOOLEAN/GENERIC |
| Weather data | Custom oracle | NUMERIC |
| On-chain state | Direct read | BOOLEAN/NUMERIC |
| Random numbers | VRF, commit-reveal | GENERIC |
Need a custom resolver? Check the examples repository for resolver templates, or reach out on Discord for guidance.