🔮 第20课:预言机

DeFi 阶段四 ✅ 验证通过

🎯 学习目标:理解预言机问题(区块链无法访问链外数据),掌握Chainlink预言机架构与使用方法,实现价格馈送合约,理解去中心化预言机的安全模型。

📖 一、预言机问题

区块链是确定性沙盒:
• EVM执行环境完全封闭,无法访问外部网络
• 合约不能发起HTTP请求、读取文件、获取系统时间
• 所有节点必须对相同输入产生相同结果

但DeFi需要外部数据:
• 借贷协议需要ETH/USD价格 → 清算
• 合成资产需要真实世界价格 → 铸造/销毁
• 保险合约需要航班状态 → 理赔
• 预测市场需要赛事结果 → 结算

预言机 = 将链外数据安全地输入链上的基础设施

1.1 预言机安全挑战

预言机的"不可能三角" 去中心化 △ ╱ ╲ ╱ ╲ ╱ ╲ ╱ 不可能 ╲ ╱ 同时满足 ╲ ╱ ╲ ╱___________________╲ 数据新鲜度 数据准确性 单一预言机问题: ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 合约请求数据│ ──►│ 单一预言机 │ ──►│ 返回价格 │ └──────────┘ └──────────┘ └──────────┘ │ ❌ 单点故障 ❌ 可被操控 ❌ 可能停机 2020年: Synthetix预言机故障 → sKRW价格异常,套利者获利超10亿美元

📖 二、Chainlink架构

Chainlink 去中心化预言机网络 (DON) ┌─────────────────────────────────────────────┐ │ 链下层 │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │节点1 │ │节点2 │ │节点3 │ │节点N │ │ │ │Binance│ │Coinbase│ │Kraken│ │... │ │ │ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ 聚合/中位数/加权平均 │ │ │ │ 去除异常值 → 生成单一价格答案 │ │ │ └──────────────────┬──────────────────┘ │ │ │ │ └─────────────────────┼───────────────────────┘ │ 链上交易 ▼ ┌─────────────────────────────────────────────┐ │ 链上层 │ │ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Price Feed │ │ 你的合约 │ │ │ │ (聚合合约) │◄───│ .getPrice() │ │ │ │ │ │ │ │ │ │ latestAnswer │ └──────────────────┘ │ │ │ latestTimestamp│ │ │ │ decimals │ │ │ └──────────────┘ │ └─────────────────────────────────────────────┘

2.1 Chainlink Price Feed

// Chainlink聚合器接口
interface AggregatorV3Interface {
    function latestRoundData() external view returns (
        uint80 roundId,        // 轮次ID
        int256 answer,         // 价格(带精度)
        uint256 startedAt,     // 本轮开始时间
        uint256 updatedAt,     // 最后更新时间
        uint80 answeredInRound // 回答所在轮次
    );
    function decimals() external view returns (uint8);
    function description() external view returns (string memory);
}

// 常用Chainlink Price Feed地址(Sepolia测试网)
// ETH/USD: 0x694AA1769357215DE4FAC081bf1f309aDC325306
// BTC/USD: 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43
// LINK/USD: 0xc59E3633BAAC79493d908e6364a2dAaFEEd3f681

📖 三、安全使用Chainlink

// contracts/OracleConsumer.sol — 安全使用Chainlink的示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

/**
 * @title OracleConsumer
 * @dev 安全消费Chainlink价格数据的合约
 */
