Oracles

Connecting smart contracts to the outside world


Oracles

Smart contracts are deterministic — given the same inputs, they always produce the same outputs. This determinism is essential for consensus, but it creates a problem: how can contracts access real-world data?

Oracles are the bridge between on-chain and off-chain worlds. They bring external data (prices, weather, sports scores, random numbers) onto the blockchain in a way that smart contracts can consume.

The Oracle Problem

Smart contracts cannot directly:

  • Make HTTP requests
  • Read from databases
  • Access file systems
  • Generate random numbers

This is by design. If contracts could access external data arbitrarily, nodes would get different results and consensus would fail.

The oracle problem is: How do we bring external data on-chain in a decentralized, trustworthy manner?

Types of Oracles

By Data Source

Software oracles — Fetch data from web APIs (prices, weather, etc.)

Hardware oracles — Read from IoT sensors and physical devices

Human oracles — Rely on human judgment (prediction markets, dispute resolution)

Computation oracles — Perform off-chain computation and return results

By Direction

Inbound oracles — Bring external data to the blockchain (most common)

Outbound oracles — Trigger external actions based on on-chain events

By Trust Model

Centralized oracles — Single source of truth (simple but risky)

Decentralized oracles — Multiple sources aggregated (more robust)

Oracle Design Patterns

Request-Response

The contract requests data, and the oracle responds asynchronously:

// 1. Contract makes a request
function requestPrice(string memory symbol) external {
    bytes32 requestId = oracle.request(symbol);
    pendingRequests[requestId] = msg.sender;
}
 
// 2. Oracle calls back with data
function fulfillPrice(bytes32 requestId, uint256 price) external onlyOracle {
    address requester = pendingRequests[requestId];
    // Process the price data
}

Publish-Subscribe

The oracle publishes data, contracts read when needed:

interface IPriceFeed {
    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
}
 
contract MyContract {
    IPriceFeed public priceFeed;
 
    function getEthPrice() public view returns (int256) {
        (, int256 price, , , ) = priceFeed.latestRoundData();
        return price;
    }
}

Immediate-Read

For on-chain data that doesn't require external sources:

function getBlockTimestamp() public view returns (uint256) {
    return block.timestamp;
}

Chainlink is the most widely used oracle solution in the EVM ecosystem. It provides:

  • Price Feeds — Aggregated cryptocurrency and asset prices
  • VRF — Verifiable random number generation
  • Automation — Trigger contracts based on time or conditions
  • CCIP — Cross-chain messaging
  • Functions — Custom off-chain computation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
 
contract PriceConsumer {
    AggregatorV3Interface internal priceFeed;
 
    constructor() {
        // ETH/USD on Ethereum Mainnet
        priceFeed = AggregatorV3Interface(
            0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
        );
    }
 
    function getLatestPrice() public view returns (int256) {
        (
            /* uint80 roundId */,
            int256 price,
            /* uint256 startedAt */,
            uint256 updatedAt,
            /* uint80 answeredInRound */
        ) = priceFeed.latestRoundData();
 
        // Check freshness
        require(block.timestamp - updatedAt < 3600, "Stale price");
 
        return price; // 8 decimals for USD pairs
    }
}
Chainlink price feeds are available on Ethereum mainnet and many L2s. Ethereum Classic has community-maintained price feeds through initiatives like the ETC Cooperative. Always verify the contract addresses for your specific network.

For provably fair random numbers:

import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
 
contract RandomGame is VRFConsumerBaseV2 {
    // Request random number
    function requestRandomWords() external returns (uint256 requestId) {
        requestId = COORDINATOR.requestRandomWords(
            keyHash,
            s_subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
    }
 
    // Callback with random number
    function fulfillRandomWords(
        uint256 requestId,
        uint256[] memory randomWords
    ) internal override {
        uint256 randomNumber = randomWords[0];
        // Use the random number
    }
}

Other Oracle Solutions

Band Protocol

Similar to Chainlink, with a focus on cross-chain data:

interface IStdReference {
    function getReferenceData(
        string memory base,
        string memory quote
    ) external view returns (ReferenceData memory);
}

Tellor

A permissionless oracle where anyone can submit data, with staking and dispute mechanisms:

interface ITellor {
    function getDataBefore(
        bytes32 queryId,
        uint256 timestamp
    ) external view returns (bytes memory, uint256);
}

Pyth Network

High-frequency price updates with sub-second latency:

interface IPyth {
    function getPriceUnsafe(bytes32 id) external view returns (Price memory);
    function updatePriceFeeds(bytes[] calldata updateData) external payable;
}

UMA (Optimistic Oracle)

Uses an optimistic model where data is assumed correct unless disputed:

interface OptimisticOracleV2 {
    function requestPrice(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        IERC20 currency,
        uint256 reward
    ) external returns (uint256 totalBond);
}

Building Your Own Oracle

For simple use cases, you can build a basic oracle:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
contract SimpleOracle {
    address public owner;
    uint256 public price;
    uint256 public lastUpdated;
 
    constructor() {
        owner = msg.sender;
    }
 
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
 
    function updatePrice(uint256 newPrice) external onlyOwner {
        price = newPrice;
        lastUpdated = block.timestamp;
    }
 
    function getPrice() external view returns (uint256, uint256) {
        return (price, lastUpdated);
    }
}
A centralized oracle like this is a single point of failure. For anything beyond testing, use a decentralized oracle network.

Oracle Security Considerations

Data Quality

  • Stale data — Check updatedAt timestamp
  • Incorrect data — Use aggregated sources, not single providers
  • Missing data — Handle cases where oracle returns zero or reverts
function getPrice() public view returns (uint256) {
    (, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
 
    require(price > 0, "Invalid price");
    require(block.timestamp - updatedAt < MAX_DELAY, "Stale price");
 
    return uint256(price);
}

Flash Loan Attacks

Price oracles that read from on-chain DEXes can be manipulated with flash loans:

// VULNERABLE - uses spot price
function getEthPrice() public view returns (uint256) {
    (uint112 reserve0, uint112 reserve1, ) = uniswapPair.getReserves();
    return reserve0 / reserve1; // Can be manipulated!
}

Defense: Use time-weighted average prices (TWAP) or off-chain oracles:

// SAFE - uses Chainlink
function getEthPrice() public view returns (uint256) {
    (, int256 price, , , ) = chainlinkPriceFeed.latestRoundData();
    return uint256(price);
}

Centralization Risk

Even decentralized oracles have some centralization:

  • Who controls the node operators?
  • How are aggregation parameters set?
  • What happens if majority of nodes collude?

Oracles on Ethereum Classic

Ethereum Classic presents unique challenges for oracles:

  • Smaller ecosystem means fewer oracle providers
  • Lower transaction volume may affect update frequency
  • Cross-chain bridges can provide some oracle functionality
For ETC projects, consider: 1. Chainlink feeds where available 2. Custom oracle contracts with multi-sig updates 3. Cross-chain bridges from ETH oracle data 4. Community-maintained oracle cooperatives

Conclusions

Oracles are essential infrastructure for connecting smart contracts to real-world data. Key takeaways:

  • Use established oracle networks (Chainlink, etc.) for critical data
  • Always validate oracle data (freshness, bounds, sanity checks)
  • Be aware of manipulation risks, especially for on-chain price feeds
  • Consider the trust model — who can update the data?
  • Have fallback mechanisms for oracle failures