fix contract

This commit is contained in:
2026-01-12 14:33:16 +08:00
parent a18b9a42e4
commit d56f83726b
70 changed files with 1988 additions and 142 deletions

View File

@@ -20,5 +20,7 @@ interface IYTPoolManager {
) external returns (uint256);
function getPrice(bool _maximise) external view returns (uint256);
function getAumInUsdy(bool _maximise) external view returns (uint256);
function onLPTransfer(address _from, address _to) external;
}

View File

@@ -543,8 +543,9 @@ contract Lending is
function getBalance(address account) external view override returns (int256) {
int104 principal = userBasic[account].principal;
// 使用 supplyIndex 计算实际余额(含利息)
return LendingMath.principalToBalance(principal, supplyIndex);
// 根据余额正负使用对应的索引正余额用supplyIndex负余额用borrowIndex
uint256 index = principal >= 0 ? supplyIndex : borrowIndex;
return LendingMath.principalToBalance(principal, index);
}
function supplyBalanceOf(address account) external view override returns (uint256) {

View File

@@ -9,10 +9,14 @@ import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interf
contract LendingPriceFeed is OwnableUpgradeable, UUPSUpgradeable {
address public usdcAddress;
AggregatorV3Interface internal usdcPriceFeed;
/// @notice 价格过期阈值(秒)
uint256 public priceStalenesThreshold;
error InvalidUsdcAddress();
error InvalidUsdcPriceFeedAddress();
error InvalidChainlinkPrice();
error StalePrice();
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
@@ -26,6 +30,7 @@ contract LendingPriceFeed is OwnableUpgradeable, UUPSUpgradeable {
if (_usdcPriceFeed == address(0)) revert InvalidUsdcPriceFeedAddress();
usdcAddress = _usdcAddress;
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
priceStalenesThreshold = 3600; // 默认1小时
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
@@ -35,6 +40,15 @@ contract LendingPriceFeed is OwnableUpgradeable, UUPSUpgradeable {
usdcAddress = _usdcAddress;
}
/**
* @notice 设置价格过期阈值
* @param _threshold 阈值例如3600 = 1小时86400 = 24小时
*/
function setPriceStalenessThreshold(uint256 _threshold) external onlyOwner {
require(_threshold > 0 && _threshold <= 7 days, "Invalid threshold");
priceStalenesThreshold = _threshold;
}
function getPrice(address _token) external view returns (uint256) {
if (_token == usdcAddress) {
return _getUSDCPrice();
@@ -44,15 +58,21 @@ contract LendingPriceFeed is OwnableUpgradeable, UUPSUpgradeable {
function _getUSDCPrice() internal view returns (uint256) {
(
/* uint80 roundId */,
uint80 roundId,
int256 price,
/* uint256 startedAt */,
/* uint256 updatedAt */,
/* uint80 answeredInRound */
uint256 updatedAt,
uint80 answeredInRound
) = usdcPriceFeed.latestRoundData();
// 价格有效性检查
if (price <= 0) revert InvalidChainlinkPrice();
// 新鲜度检查:确保价格数据不过期
if (updatedAt == 0) revert StalePrice();
if (answeredInRound < roundId) revert StalePrice();
if (block.timestamp - updatedAt > priceStalenesThreshold) revert StalePrice();
return uint256(price) * 1e22; // 1e22 = 10^(30-8)
}
}

View File

@@ -71,6 +71,7 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
event HandlerSet(address indexed handler, bool isActive);
event GovChanged(address indexed oldGov, address indexed newGov);
event AumAdjustmentChanged(uint256 addition, uint256 deduction);
event CooldownInherited(address indexed from, address indexed to, uint256 cooldownTime);
modifier onlyGov() {
if (msg.sender != gov) revert Forbidden();
@@ -138,6 +139,24 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
emit AumAdjustmentChanged(_addition, _deduction);
}
/**
* @notice LP 代币转账时的回调函数
* @param _from 发送方地址
* @param _to 接收方地址
* @dev 当 LP 代币转账时,接收方继承发送方的冷却时间,防止绕过冷却期
*/
function onLPTransfer(address _from, address _to) external {
// 只允许 ytLP 代币合约调用
if (msg.sender != ytLP) revert Forbidden();
// 如果发送方有冷却时间记录,且接收方的冷却时间更早(或没有记录)
// 则将发送方的冷却时间继承给接收方
if (lastAddedAt[_from] > 0 && lastAddedAt[_to] < lastAddedAt[_from]) {
lastAddedAt[_to] = lastAddedAt[_from];
emit CooldownInherited(_from, _to, lastAddedAt[_from]);
}
}
/**
* @notice 为指定账户添加流动性Handler调用
*/

View File

@@ -24,6 +24,7 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
error SpreadTooHigh();
error InvalidAddress();
error InvalidChainlinkPrice();
error StalePrice();
address public gov;
@@ -35,6 +36,7 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
// 价格保护参数
uint256 public maxPriceChangeBps; // 5% 最大价格变动
uint256 public priceStalenesThreshold; // 价格过期阈值(秒)
/// @notice USDC价格Feed
AggregatorV3Interface internal usdcPriceFeed;
@@ -72,6 +74,7 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
gov = msg.sender;
maxPriceChangeBps = 500; // 5% 最大价格变动
priceStalenesThreshold = 3600; // 默认1小时
}
/**
@@ -116,6 +119,15 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
maxPriceChangeBps = _maxPriceChangeBps;
}
/**
* @notice 设置价格过期阈值
* @param _threshold 阈值例如3600 = 1小时86400 = 24小时
*/
function setPriceStalenessThreshold(uint256 _threshold) external onlyGov {
require(_threshold > 0 && _threshold <= 7 days, "Invalid threshold");
priceStalenesThreshold = _threshold;
}
/**
* @notice 设置代币价差
* @param _token 代币地址
@@ -220,15 +232,21 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
*/
function _getUSDCPrice() internal view returns (uint256) {
(
/* uint80 roundId */,
uint80 roundId,
int256 price,
/* uint256 startedAt */,
/* uint256 updatedAt */,
/* uint80 answeredInRound */
uint256 updatedAt,
uint80 answeredInRound
) = usdcPriceFeed.latestRoundData();
// 价格有效性检查
if (price <= 0) revert InvalidChainlinkPrice();
// 新鲜度检查:确保价格数据不过期
if (updatedAt == 0) revert StalePrice();
if (answeredInRound < roundId) revert StalePrice();
if (block.timestamp - updatedAt > priceStalenesThreshold) revert StalePrice();
return uint256(price) * 1e22; // 1e22 = 10^(30-8)
}

View File

@@ -20,9 +20,12 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
error NotMinter();
error InvalidMinter();
error InvalidPoolManager();
mapping(address => bool) public isMinter;
address public poolManager;
event MinterSet(address indexed minter, bool isActive);
/**
@@ -56,6 +59,16 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
emit MinterSet(_minter, _isActive);
}
/**
* @notice 设置 PoolManager 地址
* @param _poolManager PoolManager 合约地址
* @dev 用于在转账时通知 PoolManager 更新冷却时间
*/
function setPoolManager(address _poolManager) external onlyOwner {
if (_poolManager == address(0)) revert InvalidPoolManager();
poolManager = _poolManager;
}
/**
* @notice 铸造ytLP代币
* @param _to 接收地址
@@ -74,6 +87,22 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
_burn(_from, _amount);
}
/**
* @notice 重写 _update 函数,在转账时更新冷却时间
* @dev 当 LP 代币转账时,接收方继承发送方的冷却时间,防止绕过冷却期
*/
function _update(address from, address to, uint256 value) internal override {
super._update(from, to, value);
// 只在实际转账时触发(不包括 mint 和 burn
if (from != address(0) && to != address(0) && poolManager != address(0)) {
// 通知 PoolManager 更新接收方的冷却时间
(bool success, ) = poolManager.call(
abi.encodeWithSignature("onLPTransfer(address,address)", from, to)
);
}
}
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes

View File

@@ -238,6 +238,17 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
YTAssetVault(_vault).setManager(_manager);
}
/**
* @notice 设置vault的价格过期阈值
* @param _vault vault地址
* @param _threshold 阈值(秒)
*/
function setPriceStalenessThreshold(address _vault, uint256 _threshold) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).setPriceStalenessThreshold(_threshold);
}
/**
* @notice 设置vault的下一个赎回时间
* @param _vault vault地址

View File

@@ -43,6 +43,7 @@ contract YTAssetVault is
error InvalidBatchSize();
error InvalidPriceFeed();
error InvalidChainlinkPrice();
error StalePrice();
/// @notice 工厂合约地址
address public factory;
@@ -71,6 +72,9 @@ contract YTAssetVault is
/// @notice Chainlink价格精度
uint256 public constant CHAINLINK_PRICE_PRECISION = 1e8;
/// @notice 价格过期阈值(秒)
uint256 public priceStalenesThreshold;
/// @notice 下一个赎回开放时间(所有用户统一)
uint256 public nextRedemptionTime;
@@ -165,6 +169,9 @@ contract YTAssetVault is
// 设置赎回时间
nextRedemptionTime = _redemptionTime;
// 设置默认价格过期阈值1小时
priceStalenesThreshold = 3600;
}
/**
@@ -179,15 +186,21 @@ contract YTAssetVault is
*/
function _getUSDCPrice() internal view returns (uint256) {
(
/* uint80 roundId */,
uint80 roundId,
int256 price,
/* uint256 startedAt */,
/* uint256 updatedAt */,
/* uint80 answeredInRound */
uint256 updatedAt,
uint80 answeredInRound
) = usdcPriceFeed.latestRoundData();
// 价格有效性检查
if (price <= 0) revert InvalidChainlinkPrice();
// 新鲜度检查:确保价格数据不过期
if (updatedAt == 0) revert StalePrice();
if (answeredInRound < roundId) revert StalePrice();
if (block.timestamp - updatedAt > priceStalenesThreshold) revert StalePrice();
return uint256(price);
}
@@ -228,6 +241,15 @@ contract YTAssetVault is
emit ManagerSet(_manager);
}
/**
* @notice 设置价格过期阈值
* @param _threshold 阈值例如3600 = 1小时86400 = 24小时
*/
function setPriceStalenessThreshold(uint256 _threshold) external onlyFactory {
require(_threshold > 0 && _threshold <= 7 days, "Invalid threshold");
priceStalenesThreshold = _threshold;
}
/**
* @notice 暂停合约仅factory可调用
* @dev 暂停后,所有资金流动操作将被禁止