Fix & Optimize contract

This commit is contained in:
2025-12-23 14:05:41 +08:00
parent b927acf3b7
commit d2e9377f78
117 changed files with 745 additions and 191 deletions

View File

@@ -0,0 +1,448 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./YTAssetVault.sol";
/**
* @title YTAssetFactory
* @notice 用于批量创建和管理YT资产金库合约的工厂
* @dev UUPS可升级合约
*/
contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
error InvalidAddress();
error VaultNotExists();
error InvalidHardCap();
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice YTAssetVault实现合约地址
address public vaultImplementation;
/// @notice 所有创建的vault地址列表
address[] public allVaults;
/// @notice vault地址 => 是否存在
mapping(address => bool) public isVault;
/// @notice 默认硬顶值0表示无限制
uint256 public defaultHardCap;
event VaultCreated(
address indexed vault,
address indexed manager,
string name,
string symbol,
uint256 hardCap,
uint256 index
);
event VaultImplementationUpdated(address indexed newImplementation);
event DefaultHardCapSet(uint256 newDefaultHardCap);
event HardCapSet(address indexed vault, uint256 newHardCap);
event PricesUpdated(address indexed vault, uint256 wusdPrice, uint256 ytPrice);
event NextRedemptionTimeSet(address indexed vault, uint256 redemptionTime);
/**
* @notice 初始化工厂
* @param _vaultImplementation YTAssetVault实现合约地址
* @param _defaultHardCap 默认硬顶值
*/
function initialize(
address _vaultImplementation,
uint256 _defaultHardCap
) external initializer {
if (_vaultImplementation == address(0)) revert InvalidAddress();
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
vaultImplementation = _vaultImplementation;
defaultHardCap = _defaultHardCap;
}
/**
* @notice 授权升级仅owner可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
/**
* @notice 更新YTAssetVault实现合约
* @param _newImplementation 新的实现合约地址
*/
function setVaultImplementation(address _newImplementation) external onlyOwner {
if (_newImplementation == address(0)) revert InvalidAddress();
vaultImplementation = _newImplementation;
emit VaultImplementationUpdated(_newImplementation);
}
/**
* @notice 设置默认硬顶
* @param _defaultHardCap 新的默认硬顶值
*/
function setDefaultHardCap(uint256 _defaultHardCap) external onlyOwner {
defaultHardCap = _defaultHardCap;
emit DefaultHardCapSet(_defaultHardCap);
}
/**
* @notice 创建新的YTAssetVault
* @param _name YT代币名称
* @param _symbol YT代币符号
* @param _manager 管理员地址
* @param _hardCap 硬顶限制0表示使用默认值
* @param _wusd WUSD代币地址传0使用默认地址
* @param _redemptionTime 赎回时间Unix时间戳
* @param _initialWusdPrice 初始WUSD价格精度1e30传0则使用默认值1.0
* @param _initialYtPrice 初始YT价格精度1e30传0则使用默认值1.0
* @return vault 新创建的vault地址
*/
function createVault(
string memory _name,
string memory _symbol,
address _manager,
uint256 _hardCap,
address _wusd,
uint256 _redemptionTime,
uint256 _initialWusdPrice,
uint256 _initialYtPrice
) external onlyOwner returns (address vault) {
if (_manager == address(0)) revert InvalidAddress();
// 如果传入0使用默认硬顶
uint256 actualHardCap = _hardCap == 0 ? defaultHardCap : _hardCap;
// 编码初始化数据
bytes memory initData = abi.encodeWithSelector(
YTAssetVault.initialize.selector,
_name,
_symbol,
_manager,
actualHardCap,
_wusd,
_redemptionTime,
_initialWusdPrice,
_initialYtPrice
);
// 部署代理合约
vault = address(new ERC1967Proxy(vaultImplementation, initData));
// 记录vault信息
allVaults.push(vault);
isVault[vault] = true;
emit VaultCreated(
vault,
_manager,
_name,
_symbol,
actualHardCap,
allVaults.length - 1
);
}
/**
* @notice 批量创建vault
* @param _names YT代币名称数组
* @param _symbols YT代币符号数组
* @param _managers 管理员地址数组
* @param _hardCaps 硬顶数组
* @param _wusd WUSD代币地址传0使用默认地址
* @param _redemptionTimes 赎回时间数组Unix时间戳
* @param _initialWusdPrices 初始WUSD价格数组精度1e30
* @param _initialYtPrices 初始YT价格数组精度1e30
* @return vaults 创建的vault地址数组
*/
function createVaultBatch(
string[] memory _names,
string[] memory _symbols,
address[] memory _managers,
uint256[] memory _hardCaps,
address _wusd,
uint256[] memory _redemptionTimes,
uint256[] memory _initialWusdPrices,
uint256[] memory _initialYtPrices
) external returns (address[] memory vaults) {
require(
_names.length == _symbols.length &&
_names.length == _managers.length &&
_names.length == _hardCaps.length &&
_names.length == _redemptionTimes.length &&
_names.length == _initialWusdPrices.length &&
_names.length == _initialYtPrices.length,
"Length mismatch"
);
vaults = new address[](_names.length);
for (uint256 i = 0; i < _names.length; i++) {
vaults[i] = this.createVault(
_names[i],
_symbols[i],
_managers[i],
_hardCaps[i],
_wusd,
_redemptionTimes[i],
_initialWusdPrices[i],
_initialYtPrices[i]
);
}
}
/**
* @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 _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地址
*/
function pauseVault(address _vault) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).pause();
}
/**
* @notice 恢复vault
* @param _vault vault地址
*/
function unpauseVault(address _vault) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).unpause();
}
/**
* @notice 批量暂停vaults
* @param _vaults vault地址数组
*/
function pauseVaultBatch(address[] memory _vaults) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).pause();
}
}
/**
* @notice 批量恢复vaults
* @param _vaults vault地址数组
*/
function unpauseVaultBatch(address[] memory _vaults) external onlyOwner {
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).unpause();
}
}
/**
* @notice 更新vault价格
* @param _vault vault地址
* @param _wusdPrice WUSD价格精度1e30
* @param _ytPrice YT价格精度1e30
*/
function updateVaultPrices(
address _vault,
uint256 _wusdPrice,
uint256 _ytPrice
) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
YTAssetVault(_vault).updatePrices(_wusdPrice, _ytPrice);
emit PricesUpdated(_vault, _wusdPrice, _ytPrice);
}
/**
* @notice 批量更新价格
* @param _vaults vault地址数组
* @param _wusdPrices WUSD价格数组精度1e30
* @param _ytPrices YT价格数组精度1e30
*/
function updateVaultPricesBatch(
address[] memory _vaults,
uint256[] memory _wusdPrices,
uint256[] memory _ytPrices
) external onlyOwner {
require(
_vaults.length == _wusdPrices.length &&
_vaults.length == _ytPrices.length,
"Length mismatch"
);
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).updatePrices(_wusdPrices[i], _ytPrices[i]);
emit PricesUpdated(_vaults[i], _wusdPrices[i], _ytPrices[i]);
}
}
/**
* @notice 升级指定vault
* @param _vault vault地址
* @param _newImplementation 新实现地址
*/
function upgradeVault(address _vault, address _newImplementation) external onlyOwner {
if (!isVault[_vault]) revert VaultNotExists();
if (_newImplementation == address(0)) revert InvalidAddress();
YTAssetVault(_vault).upgradeToAndCall(_newImplementation, "");
}
/**
* @notice 批量升级vault
* @param _vaults vault地址数组
* @param _newImplementation 新实现地址
*/
function upgradeVaultBatch(
address[] memory _vaults,
address _newImplementation
) external onlyOwner {
if (_newImplementation == address(0)) revert InvalidAddress();
for (uint256 i = 0; i < _vaults.length; i++) {
if (!isVault[_vaults[i]]) revert VaultNotExists();
YTAssetVault(_vaults[i]).upgradeToAndCall(_newImplementation, "");
}
}
/**
* @notice 获取所有vault数量
*/
function getVaultCount() external view returns (uint256) {
return allVaults.length;
}
/**
* @notice 获取指定范围的vault地址
* @param _start 起始索引
* @param _end 结束索引(不包含)
*/
function getVaults(uint256 _start, uint256 _end)
external
view
returns (address[] memory vaults)
{
require(_start < _end && _end <= allVaults.length, "Invalid range");
vaults = new address[](_end - _start);
for (uint256 i = _start; i < _end; i++) {
vaults[i - _start] = allVaults[i];
}
}
/**
* @notice 获取所有vault地址
*/
function getAllVaults() external view returns (address[] memory) {
return allVaults;
}
/**
* @notice 获取vault详细信息
* @param _vault vault地址
*/
function getVaultInfo(address _vault) external view returns (
bool exists,
uint256 totalAssets,
uint256 idleAssets,
uint256 managedAssets,
uint256 totalSupply,
uint256 hardCap,
uint256 wusdPrice,
uint256 ytPrice,
uint256 nextRedemptionTime
) {
exists = isVault[_vault];
if (!exists) return (false, 0, 0, 0, 0, 0, 0, 0, 0);
(
totalAssets,
idleAssets,
managedAssets,
totalSupply,
hardCap,
wusdPrice,
ytPrice,
nextRedemptionTime
) = YTAssetVault(_vault).getVaultInfo();
}
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap;
}

