DeFi 阶段四 ✅ 验证通过
// 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
// 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;
}
}
// 从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));
}
}
| 方案 | 优点 | 缺点 |
|---|---|---|
| Chainlink | 去中心化、多数据源、广泛采用 | 依赖外部网络、可能延迟 |
| Uniswap TWAP | 纯链上、抗操纵 | 需要高流动性池、更新延迟 |
| Band Protocol | 跨链支持 | 生态较小 |
| API3 (dAPI) | 第一方数据源 | 较新、采用较少 |
| Pyth Network | 高频更新、Solana生态 | EVM集成较新 |
1. 为什么智能合约不能直接访问互联网数据?
2. 使用Chainlink时,为什么要检查数据是否过期?
3. Uniswap TWAP比即时价格更安全的原因是什么?
你已掌握预言机技术!从预言机问题到Chainlink集成,从安全检查到TWAP替代方案,你让智能合约拥有了感知链外世界的能力。
关键收获:
✅ 预言机问题与去中心化方案
✅ Chainlink Price Feed使用与安全检查
✅ Uniswap TWAP时间加权平均价格
✅ 预言机方案对比与选择
✅ 预言机攻击案例与防范