gcx/blockchain/genex-contracts/src/ExchangeRateOracle.sol

101 lines
3.8 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "./interfaces/IChainlinkPriceFeed.sol";
/// @title ExchangeRateOracle — 汇率预言机集成
/// @notice 集成 Chainlink Price Feed提供各币种对 USD 汇率
/// @dev 支持多币种内置过期保护最多15分钟
contract ExchangeRateOracle is Initializable, AccessControlUpgradeable {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
// 币种 → Chainlink Price Feed 地址
mapping(string => address) public priceFeeds;
// 支持的币种列表
string[] public supportedCurrencies;
// 汇率数据最大容许延迟(秒)
uint256 public maxStaleness;
// --- Events ---
event PriceFeedSet(string indexed currency, address feed);
event PriceFeedRemoved(string indexed currency);
event MaxStalenessUpdated(uint256 oldValue, uint256 newValue);
function initialize(uint256 _maxStaleness, address admin) external initializer {
__AccessControl_init();
maxStaleness = _maxStaleness > 0 ? _maxStaleness : 900; // 默认 15 分钟
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(ADMIN_ROLE, admin);
}
/// @notice 获取某币种对 USD 的汇率
/// @param currency 币种标识(如 "CNY", "JPY", "EUR"
/// @return rate 汇率Chainlink 精度,通常 8 位小数)
function getRate(string calldata currency) external view returns (uint256 rate) {
address feed = priceFeeds[currency];
require(feed != address(0), "Oracle: unsupported currency");
(, int256 answer,, uint256 updatedAt,) = IChainlinkPriceFeed(feed).latestRoundData();
require(answer > 0, "Oracle: invalid price");
require(block.timestamp - updatedAt <= maxStaleness, "Oracle: stale price data");
rate = uint256(answer);
}
/// @notice 获取汇率及详细信息
function getRateDetails(string calldata currency)
external
view
returns (uint256 rate, uint256 updatedAt, uint8 decimals)
{
address feed = priceFeeds[currency];
require(feed != address(0), "Oracle: unsupported currency");
(, int256 answer,, uint256 _updatedAt,) = IChainlinkPriceFeed(feed).latestRoundData();
require(answer > 0, "Oracle: invalid price");
rate = uint256(answer);
updatedAt = _updatedAt;
decimals = IChainlinkPriceFeed(feed).decimals();
}
/// @notice 设置币种的 Price Feed 地址
function setPriceFeed(string calldata currency, address feed) external onlyRole(ADMIN_ROLE) {
require(feed != address(0), "Oracle: zero address");
require(bytes(currency).length > 0, "Oracle: empty currency");
// 新增还是更新
if (priceFeeds[currency] == address(0)) {
supportedCurrencies.push(currency);
}
priceFeeds[currency] = feed;
emit PriceFeedSet(currency, feed);
}
/// @notice 移除币种 Price Feed
function removePriceFeed(string calldata currency) external onlyRole(ADMIN_ROLE) {
require(priceFeeds[currency] != address(0), "Oracle: feed not found");
priceFeeds[currency] = address(0);
emit PriceFeedRemoved(currency);
}
/// @notice 更新最大过期时间
function setMaxStaleness(uint256 _maxStaleness) external onlyRole(ADMIN_ROLE) {
require(_maxStaleness > 0, "Oracle: zero staleness");
uint256 old = maxStaleness;
maxStaleness = _maxStaleness;
emit MaxStalenessUpdated(old, _maxStaleness);
}
/// @notice 获取支持的币种数量
function supportedCurrencyCount() external view returns (uint256) {
return supportedCurrencies.length;
}
}