View File

@@ -0,0 +1,570 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title YTAssetVault
* @notice 基于价格的资产金库用户根据WUSD和YT代币价格进行兑换
* @dev UUPS可升级合约YT是份额代币
*/
contract YTAssetVault is
Initializable,
ERC20Upgradeable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable,
PausableUpgradeable
{
using SafeERC20 for IERC20;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
error Forbidden();
error HardCapExceeded();
error InvalidAmount();
error InvalidHardCap();
error InvalidPrice();
error InsufficientWUSD();
error InsufficientYTA();
error StillInLockPeriod();
error RequestNotFound();
error RequestAlreadyProcessed();
error InvalidBatchSize();
/// @notice 工厂合约地址
address public factory;
/// @notice 管理员地址
address public manager;
/// @notice YT代币硬顶最大可铸造的YT数量
uint256 public hardCap;
/// @notice 已提取用于管理的WUSD数量
uint256 public managedAssets;
/// @notice WUSD代币地址
address public wusdAddress;
/// @notice WUSD价格精度1e30
uint256 public wusdPrice;
/// @notice YT价格精度1e30
uint256 public ytPrice;
/// @notice 价格精度
uint256 public constant PRICE_PRECISION = 1e30;
/// @notice 下一个赎回开放时间(所有用户统一)
uint256 public nextRedemptionTime;
/// @notice 提现请求结构体
struct WithdrawRequest {
address user; // 用户地址
uint256 ytAmount; // YT数量
uint256 wusdAmount; // 应得WUSD数量
uint256 requestTime; // 请求时间
uint256 queueIndex; // 队列位置
bool processed; // 是否已处理
}
/// @notice 请求ID => 请求详情
mapping(uint256 => WithdrawRequest) public withdrawRequests;
/// @notice 用户地址 => 用户的所有请求ID列表
mapping(address => uint256[]) private userRequestIds;
/// @notice 请求ID计数器
uint256 public requestIdCounter;
/// @notice 已处理到的队列位置
uint256 public processedUpToIndex;
/// @notice 当前待处理的请求数量(实时维护,避免循环计算)
uint256 public pendingRequestsCount;
event HardCapSet(uint256 newHardCap);
event ManagerSet(address indexed newManager);
event AssetsWithdrawn(address indexed to, uint256 amount);
event AssetsDeposited(uint256 amount);
event PriceUpdated(uint256 wusdPrice, uint256 ytPrice, uint256 timestamp);
event Buy(address indexed user, uint256 wusdAmount, uint256 ytAmount);
event Sell(address indexed user, uint256 ytAmount, uint256 wusdAmount);
event NextRedemptionTimeSet(uint256 newRedemptionTime);
event WithdrawRequestCreated(uint256 indexed requestId, address indexed user, uint256 ytAmount, uint256 wusdAmount, uint256 queueIndex);
event WithdrawRequestProcessed(uint256 indexed requestId, address indexed user, uint256 wusdAmount);
event BatchProcessed(uint256 startIndex, uint256 endIndex, uint256 processedCount, uint256 totalWusdDistributed);
modifier onlyFactory() {
if (msg.sender != factory) revert Forbidden();
_;
}
modifier onlyManager() {
if (msg.sender != manager) revert Forbidden();
_;
}
/**
* @notice 初始化金库
* @param _name YT代币名称
* @param _symbol YT代币符号
* @param _manager 管理员地址
* @param _hardCap 硬顶限制
* @param _wusd WUSD代币地址可选传0则使用默认地址
* @param _redemptionTime 赎回时间Unix时间戳
* @param _initialWusdPrice 初始WUSD价格精度1e30传0则使用默认值1.0
* @param _initialYtPrice 初始YT价格精度1e30传0则使用默认值1.0
*
* @dev 价格精度为1e30
*/
function initialize(
string memory _name,
string memory _symbol,
address _manager,
uint256 _hardCap,
address _wusd,
uint256 _redemptionTime,
uint256 _initialWusdPrice,
uint256 _initialYtPrice
) external initializer {
wusdAddress = _wusd == address(0)
? 0x7Cd017ca5ddb86861FA983a34b5F495C6F898c41
: _wusd;
__ERC20_init(_name, _symbol);
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
__Pausable_init();
factory = msg.sender;
manager = _manager;
hardCap = _hardCap;
// 使用传入的初始价格如果为0则使用默认值1.0
wusdPrice = _initialWusdPrice == 0 ? PRICE_PRECISION : _initialWusdPrice;
ytPrice = _initialYtPrice == 0 ? PRICE_PRECISION : _initialYtPrice;
// 设置赎回时间
nextRedemptionTime = _redemptionTime;
}
/**
* @notice 授权升级仅factory可调用
* @param newImplementation 新实现合约地址
*/
function _authorizeUpgrade(address newImplementation) internal override onlyFactory {}
/**
* @notice 设置硬顶
* @param _hardCap 新的硬顶值
*/
function setHardCap(uint256 _hardCap) external onlyFactory {
if (_hardCap < totalSupply()) revert InvalidHardCap();
hardCap = _hardCap;
emit HardCapSet(_hardCap);
}
/**
* @notice 设置管理员
* @param _manager 新管理员地址
*/
function setManager(address _manager) external onlyFactory {
manager = _manager;
emit ManagerSet(_manager);
}
/**
* @notice 暂停合约仅factory可调用
* @dev 暂停后,所有资金流动操作将被禁止
*/
function pause() external onlyFactory {
_pause();
}
/**
* @notice 恢复合约仅factory可调用
*/
function unpause() external onlyFactory {
_unpause();
}
/**
* @notice 设置下一个赎回开放时间仅factory可调用
* @param _nextRedemptionTime 下一个赎回时间Unix时间戳
* @dev 所有用户统一在此时间后才能赎回,类似基金的赎回日
*/
function setNextRedemptionTime(uint256 _nextRedemptionTime) external onlyFactory {
nextRedemptionTime = _nextRedemptionTime;
emit NextRedemptionTimeSet(_nextRedemptionTime);
}
/**
* @notice 更新价格仅manager可调用
* @param _wusdPrice WUSD价格精度1e30
* @param _ytPrice YT价格精度1e30
*/
function updatePrices(uint256 _wusdPrice, uint256 _ytPrice) external onlyFactory {
if (_wusdPrice == 0 || _ytPrice == 0) revert InvalidPrice();
wusdPrice = _wusdPrice;
ytPrice = _ytPrice;
emit PriceUpdated(_wusdPrice, _ytPrice, block.timestamp);
}
/**
* @notice 用WUSD购买YT
* @param _wusdAmount 支付的WUSD数量
* @return ytAmount 实际获得的YT数量
* @dev 首次购买时YT价格 = WUSD价格1:1兑换
*/
function depositYT(uint256 _wusdAmount)
external
nonReentrant
whenNotPaused
returns (uint256 ytAmount)
{
if (_wusdAmount == 0) revert InvalidAmount();
// 计算可以购买的YT数量
ytAmount = (_wusdAmount * wusdPrice) / ytPrice;
// 检查硬顶
if (hardCap > 0 && totalSupply() + ytAmount > hardCap) {
revert HardCapExceeded();
}
// 转入WUSD
IERC20(wusdAddress).safeTransferFrom(msg.sender, address(this), _wusdAmount);
// 铸造YT
_mint(msg.sender, ytAmount);
emit Buy(msg.sender, _wusdAmount, ytAmount);
}
/**
* @notice 提交YT提现请求需要等到统一赎回时间
* @param _ytAmount 卖出的YT数量
* @return requestId 提现请求ID
* @dev 用户提交请求后YT会立即销毁但WUSD需要等待批量处理后才能领取
*/
function withdrawYT(uint256 _ytAmount)
external
nonReentrant
whenNotPaused
returns (uint256 requestId)
{
if (_ytAmount == 0) revert InvalidAmount();
if (balanceOf(msg.sender) < _ytAmount) revert InsufficientYTA();
// 检查是否到达统一赎回时间
if (block.timestamp < nextRedemptionTime) {
revert StillInLockPeriod();
}
// 计算可以换取的WUSD数量
uint256 wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
// 销毁YT代币
_burn(msg.sender, _ytAmount);
// 创建提现请求
requestId = requestIdCounter;
withdrawRequests[requestId] = WithdrawRequest({
user: msg.sender,
ytAmount: _ytAmount,
wusdAmount: wusdAmount,
requestTime: block.timestamp,
queueIndex: requestId,
processed: false
});
// 记录用户的请求ID
userRequestIds[msg.sender].push(requestId);
// 递增计数器
requestIdCounter++;
// 增加待处理请求计数
pendingRequestsCount++;
emit WithdrawRequestCreated(requestId, msg.sender, _ytAmount, wusdAmount, requestId);
}
/**
* @notice 批量处理提现请求仅manager或factory可调用
* @param _batchSize 本批次最多处理的请求数量
* @return processedCount 实际处理的请求数量
* @return totalDistributed 实际分发的WUSD总量
* @dev 按照请求ID顺序即时间先后依次处理遇到资金不足时停止
*/
function processBatchWithdrawals(uint256 _batchSize)
external
nonReentrant
whenNotPaused
returns (uint256 processedCount, uint256 totalDistributed)
{
// 权限检查只有manager或factory可以调用
if (msg.sender != manager && msg.sender != factory) {
revert Forbidden();
}
if (_batchSize == 0) revert InvalidBatchSize();
uint256 availableWUSD = IERC20(wusdAddress).balanceOf(address(this));
uint256 startIndex = processedUpToIndex;
for (uint256 i = processedUpToIndex; i < requestIdCounter && processedCount < _batchSize; i++) {
WithdrawRequest storage request = withdrawRequests[i];
// 跳过已处理的请求
if (request.processed) {
continue;
}
// 检查是否有足够的WUSD
if (availableWUSD >= request.wusdAmount) {
// 转账WUSD给用户
IERC20(wusdAddress).safeTransfer(request.user, request.wusdAmount);
// 标记为已处理
request.processed = true;
// 更新统计
availableWUSD -= request.wusdAmount;
totalDistributed += request.wusdAmount;
processedCount++;
// 减少待处理请求计数
pendingRequestsCount--;
emit WithdrawRequestProcessed(i, request.user, request.wusdAmount);
} else {
// WUSD不足停止处理
break;
}
}
// 更新处理进度(跳到下一个未处理的位置)
if (processedCount > 0) {
// 找到下一个未处理的位置
for (uint256 i = processedUpToIndex; i < requestIdCounter; i++) {
if (!withdrawRequests[i].processed) {
processedUpToIndex = i;
break;
}
// 如果所有请求都已处理完
if (i == requestIdCounter - 1) {
processedUpToIndex = requestIdCounter;
}
}
}
emit BatchProcessed(startIndex, processedUpToIndex, processedCount, totalDistributed);
}
/**
* @notice 查询用户的所有提现请求ID
* @param _user 用户地址
* @return 用户的所有请求ID数组
*/
function getUserRequestIds(address _user) external view returns (uint256[] memory) {
return userRequestIds[_user];
}
/**
* @notice 查询指定请求的详情
* @param _requestId 请求ID
* @return request 请求详情
*/
function getRequestDetails(uint256 _requestId) external view returns (WithdrawRequest memory request) {
if (_requestId >= requestIdCounter) revert RequestNotFound();
return withdrawRequests[_requestId];
}
/**
* @notice 获取待处理的请求数量
* @return 待处理的请求总数
* @dev 使用实时维护的计数器O(1)复杂度避免gas爆炸
*/
function getPendingRequestsCount() external view returns (uint256) {
return pendingRequestsCount;
}
/**
* @notice 获取用户待处理的请求
* @param _user 用户地址
* @return pendingRequests 用户待处理的请求详情数组
*/
function getUserPendingRequests(address _user) external view returns (WithdrawRequest[] memory pendingRequests) {
uint256[] memory requestIds = userRequestIds[_user];
// 先计算有多少待处理的请求
uint256 pendingCount = 0;
for (uint256 i = 0; i < requestIds.length; i++) {
if (!withdrawRequests[requestIds[i]].processed) {
pendingCount++;
}
}
// 构造返回数组
pendingRequests = new WithdrawRequest[](pendingCount);
uint256 index = 0;
for (uint256 i = 0; i < requestIds.length; i++) {
uint256 requestId = requestIds[i];
if (!withdrawRequests[requestId].processed) {
pendingRequests[index] = withdrawRequests[requestId];
index++;
}
}
}
/**
* @notice 获取队列处理进度
* @return currentIndex 当前处理到的位置
* @return totalRequests 总请求数
* @return pendingRequests 待处理请求数
* @dev 使用实时维护的计数器,避免循环计算
*/
function getQueueProgress() external view returns (
uint256 currentIndex,
uint256 totalRequests,
uint256 pendingRequests
) {
currentIndex = processedUpToIndex;
totalRequests = requestIdCounter;
pendingRequests = pendingRequestsCount;
}
/**
* @notice 查询距离下次赎回开放还需等待多久
* @return remainingTime 剩余时间0表示可以赎回
*/
function getTimeUntilNextRedemption() external view returns (uint256 remainingTime) {
if (block.timestamp >= nextRedemptionTime) {
return 0;
}
return nextRedemptionTime - block.timestamp;
}
/**
* @notice 检查当前是否可以赎回
* @return 是否可以赎回
*/
function canRedeemNow() external view returns (bool) {
return block.timestamp >= nextRedemptionTime;
}
/**
* @notice 提取WUSD用于外部投资
* @param _to 接收地址
* @param _amount 提取数量
*/
function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused {
if (_amount == 0) revert InvalidAmount();
uint256 availableAssets = IERC20(wusdAddress).balanceOf(address(this));
if (_amount > availableAssets) revert InvalidAmount();
managedAssets += _amount;
IERC20(wusdAddress).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转入WUSD到合约
IERC20(wusdAddress).safeTransferFrom(msg.sender, address(this), _amount);
emit AssetsDeposited(_amount);
}
/**
* @notice 获取总资产(包含被管理的资产)
* @return 总资产 = 合约余额 + 被管理的资产
*/
function totalAssets() public view returns (uint256) {
return IERC20(wusdAddress).balanceOf(address(this)) + managedAssets;
}
/**
* @notice 获取空闲资产(可用于提取的资产)
* @return 合约中实际持有的WUSD数量
*/
function idleAssets() public view returns (uint256) {
return IERC20(wusdAddress).balanceOf(address(this));
}
/**
* @notice 预览购买计算支付指定WUSD可获得的YT数量
* @param _wusdAmount 支付的WUSD数量
* @return ytAmount 可获得的YT数量
*/
function previewBuy(uint256 _wusdAmount) external view returns (uint256 ytAmount) {
ytAmount = (_wusdAmount * wusdPrice) / ytPrice;
}
/**
* @notice 预览卖出计算卖出指定YT可获得的WUSD数量
* @param _ytAmount 卖出的YT数量
* @return wusdAmount 可获得的WUSD数量
*/
function previewSell(uint256 _ytAmount) external view returns (uint256 wusdAmount) {
wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
}
/**
* @notice 获取金库信息
*/
function getVaultInfo() external view returns (
uint256 _totalAssets,
uint256 _idleAssets,
uint256 _managedAssets,
uint256 _totalSupply,
uint256 _hardCap,
uint256 _wusdPrice,
uint256 _ytPrice,
uint256 _nextRedemptionTime
) {
_totalAssets = totalAssets();
_idleAssets = idleAssets();
_managedAssets = managedAssets;
_totalSupply = totalSupply();
_hardCap = hardCap;
_wusdPrice = wusdPrice;
_ytPrice = ytPrice;
_nextRedemptionTime = nextRedemptionTime;
}
/**
* @dev 预留存储空间,用于未来升级时添加新的状态变量
* 50个slot = 50 * 32 bytes = 1600 bytes
*/
uint256[50] private __gap;
}