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

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
/**
* @title ILending
* @notice 借贷池核心接口
*/
interface ILending { interface ILending {
event Supply(address indexed from, address indexed dst, uint256 amount); event Supply(address indexed from, address indexed dst, uint256 amount);
event Withdraw(address indexed src, address indexed to, uint256 amount); event Withdraw(address indexed src, address indexed to, uint256 amount);

View File

@@ -10,11 +10,6 @@ import "../../interfaces/IYTVault.sol";
import "../../interfaces/IYTLPToken.sol"; import "../../interfaces/IYTLPToken.sol";
import "../../interfaces/IUSDY.sol"; import "../../interfaces/IUSDY.sol";
/**
* @title YTPoolManager
* @notice 管理ytLP的铸造和赎回计算池子AUM
* @dev UUPS可升级合约
*/
contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable { contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@@ -82,13 +77,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
_; _;
} }
/**
* @notice 初始化合约
* @param _ytVault YTVault合约地址
* @param _usdy USDY代币地址
* @param _ytLP ytLP代币地址
* @param _cooldownDuration 冷却时间(秒)
*/
function initialize( function initialize(
address _ytVault, address _ytVault,
address _usdy, address _usdy,
@@ -108,10 +96,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
cooldownDuration = _cooldownDuration; cooldownDuration = _cooldownDuration;
} }
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {} function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
function setGov(address _gov) external onlyGov { function setGov(address _gov) external onlyGov {
@@ -138,9 +122,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
emit AumAdjustmentChanged(_addition, _deduction); emit AumAdjustmentChanged(_addition, _deduction);
} }
/**
* @notice 为指定账户添加流动性Handler调用
*/
function addLiquidityForAccount( function addLiquidityForAccount(
address _fundingAccount, address _fundingAccount,
address _account, address _account,
@@ -186,9 +167,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
return mintAmount; return mintAmount;
} }
/**
* @notice 为指定账户移除流动性Handler调用
*/
function removeLiquidityForAccount( function removeLiquidityForAccount(
address _account, address _account,
address _tokenOut, address _tokenOut,
@@ -235,11 +213,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
return amountOut; return amountOut;
} }
/**
* @notice 获取ytLP价格
* @param _maximise 是否取最大值
* @return ytLP价格18位精度
*/
function getPrice(bool _maximise) external view returns (uint256) { function getPrice(bool _maximise) external view returns (uint256) {
uint256 aum = getAumInUsdy(_maximise); uint256 aum = getAumInUsdy(_maximise);
uint256 supply = IERC20(ytLP).totalSupply(); uint256 supply = IERC20(ytLP).totalSupply();
@@ -249,11 +222,6 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
return aum * YTLP_PRECISION / supply; return aum * YTLP_PRECISION / supply;
} }
/**
* @notice 获取池子总价值AUM
* @param _maximise true=使用最大价格(添加流动性时), false=使用最小价格(移除流动性时)
* @return USDY计价的总价值
*/
function getAumInUsdy(bool _maximise) public view returns (uint256) { function getAumInUsdy(bool _maximise) public view returns (uint256) {
uint256 aum = IYTVault(ytVault).getPoolValue(_maximise); uint256 aum = IYTVault(ytVault).getPoolValue(_maximise);
@@ -267,10 +235,5 @@ contract YTPoolManager is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrade
return aum; return aum;
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

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

View File

@@ -10,11 +10,6 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IYTPoolManager.sol"; import "../../interfaces/IYTPoolManager.sol";
import "../../interfaces/IYTVault.sol"; import "../../interfaces/IYTVault.sol";
/**
* @title YTRewardRouter
* @notice 用户交互入口
* @dev UUPS可升级合约
*/
contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable { contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@@ -48,13 +43,6 @@ contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrad
_; _;
} }
/**
* @notice 初始化合约
* @param _usdy USDY代币地址
* @param _ytLP ytLP代币地址
* @param _ytPoolManager YTPoolManager地址
* @param _ytVault YTVault地址
*/
function initialize( function initialize(
address _usdy, address _usdy,
address _ytLP, address _ytLP,
@@ -78,35 +66,16 @@ contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrad
ytVault = _ytVault; ytVault = _ytVault;
} }
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {} function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
/**
* @notice 暂停合约仅gov可调用
* @dev 暂停后,所有资金流动操作将被禁止
*/
function pause() external onlyGov { function pause() external onlyGov {
_pause(); _pause();
} }
/**
* @notice 恢复合约仅gov可调用
*/
function unpause() external onlyGov { function unpause() external onlyGov {
_unpause(); _unpause();
} }
/**
* @notice 添加流动性
* @param _token YT代币或USDC地址
* @param _amount 代币数量
* @param _minUsdy 最小USDY数量
* @param _minYtLP 最小ytLP数量
* @return ytLPAmount 获得的ytLP数量
*/
function addLiquidity( function addLiquidity(
address _token, address _token,
uint256 _amount, uint256 _amount,
@@ -132,14 +101,6 @@ contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrad
return ytLPAmount; return ytLPAmount;
} }
/**
* @notice 移除流动性
* @param _tokenOut 输出代币地址
* @param _ytLPAmount ytLP数量
* @param _minOut 最小输出数量
* @param _receiver 接收地址
* @return amountOut 获得的代币数量
*/
function removeLiquidity( function removeLiquidity(
address _tokenOut, address _tokenOut,
uint256 _ytLPAmount, uint256 _ytLPAmount,
@@ -161,15 +122,6 @@ contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrad
return amountOut; return amountOut;
} }
/**
* @notice YT代币互换
* @param _tokenIn 输入代币地址
* @param _tokenOut 输出代币地址
* @param _amountIn 输入数量
* @param _minOut 最小输出数量
* @param _receiver 接收地址
* @return amountOut 获得的代币数量
*/
function swapYT( function swapYT(
address _tokenIn, address _tokenIn,
address _tokenOut, address _tokenOut,
@@ -192,29 +144,15 @@ contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgrad
return amountOut; return amountOut;
} }
/**
* @notice 获取ytLP价格
* @return ytLP价格18位精度
*/
function getYtLPPrice() external view returns (uint256) { function getYtLPPrice() external view returns (uint256) {
return IYTPoolManager(ytPoolManager).getPrice(true); return IYTPoolManager(ytPoolManager).getPrice(true);
} }
/**
* @notice 获取账户价值
* @param _account 账户地址
* @return 账户持有的ytLP价值USDY计价
*/
function getAccountValue(address _account) external view returns (uint256) { function getAccountValue(address _account) external view returns (uint256) {
uint256 ytLPBalance = IERC20(ytLP).balanceOf(_account); uint256 ytLPBalance = IERC20(ytLP).balanceOf(_account);
uint256 ytLPPrice = IYTPoolManager(ytPoolManager).getPrice(true); uint256 ytLPPrice = IYTPoolManager(ytPoolManager).getPrice(true);
return ytLPBalance * ytLPPrice / (10 ** 18); return ytLPBalance * ytLPPrice / (10 ** 18);
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

@@ -9,11 +9,6 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IUSDY.sol"; import "../../interfaces/IUSDY.sol";
import "../../interfaces/IYTPriceFeed.sol"; import "../../interfaces/IYTPriceFeed.sol";
/**
* @title YTVault
* @notice 核心资金池处理YT代币的存储、交换和动态手续费
* @dev UUPS可升级合约
*/
contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable { contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@@ -50,38 +45,32 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
address public priceFeed; address public priceFeed;
address public usdy; address public usdy;
mapping(address => bool) public isSwapper; // 授权的swap调用者 mapping(address => bool) public isSwapper;
bool public isSwapEnabled; bool public isSwapEnabled;
bool public emergencyMode; bool public emergencyMode;
// 代币白名单
address[] public allWhitelistedTokens; address[] public allWhitelistedTokens;
mapping(address => bool) public whitelistedTokens; mapping(address => bool) public whitelistedTokens;
mapping(address => bool) public stableTokens; // 稳定币标记 mapping(address => bool) public stableTokens;
mapping(address => uint256) public tokenDecimals; mapping(address => uint256) public tokenDecimals;
mapping(address => uint256) public tokenWeights; mapping(address => uint256) public tokenWeights;
uint256 public totalTokenWeights; uint256 public totalTokenWeights;
// 池子资产
mapping(address => uint256) public poolAmounts; mapping(address => uint256) public poolAmounts;
mapping(address => uint256) public tokenBalances; // 跟踪实际代币余额 mapping(address => uint256) public tokenBalances;
// USDY债务追踪用于动态手续费
mapping(address => uint256) public usdyAmounts; mapping(address => uint256) public usdyAmounts;
mapping(address => uint256) public maxUsdyAmounts; mapping(address => uint256) public maxUsdyAmounts;
// 手续费配置
uint256 public swapFeeBasisPoints; uint256 public swapFeeBasisPoints;
uint256 public stableSwapFeeBasisPoints; uint256 public stableSwapFeeBasisPoints;
uint256 public taxBasisPoints; uint256 public taxBasisPoints;
uint256 public stableTaxBasisPoints; uint256 public stableTaxBasisPoints;
bool public hasDynamicFees; bool public hasDynamicFees;
// 全局滑点保护 uint256 public maxSwapSlippageBps;
uint256 public maxSwapSlippageBps; // 10% 最大滑点
// 单笔交易限额
mapping(address => uint256) public maxSwapAmount; mapping(address => uint256) public maxSwapAmount;
event Swap( event Swap(
@@ -129,11 +118,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
_; _;
} }
/**
* @notice 初始化合约
* @param _usdy USDY代币地址
* @param _priceFeed 价格预言机地址
*/
function initialize(address _usdy, address _priceFeed) external initializer { function initialize(address _usdy, address _priceFeed) external initializer {
if (_usdy == address(0) || _priceFeed == address(0)) revert InvalidAddress(); if (_usdy == address(0) || _priceFeed == address(0)) revert InvalidAddress();
@@ -144,7 +128,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
usdy = _usdy; usdy = _usdy;
priceFeed = _priceFeed; priceFeed = _priceFeed;
// 初始化默认值
isSwapEnabled = true; isSwapEnabled = true;
emergencyMode = false; emergencyMode = false;
swapFeeBasisPoints = 30; swapFeeBasisPoints = 30;
@@ -152,16 +135,11 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
taxBasisPoints = 50; taxBasisPoints = 50;
stableTaxBasisPoints = 20; stableTaxBasisPoints = 20;
hasDynamicFees = true; hasDynamicFees = true;
maxSwapSlippageBps = 1000; // 10% 最大滑点 maxSwapSlippageBps = 1000;
// 将 USDY 标记为稳定币,这样 USDY ↔ 稳定币的互换可以享受低费率
stableTokens[_usdy] = true; stableTokens[_usdy] = true;
} }
/**
* @notice 授权升级仅gov可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyGov {} function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
function setGov(address _gov) external onlyGov { function setGov(address _gov) external onlyGov {
@@ -248,7 +226,7 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
} }
function setMaxSwapSlippageBps(uint256 _slippageBps) external onlyGov { function setMaxSwapSlippageBps(uint256 _slippageBps) external onlyGov {
if (_slippageBps > 2000) revert SlippageTooHigh(); // 最大20% if (_slippageBps > 2000) revert SlippageTooHigh();
maxSwapSlippageBps = _slippageBps; maxSwapSlippageBps = _slippageBps;
} }
@@ -256,12 +234,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
maxSwapAmount[_token] = _amount; maxSwapAmount[_token] = _amount;
} }
/**
* @notice 用YT代币购买USDY添加流动性时调用
* @param _token YT代币地址
* @param _receiver USDY接收地址
* @return usdyAmountAfterFees 实际获得的USDY数量
*/
function buyUSDY(address _token, address _receiver) function buyUSDY(address _token, address _receiver)
external external
onlyPoolManager onlyPoolManager
@@ -298,12 +270,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return usdyAmountAfterFees; return usdyAmountAfterFees;
} }
/**
* @notice 用USDY卖出换取YT代币移除流动性时调用
* @param _token YT代币地址
* @param _receiver YT代币接收地址
* @return amountOutAfterFees 实际获得的YT代币数量
*/
function sellUSDY(address _token, address _receiver) function sellUSDY(address _token, address _receiver)
external external
onlyPoolManager onlyPoolManager
@@ -319,29 +285,23 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
uint256 price = _getPrice(_token, true); uint256 price = _getPrice(_token, true);
// 计算赎回金额(扣费前)
uint256 redemptionAmount = usdyAmount * PRICE_PRECISION / price; uint256 redemptionAmount = usdyAmount * PRICE_PRECISION / price;
redemptionAmount = _adjustForDecimals(redemptionAmount, usdy, _token); redemptionAmount = _adjustForDecimals(redemptionAmount, usdy, _token);
if (redemptionAmount == 0) revert InvalidAmount(); if (redemptionAmount == 0) revert InvalidAmount();
// 计算手续费和实际转出金额
uint256 feeBasisPoints = _getSwapFeeBasisPoints(usdy, _token, redemptionAmount); uint256 feeBasisPoints = _getSwapFeeBasisPoints(usdy, _token, redemptionAmount);
uint256 amountOut = redemptionAmount * (BASIS_POINTS_DIVISOR - feeBasisPoints) / BASIS_POINTS_DIVISOR; uint256 amountOut = redemptionAmount * (BASIS_POINTS_DIVISOR - feeBasisPoints) / BASIS_POINTS_DIVISOR;
if (amountOut == 0) revert InvalidAmount(); if (amountOut == 0) revert InvalidAmount();
if (poolAmounts[_token] < amountOut) revert InsufficientPool(); if (poolAmounts[_token] < amountOut) revert InsufficientPool();
// 计算实际转出的代币对应的USDY价值用于减少usdyAmount记账
uint256 usdyAmountOut = amountOut * price / PRICE_PRECISION; uint256 usdyAmountOut = amountOut * price / PRICE_PRECISION;
usdyAmountOut = _adjustForDecimals(usdyAmountOut, _token, usdy); usdyAmountOut = _adjustForDecimals(usdyAmountOut, _token, usdy);
// 手续费留在池子:只减少实际转出的部分
_decreasePoolAmount(_token, amountOut); _decreasePoolAmount(_token, amountOut);
_decreaseUsdyAmount(_token, usdyAmountOut); _decreaseUsdyAmount(_token, usdyAmountOut);
// 销毁USDY
IUSDY(usdy).burn(address(this), usdyAmount); IUSDY(usdy).burn(address(this), usdyAmount);
// 转出代币
IERC20(_token).safeTransfer(_receiver, amountOut); IERC20(_token).safeTransfer(_receiver, amountOut);
_updateTokenBalance(_token); _updateTokenBalance(_token);
@@ -350,13 +310,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return amountOut; return amountOut;
} }
/**
* @notice YT代币互换
* @param _tokenIn 输入代币地址
* @param _tokenOut 输出代币地址
* @param _receiver 接收地址
* @return amountOutAfterFees 实际获得的输出代币数量
*/
function swap( function swap(
address _tokenIn, address _tokenIn,
address _tokenOut, address _tokenOut,
@@ -370,7 +323,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
uint256 amountIn = _transferIn(_tokenIn); uint256 amountIn = _transferIn(_tokenIn);
if (amountIn == 0) revert InvalidAmount(); if (amountIn == 0) revert InvalidAmount();
// 检查单笔交易限额
if (maxSwapAmount[_tokenIn] > 0) { if (maxSwapAmount[_tokenIn] > 0) {
if (amountIn > maxSwapAmount[_tokenIn]) revert AmountExceedsLimit(); if (amountIn > maxSwapAmount[_tokenIn]) revert AmountExceedsLimit();
} }
@@ -390,7 +342,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
if (amountOutAfterFees == 0) revert InvalidAmount(); if (amountOutAfterFees == 0) revert InvalidAmount();
if (poolAmounts[_tokenOut] < amountOutAfterFees) revert InsufficientPool(); if (poolAmounts[_tokenOut] < amountOutAfterFees) revert InsufficientPool();
// 全局滑点保护10%
_validateSwapSlippage(amountIn, amountOutAfterFees, priceIn, priceOut); _validateSwapSlippage(amountIn, amountOutAfterFees, priceIn, priceOut);
_increasePoolAmount(_tokenIn, amountIn); _increasePoolAmount(_tokenIn, amountIn);
@@ -407,26 +358,14 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return amountOutAfterFees; return amountOutAfterFees;
} }
/**
* @notice 获取代币价格(带价差)
* @param _token 代币地址
* @param _maximise true=最大价格, false=最小价格
* @return 价格30位精度
*/
function getPrice(address _token, bool _maximise) external view returns (uint256) { function getPrice(address _token, bool _maximise) external view returns (uint256) {
return _getPrice(_token, _maximise); return _getPrice(_token, _maximise);
} }
/**
* @notice 获取最大价格
*/
function getMaxPrice(address _token) external view returns (uint256) { function getMaxPrice(address _token) external view returns (uint256) {
return _getPrice(_token, true); return _getPrice(_token, true);
} }
/**
* @notice 获取最小价格
*/
function getMinPrice(address _token) external view returns (uint256) { function getMinPrice(address _token) external view returns (uint256) {
return _getPrice(_token, false); return _getPrice(_token, false);
} }
@@ -435,11 +374,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return allWhitelistedTokens; return allWhitelistedTokens;
} }
/**
* @notice 获取池子总价值
* @param _maximise true=使用最大价格(对协议有利), false=使用最小价格(对用户有利)
* @return 池子总价值USDY计价
*/
function getPoolValue(bool _maximise) external view returns (uint256) { function getPoolValue(bool _maximise) external view returns (uint256) {
uint256 totalValue = 0; uint256 totalValue = 0;
for (uint256 i = 0; i < allWhitelistedTokens.length; i++) { for (uint256 i = 0; i < allWhitelistedTokens.length; i++) {
@@ -476,13 +410,6 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
usdyAmounts[_token] = value - _amount; usdyAmounts[_token] = value - _amount;
} }
/**
* @notice 获取swap手续费率公开方法供前端调用
* @param _tokenIn 输入代币
* @param _tokenOut 输出代币
* @param _usdyAmount USDY数量
* @return 手续费率basis points
*/
function getSwapFeeBasisPoints( function getSwapFeeBasisPoints(
address _tokenIn, address _tokenIn,
address _tokenOut, address _tokenOut,
@@ -549,14 +476,11 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
? nextAmount - targetAmount ? nextAmount - targetAmount
: targetAmount - nextAmount; : targetAmount - nextAmount;
// 改善平衡 → 降低手续费
if (nextDiff < initialDiff) { if (nextDiff < initialDiff) {
uint256 rebateBps = _taxBasisPoints * initialDiff / targetAmount; uint256 rebateBps = _taxBasisPoints * initialDiff / targetAmount;
return rebateBps > _feeBasisPoints ? 0 : _feeBasisPoints - rebateBps; return rebateBps > _feeBasisPoints ? 0 : _feeBasisPoints - rebateBps;
} }
// 恶化平衡 → 提高手续费
// taxBps = tax * (a + b) / (2 * target)
uint256 sumDiff = initialDiff + nextDiff; uint256 sumDiff = initialDiff + nextDiff;
if (sumDiff / 2 > targetAmount) { if (sumDiff / 2 > targetAmount) {
sumDiff = targetAmount * 2; sumDiff = targetAmount * 2;
@@ -596,10 +520,8 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
uint256 _priceIn, uint256 _priceIn,
uint256 _priceOut uint256 _priceOut
) private view { ) private view {
// 计算预期输出(不含手续费)
uint256 expectedOut = _amountIn * _priceIn / _priceOut; uint256 expectedOut = _amountIn * _priceIn / _priceOut;
// 计算实际滑点
if (expectedOut > _amountOut) { if (expectedOut > _amountOut) {
uint256 slippage = (expectedOut - _amountOut) * BASIS_POINTS_DIVISOR / expectedOut; uint256 slippage = (expectedOut - _amountOut) * BASIS_POINTS_DIVISOR / expectedOut;
if (slippage > maxSwapSlippageBps) revert SlippageTooHigh(); if (slippage > maxSwapSlippageBps) revert SlippageTooHigh();
@@ -629,10 +551,5 @@ contract YTVault is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
return _amount * (10 ** (decimalsTo - decimalsFrom)); return _amount * (10 ** (decimalsTo - decimalsFrom));
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

@@ -6,11 +6,6 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/**
* @title USDY Token
* @notice 统一计价代币
* @dev 只有授权的Vault可以铸造和销毁UUPS可升级合约
*/
contract USDY is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable { contract USDY is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor /// @custom:oz-upgrades-unsafe-allow constructor
@@ -31,62 +26,32 @@ contract USDY is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgrad
_; _;
} }
/**
* @notice 初始化合约
*/
function initialize() external initializer { function initialize() external initializer {
__ERC20_init("YT USD", "USDY"); __ERC20_init("YT USD", "USDY");
__Ownable_init(msg.sender); __Ownable_init(msg.sender);
__UUPSUpgradeable_init(); __UUPSUpgradeable_init();
} }
/**
* @notice 授权升级仅owner可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
/**
* @notice 添加授权的Vault地址
* @param _vault Vault合约地址
*/
function addVault(address _vault) external onlyOwner { function addVault(address _vault) external onlyOwner {
if (_vault == address(0)) revert InvalidVault(); if (_vault == address(0)) revert InvalidVault();
vaults[_vault] = true; vaults[_vault] = true;
emit VaultAdded(_vault); emit VaultAdded(_vault);
} }
/**
* @notice 移除授权的Vault地址
* @param _vault Vault合约地址
*/
function removeVault(address _vault) external onlyOwner { function removeVault(address _vault) external onlyOwner {
vaults[_vault] = false; vaults[_vault] = false;
emit VaultRemoved(_vault); emit VaultRemoved(_vault);
} }
/**
* @notice 铸造USDY代币
* @param _account 接收地址
* @param _amount 铸造数量
*/
function mint(address _account, uint256 _amount) external onlyVault { function mint(address _account, uint256 _amount) external onlyVault {
_mint(_account, _amount); _mint(_account, _amount);
} }
/**
* @notice 销毁USDY代币
* @param _account 销毁地址
* @param _amount 销毁数量
*/
function burn(address _account, uint256 _amount) external onlyVault { function burn(address _account, uint256 _amount) external onlyVault {
_burn(_account, _amount); _burn(_account, _amount);
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

@@ -6,11 +6,6 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/**
* @title YTLPToken
* @notice LP代币代表用户在池子中的份额
* @dev 只有授权的MinterYTPoolManager可以铸造和销毁UUPS可升级合约
*/
contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable { contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor /// @custom:oz-upgrades-unsafe-allow constructor
@@ -25,19 +20,12 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
event MinterSet(address indexed minter, bool isActive); event MinterSet(address indexed minter, bool isActive);
/**
* @notice 初始化合约
*/
function initialize() external initializer { function initialize() external initializer {
__ERC20_init("YT Liquidity Provider", "ytLP"); __ERC20_init("YT Liquidity Provider", "ytLP");
__Ownable_init(msg.sender); __Ownable_init(msg.sender);
__UUPSUpgradeable_init(); __UUPSUpgradeable_init();
} }
/**
* @notice 授权升级仅owner可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
modifier onlyMinter() { modifier onlyMinter() {
@@ -45,39 +33,19 @@ contract YTLPToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSU
_; _;
} }
/**
* @notice 设置铸造权限
* @param _minter 铸造者地址
* @param _isActive 是否激活
*/
function setMinter(address _minter, bool _isActive) external onlyOwner { function setMinter(address _minter, bool _isActive) external onlyOwner {
if (_minter == address(0)) revert InvalidMinter(); if (_minter == address(0)) revert InvalidMinter();
isMinter[_minter] = _isActive; isMinter[_minter] = _isActive;
emit MinterSet(_minter, _isActive); emit MinterSet(_minter, _isActive);
} }
/**
* @notice 铸造ytLP代币
* @param _to 接收地址
* @param _amount 铸造数量
*/
function mint(address _to, uint256 _amount) external onlyMinter { function mint(address _to, uint256 _amount) external onlyMinter {
_mint(_to, _amount); _mint(_to, _amount);
} }
/**
* @notice 销毁ytLP代币
* @param _from 销毁地址
* @param _amount 销毁数量
*/
function burn(address _from, uint256 _amount) external onlyMinter { function burn(address _from, uint256 _amount) external onlyMinter {
_burn(_from, _amount); _burn(_from, _amount);
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

@@ -7,11 +7,6 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./YTAssetVault.sol"; import "./YTAssetVault.sol";
/**
* @title YTAssetFactory
* @notice 用于批量创建和管理YT资产金库合约的工厂
* @dev UUPS可升级合约
*/
contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable { contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
error InvalidAddress(); error InvalidAddress();
@@ -23,16 +18,9 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_disableInitializers(); _disableInitializers();
} }
/// @notice YTAssetVault实现合约地址
address public vaultImplementation; address public vaultImplementation;
/// @notice 所有创建的vault地址列表
address[] public allVaults; address[] public allVaults;
/// @notice vault地址 => 是否存在
mapping(address => bool) public isVault; mapping(address => bool) public isVault;
/// @notice 默认硬顶值0表示无限制
uint256 public defaultHardCap; uint256 public defaultHardCap;
event VaultCreated( event VaultCreated(
@@ -49,11 +37,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
event PricesUpdated(address indexed vault, uint256 ytPrice); event PricesUpdated(address indexed vault, uint256 ytPrice);
event NextRedemptionTimeSet(address indexed vault, uint256 redemptionTime); event NextRedemptionTimeSet(address indexed vault, uint256 redemptionTime);
/**
* @notice 初始化工厂
* @param _vaultImplementation YTAssetVault实现合约地址
* @param _defaultHardCap 默认硬顶值
*/
function initialize( function initialize(
address _vaultImplementation, address _vaultImplementation,
uint256 _defaultHardCap uint256 _defaultHardCap
@@ -66,44 +49,20 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
vaultImplementation = _vaultImplementation; vaultImplementation = _vaultImplementation;
defaultHardCap = _defaultHardCap; defaultHardCap = _defaultHardCap;
} }
/**
* @notice 授权升级仅owner可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
/**
* @notice 更新YTAssetVault实现合约
* @param _newImplementation 新的实现合约地址
*/
function setVaultImplementation(address _newImplementation) external onlyOwner { function setVaultImplementation(address _newImplementation) external onlyOwner {
if (_newImplementation == address(0)) revert InvalidAddress(); if (_newImplementation == address(0)) revert InvalidAddress();
vaultImplementation = _newImplementation; vaultImplementation = _newImplementation;
emit VaultImplementationUpdated(_newImplementation); emit VaultImplementationUpdated(_newImplementation);
} }
/**
* @notice 设置默认硬顶
* @param _defaultHardCap 新的默认硬顶值
*/
function setDefaultHardCap(uint256 _defaultHardCap) external onlyOwner { function setDefaultHardCap(uint256 _defaultHardCap) external onlyOwner {
defaultHardCap = _defaultHardCap; defaultHardCap = _defaultHardCap;
emit DefaultHardCapSet(_defaultHardCap); emit DefaultHardCapSet(_defaultHardCap);
} }
/**
* @notice 创建新的YTAssetVault
* @param _name YT代币名称
* @param _symbol YT代币符号
* @param _manager 管理员地址
* @param _hardCap 硬顶限制0表示使用默认值
* @param _usdc USDC代币地址传0使用默认地址
* @param _redemptionTime 赎回时间Unix时间戳
* @param _initialYtPrice 初始YT价格精度1e30传0则使用默认值1.0
* @param _usdcPriceFeed Chainlink USDC价格Feed地址
* @return vault 新创建的vault地址
*/
function createVault( function createVault(
string memory _name, string memory _name,
string memory _symbol, string memory _symbol,
@@ -115,11 +74,9 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
address _usdcPriceFeed address _usdcPriceFeed
) external onlyOwner returns (address vault) { ) external onlyOwner returns (address vault) {
if (_manager == address(0)) revert InvalidAddress(); if (_manager == address(0)) revert InvalidAddress();
// 如果传入0使用默认硬顶
uint256 actualHardCap = _hardCap == 0 ? defaultHardCap : _hardCap; uint256 actualHardCap = _hardCap == 0 ? defaultHardCap : _hardCap;
// 编码初始化数据
bytes memory initData = abi.encodeWithSelector( bytes memory initData = abi.encodeWithSelector(
YTAssetVault.initialize.selector, YTAssetVault.initialize.selector,
_name, _name,
@@ -132,10 +89,8 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_usdcPriceFeed _usdcPriceFeed
); );
// 部署代理合约
vault = address(new ERC1967Proxy(vaultImplementation, initData)); vault = address(new ERC1967Proxy(vaultImplementation, initData));
// 记录vault信息
allVaults.push(vault); allVaults.push(vault);
isVault[vault] = true; isVault[vault] = true;
@@ -149,18 +104,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
); );
} }
/**
* @notice 批量创建vault
* @param _names YT代币名称数组
* @param _symbols YT代币符号数组
* @param _managers 管理员地址数组
* @param _hardCaps 硬顶数组
* @param _usdc USDC代币地址传0使用默认地址
* @param _redemptionTimes 赎回时间数组Unix时间戳
* @param _initialYtPrices 初始YT价格数组精度1e30
* @param _usdcPriceFeed Chainlink USDC价格Feed地址
* @return vaults 创建的vault地址数组
*/
function createVaultBatch( function createVaultBatch(
string[] memory _names, string[] memory _names,
string[] memory _symbols, string[] memory _symbols,
@@ -196,11 +139,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 设置指定vault的硬顶
* @param _vault vault地址
* @param _hardCap 新的硬顶值
*/
function setHardCap(address _vault, uint256 _hardCap) external onlyOwner { function setHardCap(address _vault, uint256 _hardCap) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
@@ -208,11 +146,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
emit HardCapSet(_vault, _hardCap); emit HardCapSet(_vault, _hardCap);
} }
/**
* @notice 批量设置硬顶
* @param _vaults vault地址数组
* @param _hardCaps 硬顶值数组
*/
function setHardCapBatch( function setHardCapBatch(
address[] memory _vaults, address[] memory _vaults,
uint256[] memory _hardCaps uint256[] memory _hardCaps
@@ -226,11 +159,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 设置vault的管理员
* @param _vault vault地址
* @param _manager 新管理员地址
*/
function setVaultManager(address _vault, address _manager) external onlyOwner { function setVaultManager(address _vault, address _manager) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
if (_manager == address(0)) revert InvalidAddress(); if (_manager == address(0)) revert InvalidAddress();
@@ -238,11 +166,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
YTAssetVault(_vault).setManager(_manager); YTAssetVault(_vault).setManager(_manager);
} }
/**
* @notice 设置vault的下一个赎回时间
* @param _vault vault地址
* @param _nextRedemptionTime 赎回时间Unix时间戳
*/
function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner { function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
@@ -250,11 +173,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
emit NextRedemptionTimeSet(_vault, _nextRedemptionTime); emit NextRedemptionTimeSet(_vault, _nextRedemptionTime);
} }
/**
* @notice 批量设置赎回时间
* @param _vaults vault地址数组
* @param _nextRedemptionTime 统一的赎回时间
*/
function setVaultNextRedemptionTimeBatch( function setVaultNextRedemptionTimeBatch(
address[] memory _vaults, address[] memory _vaults,
uint256 _nextRedemptionTime uint256 _nextRedemptionTime
@@ -266,30 +184,18 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 暂停vault紧急情况
* @param _vault vault地址
*/
function pauseVault(address _vault) external onlyOwner { function pauseVault(address _vault) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).pause(); YTAssetVault(_vault).pause();
} }
/**
* @notice 恢复vault
* @param _vault vault地址
*/
function unpauseVault(address _vault) external onlyOwner { function unpauseVault(address _vault) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).unpause(); YTAssetVault(_vault).unpause();
} }
/**
* @notice 批量暂停vaults
* @param _vaults vault地址数组
*/
function pauseVaultBatch(address[] memory _vaults) external onlyOwner { function pauseVaultBatch(address[] memory _vaults) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) { for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists(); if (!isVault[_vaults[i]]) revert VaultNotExists();
@@ -297,10 +203,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 批量恢复vaults
* @param _vaults vault地址数组
*/
function unpauseVaultBatch(address[] memory _vaults) external onlyOwner { function unpauseVaultBatch(address[] memory _vaults) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) { for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists(); if (!isVault[_vaults[i]]) revert VaultNotExists();
@@ -308,11 +210,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 更新vault价格
* @param _vault vault地址
* @param _ytPrice YT价格精度1e30
*/
function updateVaultPrices( function updateVaultPrices(
address _vault, address _vault,
uint256 _ytPrice uint256 _ytPrice
@@ -323,11 +220,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
emit PricesUpdated(_vault, _ytPrice); emit PricesUpdated(_vault, _ytPrice);
} }
/**
* @notice 批量更新价格
* @param _vaults vault地址数组
* @param _ytPrices YT价格数组精度1e30
*/
function updateVaultPricesBatch( function updateVaultPricesBatch(
address[] memory _vaults, address[] memory _vaults,
uint256[] memory _ytPrices uint256[] memory _ytPrices
@@ -341,11 +233,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 升级指定vault
* @param _vault vault地址
* @param _newImplementation 新实现地址
*/
function upgradeVault(address _vault, address _newImplementation) external onlyOwner { function upgradeVault(address _vault, address _newImplementation) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists(); if (!isVault[_vault]) revert VaultNotExists();
if (_newImplementation == address(0)) revert InvalidAddress(); if (_newImplementation == address(0)) revert InvalidAddress();
@@ -353,11 +240,6 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
YTAssetVault(_vault).upgradeToAndCall(_newImplementation, ""); YTAssetVault(_vault).upgradeToAndCall(_newImplementation, "");
} }
/**
* @notice 批量升级vault
* @param _vaults vault地址数组
* @param _newImplementation 新实现地址
*/
function upgradeVaultBatch( function upgradeVaultBatch(
address[] memory _vaults, address[] memory _vaults,
address _newImplementation address _newImplementation
@@ -370,18 +252,10 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 获取所有vault数量
*/
function getVaultCount() external view returns (uint256) { function getVaultCount() external view returns (uint256) {
return allVaults.length; return allVaults.length;
} }
/**
* @notice 获取指定范围的vault地址
* @param _start 起始索引
* @param _end 结束索引(不包含)
*/
function getVaults(uint256 _start, uint256 _end) function getVaults(uint256 _start, uint256 _end)
external external
view view
@@ -395,17 +269,10 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
} }
} }
/**
* @notice 获取所有vault地址
*/
function getAllVaults() external view returns (address[] memory) { function getAllVaults() external view returns (address[] memory) {
return allVaults; return allVaults;
} }
/**
* @notice 获取vault详细信息
* @param _vault vault地址
*/
function getVaultInfo(address _vault) external view returns ( function getVaultInfo(address _vault) external view returns (
bool exists, bool exists,
uint256 totalAssets, uint256 totalAssets,
@@ -431,9 +298,5 @@ contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
) = YTAssetVault(_vault).getVaultInfo(); ) = YTAssetVault(_vault).getVaultInfo();
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

View File

@@ -11,11 +11,6 @@ import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
/**
* @title YTAssetVault
* @notice 基于价格的资产金库用户根据USDC和YT代币价格进行兑换
* @dev UUPS可升级合约YT是份额代币
*/
contract YTAssetVault is contract YTAssetVault is
Initializable, Initializable,
ERC20Upgradeable, ERC20Upgradeable,
@@ -44,62 +39,39 @@ contract YTAssetVault is
error InvalidPriceFeed(); error InvalidPriceFeed();
error InvalidChainlinkPrice(); error InvalidChainlinkPrice();
/// @notice 工厂合约地址
address public factory; address public factory;
/// @notice 管理员地址
address public manager; address public manager;
/// @notice YT代币硬顶最大可铸造的YT数量
uint256 public hardCap; uint256 public hardCap;
/// @notice 已提取用于管理的USDC数量
uint256 public managedAssets; uint256 public managedAssets;
/// @notice USDC代币地址
address public usdcAddress; address public usdcAddress;
/// @notice USDC代币精度从代币合约读取
uint8 public usdcDecimals; uint8 public usdcDecimals;
/// @notice YT价格精度1e30
uint256 public ytPrice; uint256 public ytPrice;
/// @notice 价格精度
uint256 public constant PRICE_PRECISION = 1e30; uint256 public constant PRICE_PRECISION = 1e30;
/// @notice Chainlink价格精度
uint256 public constant CHAINLINK_PRICE_PRECISION = 1e8; uint256 public constant CHAINLINK_PRICE_PRECISION = 1e8;
/// @notice 下一个赎回开放时间(所有用户统一)
uint256 public nextRedemptionTime; uint256 public nextRedemptionTime;
/// @notice USDC价格Feed
AggregatorV3Interface internal usdcPriceFeed; AggregatorV3Interface internal usdcPriceFeed;
/// @notice 提现请求结构体
struct WithdrawRequest { struct WithdrawRequest {
address user; // 用户地址 address user;
uint256 ytAmount; // YT数量 uint256 ytAmount;
uint256 usdcAmount; // 应得USDC数量 uint256 usdcAmount;
uint256 requestTime; // 请求时间 uint256 requestTime;
uint256 queueIndex; // 队列位置 uint256 queueIndex;
bool processed; // 是否已处理 bool processed;
} }
/// @notice 请求ID => 请求详情
mapping(uint256 => WithdrawRequest) public withdrawRequests; mapping(uint256 => WithdrawRequest) public withdrawRequests;
/// @notice 用户地址 => 用户的所有请求ID列表
mapping(address => uint256[]) private userRequestIds; mapping(address => uint256[]) private userRequestIds;
/// @notice 请求ID计数器
uint256 public requestIdCounter; uint256 public requestIdCounter;
/// @notice 已处理到的队列位置
uint256 public processedUpToIndex; uint256 public processedUpToIndex;
/// @notice 当前待处理的请求数量(实时维护,避免循环计算)
uint256 public pendingRequestsCount; uint256 public pendingRequestsCount;
event HardCapSet(uint256 newHardCap); event HardCapSet(uint256 newHardCap);
@@ -124,16 +96,6 @@ contract YTAssetVault is
_; _;
} }
/**
* @notice 初始化金库
* @param _name YT代币名称
* @param _symbol YT代币符号
* @param _manager 管理员地址
* @param _hardCap 硬顶限制
* @param _usdc USDC代币地址可选传0则使用默认地址
* @param _redemptionTime 赎回时间Unix时间戳
* @param _initialYtPrice 初始YT价格精度1e30传0则使用默认值1.0
*/
function initialize( function initialize(
string memory _name, string memory _name,
string memory _symbol, string memory _symbol,
@@ -153,30 +115,19 @@ contract YTAssetVault is
usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed); usdcPriceFeed = AggregatorV3Interface(_usdcPriceFeed);
usdcAddress = _usdc; usdcAddress = _usdc;
// 获取USDC的decimals
usdcDecimals = IERC20Metadata(usdcAddress).decimals(); usdcDecimals = IERC20Metadata(usdcAddress).decimals();
factory = msg.sender; factory = msg.sender;
manager = _manager; manager = _manager;
hardCap = _hardCap; hardCap = _hardCap;
// 使用传入的初始价格如果为0则使用默认值1.0
ytPrice = _initialYtPrice == 0 ? PRICE_PRECISION : _initialYtPrice; ytPrice = _initialYtPrice == 0 ? PRICE_PRECISION : _initialYtPrice;
// 设置赎回时间
nextRedemptionTime = _redemptionTime; nextRedemptionTime = _redemptionTime;
} }
/**
* @notice 授权升级仅factory可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyFactory {} function _authorizeUpgrade(address newImplementation) internal override onlyFactory {}
/**
* @notice 获取并验证USDC价格从Chainlink
* @return 返回uint256格式的USDC价格精度为1e8
*/
function _getUSDCPrice() internal view returns (uint256) { function _getUSDCPrice() internal view returns (uint256) {
( (
/* uint80 roundId */, /* uint80 roundId */,
@@ -191,72 +142,40 @@ contract YTAssetVault is
return uint256(price); return uint256(price);
} }
/**
* @notice 计算价格转换因子
* @dev 转换因子 = 10^(ytDecimals) * PRICE_PRECISION / (10^(usdcDecimals) * CHAINLINK_PRICE_PRECISION)
* @return 价格转换因子
*/
function _getPriceConversionFactor() internal view returns (uint256) { function _getPriceConversionFactor() internal view returns (uint256) {
uint8 ytDecimals = decimals(); uint8 ytDecimals = decimals();
// 分子: 10^ytDecimals * PRICE_PRECISION (1e30)
uint256 numerator = (10 ** ytDecimals) * PRICE_PRECISION; uint256 numerator = (10 ** ytDecimals) * PRICE_PRECISION;
// 分母: 10^usdcDecimals * CHAINLINK_PRICE_PRECISION (1e8)
uint256 denominator = (10 ** usdcDecimals) * CHAINLINK_PRICE_PRECISION; uint256 denominator = (10 ** usdcDecimals) * CHAINLINK_PRICE_PRECISION;
// 返回转换因子
return numerator / denominator; return numerator / denominator;
} }
/**
* @notice 设置硬顶
* @param _hardCap 新的硬顶值
*/
function setHardCap(uint256 _hardCap) external onlyFactory { function setHardCap(uint256 _hardCap) external onlyFactory {
if (_hardCap < totalSupply()) revert InvalidHardCap(); if (_hardCap < totalSupply()) revert InvalidHardCap();
hardCap = _hardCap; hardCap = _hardCap;
emit HardCapSet(_hardCap); emit HardCapSet(_hardCap);
} }
/**
* @notice 设置管理员
* @param _manager 新管理员地址
*/
function setManager(address _manager) external onlyFactory { function setManager(address _manager) external onlyFactory {
manager = _manager; manager = _manager;
emit ManagerSet(_manager); emit ManagerSet(_manager);
} }
/**
* @notice 暂停合约仅factory可调用
* @dev 暂停后,所有资金流动操作将被禁止
*/
function pause() external onlyFactory { function pause() external onlyFactory {
_pause(); _pause();
} }
/**
* @notice 恢复合约仅factory可调用
*/
function unpause() external onlyFactory { function unpause() external onlyFactory {
_unpause(); _unpause();
} }
/**
* @notice 设置下一个赎回开放时间仅factory可调用
* @param _nextRedemptionTime 下一个赎回时间Unix时间戳
* @dev 所有用户统一在此时间后才能赎回,类似基金的赎回日
*/
function setNextRedemptionTime(uint256 _nextRedemptionTime) external onlyFactory { function setNextRedemptionTime(uint256 _nextRedemptionTime) external onlyFactory {
nextRedemptionTime = _nextRedemptionTime; nextRedemptionTime = _nextRedemptionTime;
emit NextRedemptionTimeSet(_nextRedemptionTime); emit NextRedemptionTimeSet(_nextRedemptionTime);
} }
/**
* @notice 更新价格仅manager可调用
* @param _ytPrice YT价格精度1e30
*/
function updatePrices(uint256 _ytPrice) external onlyFactory { function updatePrices(uint256 _ytPrice) external onlyFactory {
if (_ytPrice == 0) revert InvalidPrice(); if (_ytPrice == 0) revert InvalidPrice();
@@ -265,12 +184,6 @@ contract YTAssetVault is
emit PriceUpdated(_ytPrice, block.timestamp); emit PriceUpdated(_ytPrice, block.timestamp);
} }
/**
* @notice 用USDC购买YT
* @param _usdcAmount 支付的USDC数量
* @return ytAmount 实际获得的YT数量
* @dev 首次购买时YT价格 = USDC价格1:1兑换
*/
function depositYT(uint256 _usdcAmount) function depositYT(uint256 _usdcAmount)
external external
nonReentrant nonReentrant
@@ -282,30 +195,19 @@ contract YTAssetVault is
uint256 usdcPrice = _getUSDCPrice(); uint256 usdcPrice = _getUSDCPrice();
uint256 conversionFactor = _getPriceConversionFactor(); uint256 conversionFactor = _getPriceConversionFactor();
// 计算可以购买的YT数量
// ytAmount = _usdcAmount * usdcPrice * conversionFactor / ytPrice
ytAmount = (_usdcAmount * usdcPrice * conversionFactor) / ytPrice; ytAmount = (_usdcAmount * usdcPrice * conversionFactor) / ytPrice;
// 检查硬顶
if (hardCap > 0 && totalSupply() + ytAmount > hardCap) { if (hardCap > 0 && totalSupply() + ytAmount > hardCap) {
revert HardCapExceeded(); revert HardCapExceeded();
} }
// 转入USDC
IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _usdcAmount); IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _usdcAmount);
// 铸造YT
_mint(msg.sender, ytAmount); _mint(msg.sender, ytAmount);
emit Buy(msg.sender, _usdcAmount, ytAmount); emit Buy(msg.sender, _usdcAmount, ytAmount);
} }
/**
* @notice 提交YT提现请求需要等到统一赎回时间
* @param _ytAmount 卖出的YT数量
* @return requestId 提现请求ID
* @dev 用户提交请求后YT会立即销毁
*/
function withdrawYT(uint256 _ytAmount) function withdrawYT(uint256 _ytAmount)
external external
nonReentrant nonReentrant
@@ -315,7 +217,6 @@ contract YTAssetVault is
if (_ytAmount == 0) revert InvalidAmount(); if (_ytAmount == 0) revert InvalidAmount();
if (balanceOf(msg.sender) < _ytAmount) revert InsufficientYTA(); if (balanceOf(msg.sender) < _ytAmount) revert InsufficientYTA();
// 检查是否到达统一赎回时间
if (block.timestamp < nextRedemptionTime) { if (block.timestamp < nextRedemptionTime) {
revert StillInLockPeriod(); revert StillInLockPeriod();
} }
@@ -323,14 +224,10 @@ contract YTAssetVault is
uint256 usdcPrice = _getUSDCPrice(); uint256 usdcPrice = _getUSDCPrice();
uint256 conversionFactor = _getPriceConversionFactor(); uint256 conversionFactor = _getPriceConversionFactor();
// 计算可以换取的USDC数量
// usdcAmount = _ytAmount * ytPrice / (usdcPrice * conversionFactor)
uint256 usdcAmount = (_ytAmount * ytPrice) / (usdcPrice * conversionFactor); uint256 usdcAmount = (_ytAmount * ytPrice) / (usdcPrice * conversionFactor);
// 销毁YT代币
_burn(msg.sender, _ytAmount); _burn(msg.sender, _ytAmount);
// 创建提现请求
requestId = requestIdCounter; requestId = requestIdCounter;
withdrawRequests[requestId] = WithdrawRequest({ withdrawRequests[requestId] = WithdrawRequest({
user: msg.sender, user: msg.sender,
@@ -341,32 +238,21 @@ contract YTAssetVault is
processed: false processed: false
}); });
// 记录用户的请求ID
userRequestIds[msg.sender].push(requestId); userRequestIds[msg.sender].push(requestId);
// 递增计数器
requestIdCounter++; requestIdCounter++;
// 增加待处理请求计数
pendingRequestsCount++; pendingRequestsCount++;
emit WithdrawRequestCreated(requestId, msg.sender, _ytAmount, usdcAmount, requestId); emit WithdrawRequestCreated(requestId, msg.sender, _ytAmount, usdcAmount, requestId);
} }
/**
* @notice 批量处理提现请求仅manager或factory可调用
* @param _batchSize 本批次最多处理的请求数量
* @return processedCount 实际处理的请求数量
* @return totalDistributed 实际分发的USDC总量
* @dev 按照请求ID顺序即时间先后依次处理遇到资金不足时停止
*/
function processBatchWithdrawals(uint256 _batchSize) function processBatchWithdrawals(uint256 _batchSize)
external external
nonReentrant nonReentrant
whenNotPaused whenNotPaused
returns (uint256 processedCount, uint256 totalDistributed) returns (uint256 processedCount, uint256 totalDistributed)
{ {
// 权限检查只有manager或factory可以调用
if (msg.sender != manager && msg.sender != factory) { if (msg.sender != manager && msg.sender != factory) {
revert Forbidden(); revert Forbidden();
} }
@@ -379,43 +265,33 @@ contract YTAssetVault is
for (uint256 i = processedUpToIndex; i < requestIdCounter && processedCount < _batchSize; i++) { for (uint256 i = processedUpToIndex; i < requestIdCounter && processedCount < _batchSize; i++) {
WithdrawRequest storage request = withdrawRequests[i]; WithdrawRequest storage request = withdrawRequests[i];
// 跳过已处理的请求
if (request.processed) { if (request.processed) {
continue; continue;
} }
// 检查是否有足够的USDC
if (availableUSDC >= request.usdcAmount) { if (availableUSDC >= request.usdcAmount) {
// 转账USDC给用户
IERC20(usdcAddress).safeTransfer(request.user, request.usdcAmount); IERC20(usdcAddress).safeTransfer(request.user, request.usdcAmount);
// 标记为已处理
request.processed = true; request.processed = true;
// 更新统计
availableUSDC -= request.usdcAmount; availableUSDC -= request.usdcAmount;
totalDistributed += request.usdcAmount; totalDistributed += request.usdcAmount;
processedCount++; processedCount++;
// 减少待处理请求计数
pendingRequestsCount--; pendingRequestsCount--;
emit WithdrawRequestProcessed(i, request.user, request.usdcAmount); emit WithdrawRequestProcessed(i, request.user, request.usdcAmount);
} else { } else {
// USDC不足停止处理
break; break;
} }
} }
// 更新处理进度(跳到下一个未处理的位置)
if (processedCount > 0) { if (processedCount > 0) {
// 找到下一个未处理的位置
for (uint256 i = processedUpToIndex; i < requestIdCounter; i++) { for (uint256 i = processedUpToIndex; i < requestIdCounter; i++) {
if (!withdrawRequests[i].processed) { if (!withdrawRequests[i].processed) {
processedUpToIndex = i; processedUpToIndex = i;
break; break;
} }
// 如果所有请求都已处理完
if (i == requestIdCounter - 1) { if (i == requestIdCounter - 1) {
processedUpToIndex = requestIdCounter; processedUpToIndex = requestIdCounter;
} }
@@ -425,43 +301,22 @@ contract YTAssetVault is
emit BatchProcessed(startIndex, processedUpToIndex, processedCount, totalDistributed); emit BatchProcessed(startIndex, processedUpToIndex, processedCount, totalDistributed);
} }
/**
* @notice 查询用户的所有提现请求ID
* @param _user 用户地址
* @return 用户的所有请求ID数组
*/
function getUserRequestIds(address _user) external view returns (uint256[] memory) { function getUserRequestIds(address _user) external view returns (uint256[] memory) {
return userRequestIds[_user]; return userRequestIds[_user];
} }
/**
* @notice 查询指定请求的详情
* @param _requestId 请求ID
* @return request 请求详情
*/
function getRequestDetails(uint256 _requestId) external view returns (WithdrawRequest memory request) { function getRequestDetails(uint256 _requestId) external view returns (WithdrawRequest memory request) {
if (_requestId >= requestIdCounter) revert RequestNotFound(); if (_requestId >= requestIdCounter) revert RequestNotFound();
return withdrawRequests[_requestId]; return withdrawRequests[_requestId];
} }
/**
* @notice 获取待处理的请求数量
* @return 待处理的请求总数
* @dev 使用实时维护的计数器O(1)复杂度避免gas爆炸
*/
function getPendingRequestsCount() external view returns (uint256) { function getPendingRequestsCount() external view returns (uint256) {
return pendingRequestsCount; return pendingRequestsCount;
} }
/**
* @notice 获取用户待处理的请求
* @param _user 用户地址
* @return pendingRequests 用户待处理的请求详情数组
*/
function getUserPendingRequests(address _user) external view returns (WithdrawRequest[] memory pendingRequests) { function getUserPendingRequests(address _user) external view returns (WithdrawRequest[] memory pendingRequests) {
uint256[] memory requestIds = userRequestIds[_user]; uint256[] memory requestIds = userRequestIds[_user];
// 先计算有多少待处理的请求
uint256 pendingCount = 0; uint256 pendingCount = 0;
for (uint256 i = 0; i < requestIds.length; i++) { for (uint256 i = 0; i < requestIds.length; i++) {
if (!withdrawRequests[requestIds[i]].processed) { if (!withdrawRequests[requestIds[i]].processed) {
@@ -469,7 +324,6 @@ contract YTAssetVault is
} }
} }
// 构造返回数组
pendingRequests = new WithdrawRequest[](pendingCount); pendingRequests = new WithdrawRequest[](pendingCount);
uint256 index = 0; uint256 index = 0;
for (uint256 i = 0; i < requestIds.length; i++) { for (uint256 i = 0; i < requestIds.length; i++) {
@@ -481,13 +335,6 @@ contract YTAssetVault is
} }
} }
/**
* @notice 获取队列处理进度
* @return currentIndex 当前处理到的位置
* @return totalRequests 总请求数
* @return pendingRequests 待处理请求数
* @dev 使用实时维护的计数器,避免循环计算
*/
function getQueueProgress() external view returns ( function getQueueProgress() external view returns (
uint256 currentIndex, uint256 currentIndex,
uint256 totalRequests, uint256 totalRequests,
@@ -498,10 +345,6 @@ contract YTAssetVault is
pendingRequests = pendingRequestsCount; pendingRequests = pendingRequestsCount;
} }
/**
* @notice 查询距离下次赎回开放还需等待多久
* @return remainingTime 剩余时间0表示可以赎回
*/
function getTimeUntilNextRedemption() external view returns (uint256 remainingTime) { function getTimeUntilNextRedemption() external view returns (uint256 remainingTime) {
if (block.timestamp >= nextRedemptionTime) { if (block.timestamp >= nextRedemptionTime) {
return 0; return 0;
@@ -509,19 +352,10 @@ contract YTAssetVault is
return nextRedemptionTime - block.timestamp; return nextRedemptionTime - block.timestamp;
} }
/**
* @notice 检查当前是否可以赎回
* @return 是否可以赎回
*/
function canRedeemNow() external view returns (bool) { function canRedeemNow() external view returns (bool) {
return block.timestamp >= nextRedemptionTime; return block.timestamp >= nextRedemptionTime;
} }
/**
* @notice 提取USDC用于外部投资
* @param _to 接收地址
* @param _amount 提取数量
*/
function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused { function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount(); if (_amount == 0) revert InvalidAmount();
@@ -534,69 +368,40 @@ contract YTAssetVault is
emit AssetsWithdrawn(_to, _amount); emit AssetsWithdrawn(_to, _amount);
} }
/**
* @notice 将管理的资产归还到金库(可以归还更多,产生收益)
* @param _amount 归还数量
*/
function depositManagedAssets(uint256 _amount) external onlyManager nonReentrant whenNotPaused { function depositManagedAssets(uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount(); if (_amount == 0) revert InvalidAmount();
// 先更新状态遵循CEI模式
if (_amount >= managedAssets) { if (_amount >= managedAssets) {
// 归还金额 >= 已管理资产managedAssets归零多余部分是收益
managedAssets = 0; managedAssets = 0;
} else { } else {
// 归还金额 < 已管理资产,部分归还
managedAssets -= _amount; managedAssets -= _amount;
} }
// 从manager转入USDC到合约
IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _amount); IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), _amount);
emit AssetsDeposited(_amount); emit AssetsDeposited(_amount);
} }
/**
* @notice 获取总资产(包含被管理的资产)
* @return 总资产 = 合约余额 + 被管理的资产
*/
function totalAssets() public view returns (uint256) { function totalAssets() public view returns (uint256) {
return IERC20(usdcAddress).balanceOf(address(this)) + managedAssets; return IERC20(usdcAddress).balanceOf(address(this)) + managedAssets;
} }
/**
* @notice 获取空闲资产(可用于提取的资产)
* @return 合约中实际持有的USDC数量
*/
function idleAssets() public view returns (uint256) { function idleAssets() public view returns (uint256) {
return IERC20(usdcAddress).balanceOf(address(this)); return IERC20(usdcAddress).balanceOf(address(this));
} }
/**
* @notice 预览购买计算支付指定USDC可获得的YT数量
* @param _usdcAmount 支付的USDC数量
* @return ytAmount 可获得的YT数量
*/
function previewBuy(uint256 _usdcAmount) external view returns (uint256 ytAmount) { function previewBuy(uint256 _usdcAmount) external view returns (uint256 ytAmount) {
uint256 usdcPrice = _getUSDCPrice(); uint256 usdcPrice = _getUSDCPrice();
uint256 conversionFactor = _getPriceConversionFactor(); uint256 conversionFactor = _getPriceConversionFactor();
ytAmount = (_usdcAmount * usdcPrice * conversionFactor) / ytPrice; ytAmount = (_usdcAmount * usdcPrice * conversionFactor) / ytPrice;
} }
/**
* @notice 预览卖出计算卖出指定YT可获得的USDC数量
* @param _ytAmount 卖出的YT数量
* @return usdcAmount 可获得的USDC数量
*/
function previewSell(uint256 _ytAmount) external view returns (uint256 usdcAmount) { function previewSell(uint256 _ytAmount) external view returns (uint256 usdcAmount) {
uint256 usdcPrice = _getUSDCPrice(); uint256 usdcPrice = _getUSDCPrice();
uint256 conversionFactor = _getPriceConversionFactor(); uint256 conversionFactor = _getPriceConversionFactor();
usdcAmount = (_ytAmount * ytPrice) / (usdcPrice * conversionFactor); usdcAmount = (_ytAmount * ytPrice) / (usdcPrice * conversionFactor);
} }
/**
* @notice 获取金库信息
*/
function getVaultInfo() external view returns ( function getVaultInfo() external view returns (
uint256 _totalAssets, uint256 _totalAssets,
uint256 _idleAssets, uint256 _idleAssets,
@@ -617,9 +422,5 @@ contract YTAssetVault is
_nextRedemptionTime = nextRedemptionTime; _nextRedemptionTime = nextRedemptionTime;
} }
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap; uint256[50] private __gap;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"id":"c1929388b29594e5","source_id_to_path":{"0":"contracts/interfaces/IUSDY.sol","1":"contracts/interfaces/IYTAssetVault.sol","2":"contracts/interfaces/IYTLPToken.sol","3":"contracts/interfaces/IYTPoolManager.sol","4":"contracts/interfaces/IYTPriceFeed.sol","5":"contracts/interfaces/IYTVault.sol","6":"contracts/ytLp/core/YTPoolManager.sol","7":"contracts/ytLp/core/YTPriceFeed.sol","8":"contracts/ytLp/core/YTRewardRouter.sol","9":"contracts/ytLp/core/YTVault.sol","10":"contracts/ytLp/tokens/USDY.sol","11":"contracts/ytLp/tokens/YTLPToken.sol","12":"contracts/ytVault/YTAssetFactory.sol","13":"contracts/ytVault/YTAssetVault.sol","14":"lib/forge-std/src/Base.sol","15":"lib/forge-std/src/StdAssertions.sol","16":"lib/forge-std/src/StdChains.sol","17":"lib/forge-std/src/StdCheats.sol","18":"lib/forge-std/src/StdConstants.sol","19":"lib/forge-std/src/StdError.sol","20":"lib/forge-std/src/StdInvariant.sol","21":"lib/forge-std/src/StdJson.sol","22":"lib/forge-std/src/StdMath.sol","23":"lib/forge-std/src/StdStorage.sol","24":"lib/forge-std/src/StdStyle.sol","25":"lib/forge-std/src/StdToml.sol","26":"lib/forge-std/src/StdUtils.sol","27":"lib/forge-std/src/Test.sol","28":"lib/forge-std/src/Vm.sol","29":"lib/forge-std/src/console.sol","30":"lib/forge-std/src/console2.sol","31":"lib/forge-std/src/interfaces/IMulticall3.sol","32":"lib/forge-std/src/safeconsole.sol","33":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","34":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","35":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","36":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","37":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","38":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","39":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","40":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","41":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","42":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","43":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","44":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","45":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","46":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","47":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","48":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","49":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","50":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","51":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","52":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","53":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","54":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","55":"node_modules/@openzeppelin/contracts/utils/Address.sol","56":"node_modules/@openzeppelin/contracts/utils/Context.sol","57":"node_modules/@openzeppelin/contracts/utils/Errors.sol","58":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","59":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","60":"test/YtLending.t.sol","61":"test/YtLp.t.sol","62":"test/YtVault.t.sol"},"language":"Solidity"} {"id":"47f411e32616926d","source_id_to_path":{"0":"contracts/interfaces/IUSDY.sol","1":"contracts/interfaces/IYTAssetVault.sol","2":"contracts/interfaces/IYTLPToken.sol","3":"contracts/interfaces/IYTPoolManager.sol","4":"contracts/interfaces/IYTPriceFeed.sol","5":"contracts/interfaces/IYTVault.sol","6":"contracts/ytLp/core/YTPoolManager.sol","7":"contracts/ytLp/core/YTPriceFeed.sol","8":"contracts/ytLp/core/YTRewardRouter.sol","9":"contracts/ytLp/core/YTVault.sol","10":"contracts/ytLp/tokens/USDY.sol","11":"contracts/ytLp/tokens/YTLPToken.sol","12":"contracts/ytVault/YTAssetFactory.sol","13":"contracts/ytVault/YTAssetVault.sol","14":"lib/forge-std/src/Base.sol","15":"lib/forge-std/src/StdAssertions.sol","16":"lib/forge-std/src/StdChains.sol","17":"lib/forge-std/src/StdCheats.sol","18":"lib/forge-std/src/StdConstants.sol","19":"lib/forge-std/src/StdError.sol","20":"lib/forge-std/src/StdInvariant.sol","21":"lib/forge-std/src/StdJson.sol","22":"lib/forge-std/src/StdMath.sol","23":"lib/forge-std/src/StdStorage.sol","24":"lib/forge-std/src/StdStyle.sol","25":"lib/forge-std/src/StdToml.sol","26":"lib/forge-std/src/StdUtils.sol","27":"lib/forge-std/src/Test.sol","28":"lib/forge-std/src/Vm.sol","29":"lib/forge-std/src/console.sol","30":"lib/forge-std/src/console2.sol","31":"lib/forge-std/src/interfaces/IMulticall3.sol","32":"lib/forge-std/src/safeconsole.sol","33":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","34":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","35":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","36":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","37":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","38":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","39":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","40":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","41":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","42":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","43":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","44":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","45":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","46":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","47":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","48":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","49":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","50":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","51":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","52":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","53":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","54":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","55":"node_modules/@openzeppelin/contracts/utils/Address.sol","56":"node_modules/@openzeppelin/contracts/utils/Context.sol","57":"node_modules/@openzeppelin/contracts/utils/Errors.sol","58":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","59":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","60":"test/YtLp.t.sol","61":"test/YtVault.t.sol"},"language":"Solidity"}