This commit is contained in:
2025-12-25 13:29:35 +08:00
parent e21ee7a5df
commit cbe16b7f40
25 changed files with 43 additions and 740 deletions

View File

@@ -6,11 +6,6 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "../../interfaces/IYTAssetVault.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
/**
* @title YTPriceFeed
* @notice 价格读取器直接从YT合约读取价格变量带保护机制和价差
* @dev UUPS可升级合约
*/
contract YTPriceFeed is Initializable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
@@ -33,19 +28,14 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
address public usdcAddress;
// 价格保护参数
uint256 public maxPriceChangeBps; // 5% 最大价格变动
/// @notice USDC价格Feed
AggregatorV3Interface internal usdcPriceFeed;
// 价差配置(每个代币可以有不同的价差)
mapping(address => uint256) public spreadBasisPoints;
// 价格历史记录
mapping(address => uint256) public lastPrice;
// 价格更新权限
mapping(address => bool) public isKeeper;
event PriceUpdate(address indexed token, uint256 oldPrice, uint256 newPrice, uint256 timestamp);
@@ -62,9 +52,6 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
_;
}
/**
* @notice 初始化合约
*/
function initialize(address _usdcAddress, address _usdcPriceFeed) external initializer {
__UUPSUpgradeable_init();
if (_usdcAddress == address(0)) revert InvalidAddress();
@@ -74,64 +61,33 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
maxPriceChangeBps = 500; // 5% 最大价格变动
}
/**
* @notice 设置USDC地址
* @param _usdcAddress USDC地址
*/
function setUSDCAddress(address _usdcAddress) external onlyGov {
if (_usdcAddress == address(0)) revert InvalidAddress();
usdcAddress = _usdcAddress;
}
/**
* @notice 设置USDC价格Feed
* @param _usdcPriceFeed USDC价格Feed地址
*/
function setUSDCPriceFeed(address _usdcPriceFeed) external onlyGov {
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
}
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
/**
* @notice 设置keeper权限
* @param _keeper keeper地址
* @param _isActive 是否激活
*/
function setKeeper(address _keeper, bool _isActive) external onlyGov {
isKeeper[_keeper] = _isActive;
emit KeeperSet(_keeper, _isActive);
}
/**
* @notice 设置最大价格变动百分比
* @param _maxPriceChangeBps 最大变动(基点)
*/
function setMaxPriceChangeBps(uint256 _maxPriceChangeBps) external onlyGov {
if (_maxPriceChangeBps > 2000) revert MaxChangeTooHigh(); // 最大20%
maxPriceChangeBps = _maxPriceChangeBps;
}
/**
* @notice 设置代币价差
* @param _token 代币地址
* @param _spreadBasisPoints 价差基点例如10 = 0.1%, 100 = 1%
*/
function setSpreadBasisPoints(address _token, uint256 _spreadBasisPoints) external onlyGov {
if (_spreadBasisPoints > MAX_SPREAD_BASIS_POINTS) revert SpreadTooHigh();
spreadBasisPoints[_token] = _spreadBasisPoints;
emit SpreadUpdate(_token, _spreadBasisPoints);
}
/**
* @notice 批量设置代币价差
* @param _tokens 代币地址数组
* @param _spreadBasisPoints 价差数组
*/
function setSpreadBasisPointsForMultiple(
address[] calldata _tokens,
uint256[] calldata _spreadBasisPoints
@@ -144,11 +100,6 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
}
}
/**
* @notice 更新并缓存代币价格keeper调用
* @param _token 代币地址
* @return 更新后的价格
*/
function updatePrice(address _token) external onlyKeeper returns (uint256) {
if (_token == usdcAddress) {
return _getUSDCPrice();
@@ -157,10 +108,8 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
uint256 oldPrice = lastPrice[_token];
uint256 newPrice = _getRawPrice(_token);
// 价格波动检查
_validatePriceChange(_token, newPrice);
// 更新缓存价格
lastPrice[_token] = newPrice;
emit PriceUpdate(_token, oldPrice, newPrice, block.timestamp);
@@ -168,31 +117,13 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
return newPrice;
}
/**
* @notice 强制更新价格(紧急情况)
* @param _token 代币地址
* @param _price 新价格
*/
function forceUpdatePrice(address _token, uint256 _price) external onlyGov {
uint256 oldPrice = lastPrice[_token];
lastPrice[_token] = _price;
emit PriceUpdate(_token, oldPrice, _price, block.timestamp);
}
/**
* @notice 获取YT代币价格带波动保护和价差
* @param _token 代币地址
* @param _maximise true=最大价格(上浮价差,对协议有利), false=最小价格(下压价差,对协议有利)
* @return 价格30位精度
*
* 使用场景:
* - 添加流动性时AUM计算_maximise=true高估AUM用户获得较少LP
* - 移除流动性时AUM计算_maximise=false低估AUM用户获得较少代币
* - buyUSDY时用户卖代币_maximise=false低估用户代币价值
* - sellUSDY时用户买代币_maximise=true高估需支付的代币价值
* - swap时tokenIn_maximise=false低估输入
* - swap时tokenOut_maximise=true高估输出
*/
function getPrice(address _token, bool _maximise) external view returns (uint256) {
if (_token == usdcAddress) {
return _getUSDCPrice();
@@ -200,24 +131,15 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
uint256 basePrice = _getRawPrice(_token);
// 价格波动检查
_validatePriceChange(_token, basePrice);
// 应用价差
return _applySpread(_token, basePrice, _maximise);
}
/**
* @notice 直接读取YT代币的ytPrice变量
*/
function _getRawPrice(address _token) private view returns (uint256) {
return IYTAssetVault(_token).ytPrice();
}
/**
* @notice 获取并验证USDC价格从Chainlink
* @return 返回uint256格式的USDC价格精度为1e30
*/
function _getUSDCPrice() internal view returns (uint256) {
(
/* uint80 roundId */,
@@ -232,13 +154,6 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
return uint256(price) * 1e22; // 1e22 = 10^(30-8)
}
/**
* @notice 应用价差
* @param _token 代币地址
* @param _basePrice 基础价格
* @param _maximise true=上浮价格false=下压价格
* @return 应用价差后的价格
*/
function _applySpread(
address _token,
uint256 _basePrice,
@@ -246,41 +161,30 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
) private view returns (uint256) {
uint256 spread = spreadBasisPoints[_token];
// 如果没有设置价差,直接返回基础价格
if (spread == 0) {
return _basePrice;
}
if (_maximise) {
// 上浮价格basePrice * (1 + spread%)
return _basePrice * (BASIS_POINTS_DIVISOR + spread) / BASIS_POINTS_DIVISOR;
} else {
// 下压价格basePrice * (1 - spread%)
return _basePrice * (BASIS_POINTS_DIVISOR - spread) / BASIS_POINTS_DIVISOR;
}
}
/**
* @notice 验证价格变动是否在允许范围内
*/
function _validatePriceChange(address _token, uint256 _newPrice) private view {
uint256 oldPrice = lastPrice[_token];
// 首次设置价格,跳过检查
if (oldPrice == 0) {
return;
}
// 计算价格变动百分比
uint256 priceDiff = _newPrice > oldPrice ? _newPrice - oldPrice : oldPrice - _newPrice;
uint256 maxDiff = oldPrice * maxPriceChangeBps / BASIS_POINTS_DIVISOR;
if (priceDiff > maxDiff) revert PriceChangeTooLarge();
}
/**
* @notice 获取价格详细信息
*/
function getPriceInfo(address _token) external view returns (
uint256 currentPrice,
uint256 cachedPrice,
@@ -303,13 +207,9 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
minPrice = _applySpread(_token, currentPrice, false);
}
}
/**
* @notice 获取最大价格(上浮价差)
*/
function getMaxPrice(address _token) external view returns (uint256) {
if (_token == usdcAddress) {
// USDC通常不需要价差直接返回原价格
return _getUSDCPrice();
}
uint256 basePrice = _getRawPrice(_token);
@@ -317,12 +217,8 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
return _applySpread(_token, basePrice, true);
}
/**
* @notice 获取最小价格(下压价差)
*/
function getMinPrice(address _token) external view returns (uint256) {
if (_token == usdcAddress) {
// USDC通常不需要价差直接返回原价格
return _getUSDCPrice();
}
uint256 basePrice = _getRawPrice(_token);
@@ -330,9 +226,5 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
return _applySpread(_token, basePrice, false);
}
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap;
}
}