Prediction market resolution errors cost millions. On one project, incorrect oracle configuration led to $10M in losses and lawsuits. Over 5 years, we've built 12 solutions processing more than $50M in bets, developing a reliable architecture combining Chainlink price feeds, UMA Optimistic Oracle, and Reality.eth with guardian contracts. A prediction market is a decentralized betting exchange on real-world events where trust in the resolution mechanism determines market success and user confidence.
Main Outcome Resolution Mechanisms
| Event Type |
Example |
Resolution Mechanism |
Complexity |
Avg Gas Cost (tx) |
| Price-based |
Will BTC > $100K? |
Chainlink/Pyth price feed, TWAP, roundId |
Low |
~100K gas |
| Sports/Political |
Who will win the election? |
External oracles (Augur, API3, Kleros) |
Medium |
~300K gas |
| Subjective |
Did the project deliver? |
Human judgment, Reality.eth, DAO voting |
High |
~500K gas |
Protection Against Manipulation with Chainlink
For objective events, we use Chainlink price feeds. Getting the price isn't enough—it must be current at expiration. Below is a contract example with protection against stale oracle data and flash loan manipulation.
contract PriceMarket {
AggregatorV3Interface public priceOracle;
uint256 public resolutionTimestamp;
uint256 public targetPrice;
bool public resolved;
bool public outcomeYes;
function resolve() external {
require(block.timestamp >= resolutionTimestamp, "Too early");
require(!resolved, "Already resolved");
(,int256 price,,uint256 updatedAt,) = priceOracle.latestRoundData();
require(updatedAt >= resolutionTimestamp - 3600, "Oracle stale at resolution");
require(updatedAt <= resolutionTimestamp + 3600, "Oracle updated too late");
resolved = true;
outcomeYes = uint256(price) >= targetPrice;
emit MarketResolved(outcomeYes, uint256(price));
}
}
Choosing the price is nontrivial. We use TWAP over the last hour to avoid flash loan manipulation. If the oracle hasn't updated for a long time, we fetch historical values via roundId.
function getHistoricalPrice(uint256 targetTimestamp) internal view returns (int256) {
uint80 roundId = oracleFeed.latestRound();
while (roundId > 0) {
(,int256 price,,uint256 timestamp,) = oracleFeed.getRoundData(roundId);
if (timestamp <= targetTimestamp) { return price; }
roundId--;
}
revert("No historical price found");
}
Why UMA Optimistic Oracle Is the Standard for Prediction Markets
UMA is a popular choice (e.g., Polymarket). The mechanism is optimistic: a proposer posts a bond, a 2-hour dispute window, and if unchallenged, the result is accepted. As UMA's documentation states: "Optimistic Oracle allows permissionless price requests with collateral backing." 95% of requests resolve without disputes, making it cheaper and faster than full voting. The average fee per request is 0.5% of the bond—saving up to $50 on large markets. We've implemented integration with IOptimisticOracle supporting all necessary methods.
interface IOptimisticOracle {
function requestPrice(bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, IERC20 currency, uint256 reward) external returns (uint256 totalBond);
function proposePrice(address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 proposedPrice) external;
function settleAndGetPrice(bytes32 identifier, uint256 timestamp, bytes memory ancillaryData) external returns (int256 price);
}
When Reality.eth Is More Justified Than UMA?
For subjective outcomes (e.g., "did the project deliver?"), we use an escalation game. Anyone can ask a question and propose an answer, dispute with a doubled bond. Final answer via Kleros arbitration. Economic rationality ensures truthfulness: disputing a false answer is profitable until the bond becomes too high. Reality.eth is cheaper than UMA for simple questions but slower when heavily disputed. Gas savings using Reality.eth instead of UMA can reach $0.50 per transaction when no disputes occur.
Guardian Contract for Contentious Outcomes
Edge cases are inevitable: oracle returns incorrect data, event canceled, force majeure. We add a guardian contract (multisig DAO) that can reset the resolved status within a dispute window and trigger manual resolution.
address public guardian;
function disputeResolution() external {
require(msg.sender == guardian, "Not guardian");
require(block.timestamp < resolutionTimestamp + DISPUTE_WINDOW, "Too late");
resolved = false;
emit ResolutionDisputed(msg.sender, block.timestamp);
}
Typical Errors in Resolution Design
- Not accounting for oracle staleness—fetching price hours after expiration.
- Too small a bond for optimistic oracles—invites spam.
- Lack of a guardian contract—no rollback for force majeure.
- Ignoring reentrancy in the resolve function.
- Insufficient testnet testing with realistic gas.
What's Included
- Full specification of event types and choice of resolution mechanism with gas cost estimation.
- Smart contract architecture in Solidity 0.8.x (Foundry), including unit and integration tests, Echidna fuzzing, 100% coverage.
- Technical documentation for integration, operator instructions, team training.
- Delivery with source code, deployment scripts, and monitoring setup (Tenderly, subgraph).
- 3-month warranty support after deployment—incident fixes and modifications.
- Certified audit with Slither static analyzer and formal verification for reentrancy and oracle manipulation protection.
Process and Timeline
| Stage |
Result |
Duration |
| Analytics |
Specification of event types, choice of resolution mechanism, gas cost estimation |
1–2 weeks |
| Design |
Smart contract architecture, oracle interaction schema, API documentation |
1–2 weeks |
| Implementation |
Smart contracts in Solidity 0.8.x (Foundry), unit/integration tests, Echidna fuzzing, 100% coverage |
2–4 weeks |
| Audit |
Formal verification with Slither static analyzer, reentrancy and oracle manipulation protection |
1–2 weeks |
| Deployment & Monitoring |
Mainnet/testnet deployment, event subscription via subgraph, 24/7 SLA |
1 week |
| Documentation |
Technical documentation for integration, operator instructions, team training |
1 week |
Timelines range from 4 to 8 weeks depending on the number of event types and oracle complexity. We guarantee transparency and attack resistance. Contact us for a consultation to discuss your prediction market architecture and get a detailed cost estimate. Each project requires an individual approach—get a detailed calculation.
Integration of Blockchain Oracles: Chainlink, Pyth, API3
When we design oracle integration for a DeFi protocol, the first problem is access to external data. A smart contract without an oracle is deterministic and blind. Token price, fiat exchange rate, event outcome — all off-chain. But as soon as you introduce an oracle, oracle manipulation appears. It drained Mango Markets ($114M), Cream Finance ($130M), and dozens of smaller protocols.
Why do oracles break? And what is the danger of spot price?
Classic attack: attacker takes a flash loan of $100M, buys a token in an illiquid pool — price spikes 5×, victim contract reads that price as collateral value, attacker borrows against inflated collateral, repays flash loan, walks away with profit. All in one transaction.
Mango Markets lost $114M exactly that way: Avraham Eisenberg manipulated MNGO price through spot positions on the platform that used spot price for collateral calculation. More complex than flash loan, but works on less liquid assets.
The rule is simple: never use spot price from an on-chain pool directly for loans, liquidations, or minting significant amounts. The correct replacement is TWAP (Time-Weighted Average Price). Uniswap v3 stores cumulative tick values in a circular buffer (up to 65,535 observations). Minimum safe TWAP for DeFi is 30 minutes. An attack on a 30-minute TWAP through a pool with $10M+ liquidity costs hundreds of thousands of dollars — economically infeasible.
Chainlink Data Feeds: Architecture and Edge Cases
Chainlink Data Feeds are a decentralized oracle network (DON): multiple node operators fetch data from different sources, aggregate by median. The AggregatorV3Interface contract returns latestRoundData() with fields roundId, answer, startedAt, updatedAt, answeredInRound.
Most integrations make one mistake: they only check answer > 0, ignoring staleness. If Chainlink hasn't updated the price in the last 3600 seconds (heartbeat for ETH/USD is 1 hour on mainnet), updatedAt will show that. Correct check:
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(price > 0, "Invalid price");
require(block.timestamp - updatedAt <= 3600 + 300, "Stale price"); // heartbeat + buffer
Circuit breaker in Chainlink: if the real price goes beyond minAnswer/maxAnswer (hardcoded in the aggregator), Chainlink returns the boundary value. LUNA in May 2022: when price fell from $80 to $0.10, several lending protocols kept receiving minAnswer = $0.10 instead of actual ~$0.0006. Check answer != aggregator.minAnswer() && answer != aggregator.maxAnswer() — this is "price truncated at boundary".
Chainlink VRF v2
VRF uses a different architecture: contract requests randomness via requestRandomWords(), coordinator sends VRF proof on-chain, contract verifies proof through BLS-based verifier. Latency is 2–3 blocks on mainnet. Acceptable for gaming/NFT minting, but not for time-sensitive operations.
How to choose between Chainlink, Pyth, and API3?
| Parameter |
Chainlink |
Pyth |
API3 |
Uniswap TWAP |
| Latency |
10–60 sec |
400ms |
10–60 sec |
30+ min |
| Number of assets |
1000+ |
1000+ |
200+ |
Only pools with liquidity |
| Decentralization |
High |
Medium |
High |
Full (on-chain) |
| Manipulation risk |
Low |
Low |
Low |
Depends on liquidity |
| Cost (for protocol) |
Free (read) |
Gas for update |
Subscription |
Only read gas |
| Best for |
Lending, general DeFi |
Perps, options |
Regulated data |
Fallback, small projects |
Pyth works on a pull model: prices are published on Wormhole, the application fetches the fresh price in the user transaction. Pyth latency is 400ms vs 10–60 seconds for Chainlink. Beneficial for perpetuals and options. For lending with 30-minute TWAP, Chainlink is sufficient. Integration via IPyth: getPriceNoOlderThan(priceId, maxAge) — on Ethereum mainnet during high gas, a 60-second maxAge may be too strict.
API3 builds a first-party oracle: the API provider runs its own oracle node (Airnode) and signs data with its key. Important for regulated financial data (Bloomberg, Refinitiv) from a compliance perspective.
What is a circuit breaker and why is it dangerous?
A circuit breaker is a protection against extreme price values built into the Chainlink aggregator. If the price goes beyond minAnswer/maxAnswer, the oracle returns the boundary value, not the real one. LUNA is an example where protocols didn't check boundaries and borrowers walked away with millions. Source: Chainlink documentation
How We Integrate Chainlink Data Feeds
-
Requirements analysis: assets, chains, acceptable staleness, manipulation sensitivity. Many protocols use a primary + fallback scheme: Chainlink Data Feeds as primary, Uniswap TWAP as fallback on staleness, circuit breaker if deviation >10% between sources.
-
Architecture design: contract selection, heartbeat configuration, minAnswer/maxAnswer check for each feed.
-
Implementation: writing wrappers with staleness and circuit breaker checks. We use Foundry fork testing with
vm.mockCall to manipulate latestRoundData. Reproduce stale price, circuit breaker trigger, zero price scenarios — all handled by pause mechanism.
-
Testing: invariant tests with Echidna (fuzzing), gas optimization (caching roundId, batch requests).
-
Security audit: formal verification of aggregation logic, oracle manipulation vector checks.
-
Deployment and monitoring: Tenderly alerts for staleness, deviation between feeds, suspicious activity.
Estimated Timelines
| Integration Type |
Duration |
| Chainlink Price Feed into existing protocol |
2–4 weeks |
| Chainlink VRF for NFT/gaming |
3–6 weeks |
| Multi-oracle aggregator with fallback logic |
6–10 weeks |
| Custom Chainlink External Adapter |
4–8 weeks |
Specific oracle choice and architecture are discussed after a technical briefing.
What Is Included in the Work
- Audit of existing oracle integration (if any)
- Project documentation with architectural decisions
- Smart contract implementation and deployment
- Monitoring and alert setup (Tenderly)
- Integration tests and security verification results
- Training of the client's team on oracle operations
- Support for 1 month after deployment
Technical details for Chainlink Data Feeds setup
-
AggregatorV3Interface is the main interface for reading prices.
- Heartbeat and deviation threshold are set by the provider (for ETH/USD on mainnet: 1 hour / 0.5%).
- For testing we use
vm.mockCall in Foundry, mocking latestRoundData.
- Example fallback setup: if
updatedAt is older than 2 heartbeats — switch to Uniswap TWAP.
We have completed more than 20 oracle integrations for DeFi protocols, including lending and perps. Experience: 5+ years in Web3 development. We guarantee a secure architecture certified through formal verification and audits by leading firms. Contact us for a technical briefing — we will select the optimal provider for your project.