contract OracleConsumer {
    AggregatorV3Interface internal priceFeed;
    
    // 安全参数
    uint256 public constant STALE_THRESHOLD = 1 hours;  // 数据过期阈值
    uint256 public constant MAX_DEVIATION = 20;        // 最大偏离20%
    uint256 private lastPrice;
    uint256 private lastPriceTimestamp;

    event PriceUpdated(uint256 price, uint256 timestamp);

    constructor(address _priceFeed) {
        priceFeed = AggregatorV3Interface(_priceFeed);
    }

    /// @notice 获取最新ETH/USD价格(带安全检查)
    function getLatestPrice() external returns (uint256) {
        // 1. 获取价格数据
        (
            uint80 roundId,
            int256 price,
            ,
            uint256 updatedAt,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();

        // 2. 安全检查:价格必须为正
        require(price > 0, "Invalid price");

        // 3. 安全检查:数据未过期
        require(
            block.timestamp - updatedAt < STALE_THRESHOLD,
            "Stale price data"
        );

        // 4. 安全检查:轮次匹配(防止使用旧轮次数据)
        require(
            answeredInRound >= roundId,
            "Stale round"
        );

        // 5. 安全检查:价格偏离(防止单次价格剧烈波动)
        if (lastPrice > 0) {
            uint256 deviation = _calculateDeviation(uint256(price), lastPrice);
            require(deviation <= MAX_DEVIATION, "Price deviation too high");
        }

        lastPrice = uint256(price);
        lastPriceTimestamp = block.timestamp;

        emit PriceUpdated(lastPrice, block.timestamp);
        return lastPrice;
    }

    /// @notice 获取人类可读的价格
    function getReadablePrice() external view returns (uint256) {
        (/*roundId*/, int256 price, , , ) = priceFeed.latestRoundData();
        uint8 decimals = priceFeed.decimals();
        // Chainlink ETH/USD: 8位小数 → 转为18位
        return uint256(price) * (10 ** (18 - decimals));
    }

    function _calculateDeviation(uint256 current, uint256 previous) internal pure returns (uint256) {
        if (previous == 0) return 0;
        uint256 diff = current > previous ? current - previous : previous - current;
        return (diff * 100) / previous;
    }
}

📖 四、链上价格获取替代方案

4.1 Uniswap TWAP(时间加权平均价格)

// 从Uniswap V3获取TWAP价格
interface IUniswapV3Pool {
    function slot0() external view returns (
        uint160 sqrtPriceX96,
        int24 tick,
        uint16 observationIndex,
        uint16 observationCardinality,
        uint16 observationCardinalityNext,
        uint8 feeProtocol,
        bool unlocked
    );
    function observe(uint32[] memory secondsAgos) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
}

contract TWAPOracle {
    IUniswapV3Pool public immutable pool;
    uint32 public constant TWAP_PERIOD = 1800; // 30分钟TWAP

    constructor(address _pool) {
        pool = IUniswapV3Pool(_pool);
    }

    function getTWAPPrice() external view returns (uint256) {
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = TWAP_PERIOD;
        secondsAgos[1] = 0;

        (int56[] memory tickCumulatives, ) = pool.observe(secondsAgos);
        
        // 平均tick = (当前累计tick - 过去累计tick) / 时间间隔
        int56 tickDelta = tickCumulatives[1] - tickCumulatives[0];
        int24 avgTick = int24(tickDelta / int56(int32(TWAP_PERIOD)));

        // tick → 价格: price = sqrtRatio ≈ 1.0001^tick
        // 简化:返回tick用于外部计算
        return uint256(int256(avgTick));
    }
}

4.2 预言机方案对比

方案优点缺点
Chainlink去中心化、多数据源、广泛采用依赖外部网络、可能延迟
Uniswap TWAP纯链上、抗操纵需要高流动性池、更新延迟
Band Protocol跨链支持生态较小
API3 (dAPI)第一方数据源较新、采用较少
Pyth Network高频更新、Solana生态EVM集成较新

📖 五、预言机攻击案例

2022年: Mango Markets攻击
攻击者利用低流动性的预言机操纵价格:
1. 存入少量USDC作为抵押
2. 大量买入MNGO代币推高价格
3. 利用虚高价格借出大量资产
4. 损失:1.14亿美元

教训:永远不要使用低流动性源作为价格预言机!

🧪 练习

1. 为什么智能合约不能直接访问互联网数据? 2. 使用Chainlink时,为什么要检查数据是否过期? 3. Uniswap TWAP比即时价格更安全的原因是什么?
🔮

🏆 成就解锁:数据先知

你已掌握预言机技术!从预言机问题到Chainlink集成,从安全检查到TWAP替代方案,你让智能合约拥有了感知链外世界的能力。

关键收获:

✅ 预言机问题与去中心化方案
✅ Chainlink Price Feed使用与安全检查
✅ Uniswap TWAP时间加权平均价格
✅ 预言机方案对比与选择
✅ 预言机攻击案例与防范

📋 课程目录