This commit is contained in:
2026-01-19 11:56:15 +08:00
parent d56f83726b
commit ae33e0ec07
26 changed files with 222 additions and 217 deletions

View File

@@ -77,6 +77,12 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
priceStalenesThreshold = 3600; // 默认1小时
}
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
/**
* @notice 设置USDC地址
* @param _usdcAddress USDC地址
@@ -94,12 +100,6 @@ contract YTPriceFeed is Initializable, UUPSUpgradeable {
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
}
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
/**
* @notice 设置keeper权限
* @param _keeper keeper地址

View File

@@ -203,17 +203,7 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
maxUsdyAmounts[_token] = _maxUsdyAmount;
stableTokens[_token] = _isStable;
}
function clearWhitelistedToken(address _token) external onlyGov {
if (!whitelistedTokens[_token]) revert TokenNotWhitelisted();
totalTokenWeights = totalTokenWeights - tokenWeights[_token];
delete whitelistedTokens[_token];
delete stableTokens[_token];
delete tokenDecimals[_token];
delete tokenWeights[_token];
delete maxUsdyAmounts[_token];
}
function setSwapFees(
uint256 _swapFee,
uint256 _stableSwapFee,
@@ -241,12 +231,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
emit SwapEnabledSet(_isSwapEnabled);
}
function withdrawToken(address _token, address _receiver, uint256 _amount) external onlyGov {
if (!emergencyMode) revert NotInEmergency();
IERC20(_token).safeTransfer(_receiver, _amount);
_updateTokenBalance(_token);
}
function setMaxSwapSlippageBps(uint256 _slippageBps) external onlyGov {
if (_slippageBps > 2000) revert SlippageTooHigh(); // 最大20%
maxSwapSlippageBps = _slippageBps;
@@ -406,6 +390,22 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return amountOutAfterFees;
}
function clearWhitelistedToken(address _token) external onlyGov {
if (!whitelistedTokens[_token]) revert TokenNotWhitelisted();
totalTokenWeights = totalTokenWeights - tokenWeights[_token];
delete whitelistedTokens[_token];
delete stableTokens[_token];
delete tokenDecimals[_token];
delete tokenWeights[_token];
delete maxUsdyAmounts[_token];
}
function withdrawToken(address _token, address _receiver, uint256 _amount) external onlyGov {
if (!emergencyMode) revert NotInEmergency();
IERC20(_token).safeTransfer(_receiver, _amount);
_updateTokenBalance(_token);
}
/**
* @notice 获取代币价格(带价差)

View File

@@ -100,6 +100,7 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
(bool success, ) = poolManager.call(
abi.encodeWithSignature("onLPTransfer(address,address)", from, to)
);
require(success, "Failed to call onLPTransfer");
}
}

View File

@@ -91,6 +91,87 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
defaultHardCap = _defaultHardCap;
emit DefaultHardCapSet(_defaultHardCap);
}
/**
* @notice 设置指定vault的硬顶
* @param _vault vault地址
* @param _hardCap 新的硬顶值
*/
function setHardCap(address _vault, uint256 _hardCap) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).setHardCap(_hardCap);
emit HardCapSet(_vault, _hardCap);
}
/**
* @notice 批量设置硬顶
* @param _vaults vault地址数组
* @param _hardCaps 硬顶值数组
*/
function setHardCapBatch(
address[] memory _vaults,
uint256[] memory _hardCaps
) external onlyOwner {
require(_vaults.length == _hardCaps.length, "Length mismatch");
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).setHardCap(_hardCaps[i]);
emit HardCapSet(_vaults[i], _hardCaps[i]);
}
}
/**
* @notice 设置vault的管理员
* @param _vault vault地址
* @param _manager 新管理员地址
*/
function setVaultManager(address _vault, address _manager) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
if (_manager == address(0)) revert InvalidAddress();
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地址
* @param _nextRedemptionTime 赎回时间Unix时间戳
*/
function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).setNextRedemptionTime(_nextRedemptionTime);
emit NextRedemptionTimeSet(_vault, _nextRedemptionTime);
}
/**
* @notice 批量设置赎回时间
* @param _vaults vault地址数组
* @param _nextRedemptionTime 统一的赎回时间
*/
function setVaultNextRedemptionTimeBatch(
address[] memory _vaults,
uint256 _nextRedemptionTime
) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).setNextRedemptionTime(_nextRedemptionTime);
emit NextRedemptionTimeSet(_vaults[i], _nextRedemptionTime);
}
}
/**
* @notice 创建新的YTAssetVault
@@ -196,87 +277,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}
}
/**
* @notice 设置指定vault的硬顶
* @param _vault vault地址
* @param _hardCap 新的硬顶值
*/
function setHardCap(address _vault, uint256 _hardCap) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).setHardCap(_hardCap);
emit HardCapSet(_vault, _hardCap);
}
/**
* @notice 批量设置硬顶
* @param _vaults vault地址数组
* @param _hardCaps 硬顶值数组
*/
function setHardCapBatch(
address[] memory _vaults,
uint256[] memory _hardCaps
) external onlyOwner {
require(_vaults.length == _hardCaps.length, "Length mismatch");
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).setHardCap(_hardCaps[i]);
emit HardCapSet(_vaults[i], _hardCaps[i]);
}
}
/**
* @notice 设置vault的管理员
* @param _vault vault地址
* @param _manager 新管理员地址
*/
function setVaultManager(address _vault, address _manager) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
if (_manager == address(0)) revert InvalidAddress();
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地址
* @param _nextRedemptionTime 赎回时间Unix时间戳
*/
function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).setNextRedemptionTime(_nextRedemptionTime);
emit NextRedemptionTimeSet(_vault, _nextRedemptionTime);
}
/**
* @notice 批量设置赎回时间
* @param _vaults vault地址数组
* @param _nextRedemptionTime 统一的赎回时间
*/
function setVaultNextRedemptionTimeBatch(
address[] memory _vaults,
uint256 _nextRedemptionTime
) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).setNextRedemptionTime(_nextRedemptionTime);
emit NextRedemptionTimeSet(_vaults[i], _nextRedemptionTime);
}
}
/**
* @notice 暂停vault紧急情况
* @param _vault vault地址

View File

@@ -180,48 +180,6 @@ contract YTAssetVault is
*/
function _authorizeUpgrade(address newImplementation) internal override onlyFactory {}
/**
* @notice 获取并验证USDC价格从Chainlink
* @return 返回uint256格式的USDC价格精度为1e8
*/
function _getUSDCPrice() internal view returns (uint256) {
(
uint80 roundId,
int256 price,
/* uint256 startedAt */,
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);
}
/**
* @notice 计算价格转换因子
* @dev 转换因子 = 10^(ytDecimals) * PRICE_PRECISION / (10^(usdcDecimals) * CHAINLINK_PRICE_PRECISION)
* @return 价格转换因子
*/
function _getPriceConversionFactor() internal view returns (uint256) {
uint8 ytDecimals = decimals();
// 分子: 10^ytDecimals * PRICE_PRECISION (1e30)
uint256 numerator = (10 ** ytDecimals) * PRICE_PRECISION;
// 分母: 10^usdcDecimals * CHAINLINK_PRICE_PRECISION (1e8)
uint256 denominator = (10 ** usdcDecimals) * CHAINLINK_PRICE_PRECISION;
// 返回转换因子
return numerator / denominator;
}
/**
* @notice 设置硬顶
* @param _hardCap 新的硬顶值
@@ -250,21 +208,6 @@ contract YTAssetVault is
priceStalenesThreshold = _threshold;
}
/**
* @notice 暂停合约仅factory可调用
* @dev 暂停后,所有资金流动操作将被禁止
*/
function pause() external onlyFactory {
_pause();
}
/**
* @notice 恢复合约仅factory可调用
*/
function unpause() external onlyFactory {
_unpause();
}
/**
* @notice 设置下一个赎回开放时间仅factory可调用
* @param _nextRedemptionTime 下一个赎回时间Unix时间戳
@@ -442,6 +385,102 @@ contract YTAssetVault is
emit BatchProcessed(startIndex, processedUpToIndex, processedCount, totalDistributed);
}
/**
* @notice 提取USDC用于外部投资
* @param _to 接收地址
* @param _amount 提取数量
*/
function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount();
uint256 availableAssets = IERC20(usdcAddress).balanceOf(address(this));
if (_amount > availableAssets) revert InvalidAmount();
managedAssets += _amount;
IERC20(usdcAddress).safeTransfer(_to, _amount);
emit AssetsWithdrawn(_to, _amount);
}
/**
* @notice 将管理的资产归还到金库(可以归还更多,产生收益)
* @param _amount 归还数量
*/
function depositManagedAssets(uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount();
// 先更新状态遵循CEI模式
if (_amount >= managedAssets) {
// 归还金额 >= 已管理资产managedAssets归零多余部分是收益
managedAssets = 0;
} else {
// 归还金额 < 已管理资产,部分归还
managedAssets -= _amount;
}
// 从manager转入USDC到合约
IERC20(usdcAddress).transferFrom(msg.sender, address(this), _amount);
emit AssetsDeposited(_amount);
}
/**
* @notice 暂停合约仅factory可调用
* @dev 暂停后,所有资金流动操作将被禁止
*/
function pause() external onlyFactory {
_pause();
}
/**
* @notice 恢复合约仅factory可调用
*/
function unpause() external onlyFactory {
_unpause();
}
/**
* @notice 获取并验证USDC价格从Chainlink
* @return 返回uint256格式的USDC价格精度为1e8
*/
function _getUSDCPrice() internal view returns (uint256) {
(
uint80 roundId,
int256 price,
/* uint256 startedAt */,
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);
}
/**
* @notice 计算价格转换因子
* @dev 转换因子 = 10^(ytDecimals) * PRICE_PRECISION / (10^(usdcDecimals) * CHAINLINK_PRICE_PRECISION)
* @return 价格转换因子
*/
function _getPriceConversionFactor() internal view returns (uint256) {
uint8 ytDecimals = decimals();
// 分子: 10^ytDecimals * PRICE_PRECISION (1e30)
uint256 numerator = (10 ** ytDecimals) * PRICE_PRECISION;
// 分母: 10^usdcDecimals * CHAINLINK_PRICE_PRECISION (1e8)
uint256 denominator = (10 ** usdcDecimals) * CHAINLINK_PRICE_PRECISION;
// 返回转换因子
return numerator / denominator;
}
/**
* @notice 查询用户的所有提现请求ID
@@ -535,45 +574,6 @@ contract YTAssetVault is
return block.timestamp >= nextRedemptionTime;
}
/**
* @notice 提取USDC用于外部投资
* @param _to 接收地址
* @param _amount 提取数量
*/
function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount();
uint256 availableAssets = IERC20(usdcAddress).balanceOf(address(this));
if (_amount > availableAssets) revert InvalidAmount();
managedAssets += _amount;
IERC20(usdcAddress).safeTransfer(_to, _amount);
emit AssetsWithdrawn(_to, _amount);
}
/**
* @notice 将管理的资产归还到金库(可以归还更多,产生收益)
* @param _amount 归还数量
*/
function depositManagedAssets(uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount();
// 先更新状态遵循CEI模式
if (_amount >= managedAssets) {
// 归还金额 >= 已管理资产managedAssets归零多余部分是收益
managedAssets = 0;
} else {
// 归还金额 < 已管理资产,部分归还
managedAssets -= _amount;
}
// 从manager转入USDC到合约
IERC20(usdcAddress).transferFrom(msg.sender, address(this), _amount);
emit AssetsDeposited(_amount);
}
/**
* @notice 获取总资产(包含被管理的资产)
* @return 总资产 = 合约余额 + 被管理的资产