// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/**
* @title OracleHub
* @notice L2 原生 Oracle 中心, 聚合多个 L1 价格源
* @dev 由 Sequencer 定期更新价格, 所有 DApp 可免费读取
*/
contract OracleHub is AccessControl, ReentrancyGuard {
bytes32 public constant PRICE_UPDATER_ROLE = keccak256("PRICE_UPDATER_ROLE");
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
// ==================== 数据结构 ====================
struct PriceData {
uint256 price; // 价格 (18 位小数)
uint256 timestamp; // 更新时间
uint256 confidence; // 置信区间 (18 位小数, ±%)
uint8 sourceCount; // 数据源数量
bytes32 merkleRoot; // 多源价格 Merkle Root (可验证)
}
struct PriceSource {
string name; // 数据源名称 (e.g., "Chainlink")
uint256 weight; // 权重 (basis points, 10000 = 100%)
bool isActive; // 是否激活
}
// ==================== 状态变量 ====================
// symbol => PriceData
mapping(string => PriceData) public prices;
// sourceId => PriceSource
mapping(uint256 => PriceSource) public priceSources;
uint256 public nextSourceId;
// 价格新鲜度阈值 (秒)
uint256 public stalenessThreshold = 60;
// 价格偏差阈值 (basis points, 500 = 5%)
uint256 public deviationThreshold = 500;
// 支持的交易对列表
string[] public supportedSymbols;
mapping(string => bool) public isSymbolSupported;
// ==================== 事件 ====================
event PriceUpdated(
string indexed symbol,
uint256 price,
uint256 timestamp,
uint256 confidence,
uint8 sourceCount
);
event PriceSourceAdded(
uint256 indexed sourceId,
string name,
uint256 weight
);
event PriceAnomalyDetected(
string indexed symbol,
uint256 newPrice,
uint256 oldPrice,
uint256 deviation
);
// ==================== 构造函数 ====================
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(GUARDIAN_ROLE, msg.sender);
// 初始化默认数据源
_addPriceSource("Chainlink", 4000); // 40%
_addPriceSource("Pyth", 3000); // 30%
_addPriceSource("Chronicle", 3000); // 30%
}
// ==================== 核心功能 ====================
/**
* @notice 更新单个交易对价格
* @param symbol 交易对符号 (e.g., "BTC/USD")
* @param price 价格 (18 位小数)
* @param confidence 置信区间
* @param sourceCount 数据源数量
* @param merkleRoot 多源价格 Merkle Root
*/
function updatePrice(
string calldata symbol,
uint256 price,
uint256 confidence,
uint8 sourceCount,
bytes32 merkleRoot
) external onlyRole(PRICE_UPDATER_ROLE) {
require(price > 0, "Invalid price");
require(isSymbolSupported[symbol], "Symbol not supported");
PriceData memory oldData = prices[symbol];
// 异常检测: 价格偏差过大
if (oldData.price > 0) {
uint256 deviation = _calculateDeviation(price, oldData.price);
if (deviation > deviationThreshold) {
emit PriceAnomalyDetected(symbol, price, oldData.price, deviation);
// 注意: 这里仍然更新价格, 但发出告警事件
// Guardian 可以在链下监控并采取行动
}
}
prices[symbol] = PriceData({
price: price,
timestamp: block.timestamp,
confidence: confidence,
sourceCount: sourceCount,
merkleRoot: merkleRoot
});
emit PriceUpdated(symbol, price, block.timestamp, confidence, sourceCount);
}
/**
* @notice 批量更新多个交易对价格 (Gas 优化)
* @param symbols 交易对符号数组
* @param pricesArray 价格数组
* @param confidences 置信区间数组
* @param sourceCounts 数据源数量数组
* @param merkleRoots Merkle Root 数组
*/
function batchUpdatePrices(
string[] calldata symbols,
uint256[] calldata pricesArray,
uint256[] calldata confidences,
uint8[] calldata sourceCounts,
bytes32[] calldata merkleRoots
) external onlyRole(PRICE_UPDATER_ROLE) {
require(
symbols.length == pricesArray.length &&
symbols.length == confidences.length &&
symbols.length == sourceCounts.length &&
symbols.length == merkleRoots.length,
"Array length mismatch"
);
for (uint256 i = 0; i < symbols.length; i++) {
// 复用单个更新逻辑
this.updatePrice(
symbols[i],
pricesArray[i],
confidences[i],
sourceCounts[i],
merkleRoots[i]
);
}
}
/**
* @notice 获取最新价格 (view 函数, 零 Gas)
* @param symbol 交易对符号
* @return price 价格
* @return timestamp 更新时间
*/
function getLatestPrice(string calldata symbol)
external
view
returns (uint256 price, uint256 timestamp)
{
PriceData memory data = prices[symbol];
require(data.timestamp > 0, "Price not available");
return (data.price, data.timestamp);
}
/**
* @notice 获取价格并检查新鲜度
* @param symbol 交易对符号
* @return price 价格
* @return timestamp 更新时间
*/
function getLatestPriceWithFreshness(string calldata symbol)
external
view
returns (uint256 price, uint256 timestamp)
{
PriceData memory data = prices[symbol];
require(data.timestamp > 0, "Price not available");
require(
block.timestamp - data.timestamp <= stalenessThreshold,
"Stale price"
);
return (data.price, data.timestamp);
}
/**
* @notice 获取完整价格数据 (包括置信区间)
* @param symbol 交易对符号
* @return 完整的 PriceData 结构体
*/
function getPriceData(string calldata symbol)
external
view
returns (PriceData memory)
{
PriceData memory data = prices[symbol];
require(data.timestamp > 0, "Price not available");
return data;
}
/**
* @notice 获取多个交易对价格 (批量查询优化)
* @param symbols 交易对符号数组
* @return pricesArray 价格数组
* @return timestamps 时间戳数组
*/
function getBatchPrices(string[] calldata symbols)
external
view
returns (uint256[] memory pricesArray, uint256[] memory timestamps)
{
pricesArray = new uint256[](symbols.length);
timestamps = new uint256[](symbols.length);
for (uint256 i = 0; i < symbols.length; i++) {
PriceData memory data = prices[symbols[i]];
pricesArray[i] = data.price;
timestamps[i] = data.timestamp;
}
return (pricesArray, timestamps);
}
// ==================== 管理功能 ====================
/**
* @notice 添加支持的交易对
*/
function addSupportedSymbol(string calldata symbol)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
require(!isSymbolSupported[symbol], "Already supported");
supportedSymbols.push(symbol);
isSymbolSupported[symbol] = true;
}
/**
* @notice 添加价格数据源
*/
function addPriceSource(string calldata name, uint256 weight)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
_addPriceSource(name, weight);
}
function _addPriceSource(string memory name, uint256 weight) private {
require(weight <= 10000, "Invalid weight");
priceSources[nextSourceId] = PriceSource({
name: name,
weight: weight,
isActive: true
});
emit PriceSourceAdded(nextSourceId, name, weight);
nextSourceId++;
}
/**
* @notice 更新新鲜度阈值
*/
function setStalenessThreshold(uint256 _threshold)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
stalenessThreshold = _threshold;
}
/**
* @notice 更新偏差阈值
*/
function setDeviationThreshold(uint256 _threshold)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
deviationThreshold = _threshold;
}
/**
* @notice 紧急暂停价格源 (Guardian 权限)
*/
function pausePriceSource(uint256 sourceId)
external
onlyRole(GUARDIAN_ROLE)
{
priceSources[sourceId].isActive = false;
}
// ==================== 内部函数 ====================
/**
* @notice 计算价格偏差 (basis points)
*/
function _calculateDeviation(uint256 newPrice, uint256 oldPrice)
private
pure
returns (uint256)
{
if (oldPrice == 0) return 0;
uint256 diff = newPrice > oldPrice
? newPrice - oldPrice
: oldPrice - newPrice;
return (diff * 10000) / oldPrice;
}
// ==================== 查询函数 ====================
/**
* @notice 获取所有支持的交易对
*/
function getSupportedSymbols() external view returns (string[] memory) {
return supportedSymbols;
}
/**
* @notice 检查价格是否新鲜
*/
function isPriceFresh(string calldata symbol) external view returns (bool) {
PriceData memory data = prices[symbol];
if (data.timestamp == 0) return false;
return block.timestamp - data.timestamp <= stalenessThreshold;
}
}