user logout in the vault changed to queuing mechanism, and add function of batch sending WUSD
This commit is contained in:
@@ -31,6 +31,9 @@ contract YTAssetVault is
|
||||
error InsufficientWUSD();
|
||||
error InsufficientYTA();
|
||||
error StillInLockPeriod();
|
||||
error RequestNotFound();
|
||||
error RequestAlreadyProcessed();
|
||||
error InvalidBatchSize();
|
||||
|
||||
/// @notice 工厂合约地址
|
||||
address public factory;
|
||||
@@ -59,6 +62,31 @@ contract YTAssetVault is
|
||||
/// @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);
|
||||
@@ -67,6 +95,9 @@ contract YTAssetVault is
|
||||
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();
|
||||
@@ -218,15 +249,16 @@ contract YTAssetVault is
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 卖出YT换取WUSD(需要等到统一赎回时间)
|
||||
* @notice 提交YT提现请求(需要等到统一赎回时间)
|
||||
* @param _ytAmount 卖出的YT数量
|
||||
* @return wusdAmount 实际获得的WUSD数量
|
||||
* @return requestId 提现请求ID
|
||||
* @dev 用户提交请求后,YT会立即销毁,但WUSD需要等待批量处理后才能领取
|
||||
*/
|
||||
function withdrawYT(uint256 _ytAmount)
|
||||
external
|
||||
nonReentrant
|
||||
whenNotPaused
|
||||
returns (uint256 wusdAmount)
|
||||
returns (uint256 requestId)
|
||||
{
|
||||
if (_ytAmount == 0) revert InvalidAmount();
|
||||
if (balanceOf(msg.sender) < _ytAmount) revert InsufficientYTA();
|
||||
@@ -237,19 +269,177 @@ contract YTAssetVault is
|
||||
}
|
||||
|
||||
// 计算可以换取的WUSD数量
|
||||
wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
|
||||
uint256 wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
|
||||
|
||||
// 检查合约是否有足够的WUSD
|
||||
uint256 availableWUSD = IERC20(wusdAddress).balanceOf(address(this));
|
||||
if (wusdAmount > availableWUSD) revert InsufficientWUSD();
|
||||
|
||||
// 销毁YT
|
||||
// 销毁YT代币
|
||||
_burn(msg.sender, _ytAmount);
|
||||
|
||||
// 转出WUSD
|
||||
IERC20(wusdAddress).safeTransfer(msg.sender, wusdAmount);
|
||||
// 创建提现请求
|
||||
requestId = requestIdCounter;
|
||||
withdrawRequests[requestId] = WithdrawRequest({
|
||||
user: msg.sender,
|
||||
ytAmount: _ytAmount,
|
||||
wusdAmount: wusdAmount,
|
||||
requestTime: block.timestamp,
|
||||
queueIndex: requestId,
|
||||
processed: false
|
||||
});
|
||||
|
||||
emit Sell(msg.sender, _ytAmount, wusdAmount);
|
||||
// 记录用户的请求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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user