user logout in the vault changed to queuing mechanism, and add function of batch sending WUSD

This commit is contained in:
2025-12-19 13:26:49 +08:00
parent 21674f86a9
commit 399354646a
27 changed files with 2057 additions and 2877 deletions

View File

@@ -50,6 +50,9 @@ contract VaultTest is Test {
event AssetsDeposited(uint256 amount);
event HardCapSet(uint256 newHardCap);
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);
function setUp() public {
// 设置测试账户
@@ -346,24 +349,35 @@ contract VaultTest is Test {
// 快进到赎回时间之后
vm.warp(vault.nextRedemptionTime() + 1);
// 提
// 提交提现请求
uint256 withdrawAmount = 500 * 1e18; // 提取500 YT
uint256 expectedWusd = 500 * 1e18; // 价格1:1获得500 WUSD
uint256 user1WusdBefore = wusd.balanceOf(user1);
vm.startPrank(user1);
vm.expectEmit(true, false, false, true);
emit Sell(user1, withdrawAmount, expectedWusd);
vm.expectEmit(true, true, false, true);
emit WithdrawRequestCreated(0, user1, withdrawAmount, expectedWusd, 0);
uint256 wusdReceived = vault.withdrawYT(withdrawAmount);
uint256 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
// 验证结果
assertEq(wusdReceived, expectedWusd);
assertEq(vault.balanceOf(user1), depositAmount - withdrawAmount);
// 验证请求创建
assertEq(requestId, 0);
assertEq(vault.balanceOf(user1), depositAmount - withdrawAmount); // YT已销毁
assertEq(vault.totalSupply(), depositAmount - withdrawAmount);
assertEq(wusd.balanceOf(user1), user1WusdBefore + expectedWusd);
assertEq(wusd.balanceOf(user1), user1WusdBefore); // WUSD还未发放
assertEq(vault.pendingRequestsCount(), 1);
// 批量处理提现请求
vm.prank(manager);
(uint256 processedCount, uint256 totalDistributed) = vault.processBatchWithdrawals(10);
// 验证结果
assertEq(processedCount, 1);
assertEq(totalDistributed, expectedWusd);
assertEq(wusd.balanceOf(user1), user1WusdBefore + expectedWusd); // 现在收到了WUSD
assertEq(vault.pendingRequestsCount(), 0);
}
function test_14_WithdrawYTWithDifferentPrices() public {
@@ -386,17 +400,26 @@ contract VaultTest is Test {
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 提款500 YT
// 提交提现请求
uint256 withdrawAmount = 500 * 1e18;
// wusdAmount = 500 * 1.05 / 0.98 = 535.714285714285714285 WUSD
uint256 expectedWusd = (withdrawAmount * 1050000000000000000000000000000) / 980000000000000000000000000000;
uint256 user1BalanceBefore = wusd.balanceOf(user1);
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(withdrawAmount);
uint256 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
assertEq(wusdReceived, expectedWusd);
assertEq(wusdReceived, 535714285714285714285); // 约535.71 WUSD
assertEq(requestId, 0);
// 批量处理
vm.prank(manager);
vault.processBatchWithdrawals(10);
// 验证用户收到的WUSD余额增加量
assertEq(wusd.balanceOf(user1), user1BalanceBefore + expectedWusd);
assertEq(expectedWusd, 535714285714285714285); // 约535.71 WUSD
}
function test_15_CannotWithdrawBeforeRedemptionTime() public {
@@ -440,7 +463,7 @@ contract VaultTest is Test {
vm.stopPrank();
}
function test_18_CannotWithdrawWhenInsufficientWUSD() public {
function test_18_CannotProcessWhenInsufficientWUSD() public {
vault = _createVault();
// User1存款
@@ -456,11 +479,33 @@ contract VaultTest is Test {
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// User1尝试提款,但vault中没有WUSD
// User1可以提交提现请求(即使vault中没有WUSD
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InsufficientWUSD.selector);
vault.withdrawYT(500 * 1e18);
uint256 requestId = vault.withdrawYT(500 * 1e18);
vm.stopPrank();
assertEq(requestId, 0);
assertEq(vault.pendingRequestsCount(), 1);
// 但是批量处理时会因为资金不足而处理0个请求
vm.prank(manager);
(uint256 processedCount, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount, 0); // 没有处理任何请求
assertEq(vault.pendingRequestsCount(), 1); // 请求仍在队列中
// Manager归还资金后可以处理
vm.startPrank(manager);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositManagedAssets(1000 * 1e18);
vm.stopPrank();
// 现在可以处理了
vm.prank(manager);
(uint256 processedCount2, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount2, 1);
assertEq(vault.pendingRequestsCount(), 0);
}
function test_19_UpdatePrices() public {
@@ -895,18 +940,25 @@ contract VaultTest is Test {
// 6. 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 7. User1提取部分YT
// 7. User1提交提现请求
uint256 user1YtBalance = vault.balanceOf(user1);
uint256 withdrawYtAmount = 5000 * 1e18;
uint256 user1WusdBefore = wusd.balanceOf(user1);
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(withdrawYtAmount);
uint256 requestId = vault.withdrawYT(withdrawYtAmount);
vm.stopPrank();
assertEq(requestId, 0);
// 8. 批量处理提现
vm.prank(manager);
vault.processBatchWithdrawals(10);
// 按新价格计算: 5000 * 1.10 / 1.05 = 5238.095238095238095238 WUSD
uint256 expectedWusd = (withdrawYtAmount * 1100000000000000000000000000000) / 1050000000000000000000000000000;
assertEq(wusdReceived, expectedWusd);
assertEq(wusdReceived, 5238095238095238095238);
assertEq(wusd.balanceOf(user1), user1WusdBefore + expectedWusd);
assertEq(expectedWusd, 5238095238095238095238);
// 验证最终状态
assertEq(vault.balanceOf(user1), user1YtBalance - withdrawYtAmount);
@@ -950,22 +1002,32 @@ contract VaultTest is Test {
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// User1提取
uint256 user1WusdBefore = wusd.balanceOf(user1);
uint256 user2WusdBefore = wusd.balanceOf(user2);
// User1提交提现请求
vm.startPrank(user1);
uint256 wusdBack1 = vault.withdrawYT(ytReceived1);
uint256 requestId1 = vault.withdrawYT(ytReceived1);
vm.stopPrank();
// User2提交提现请求
vm.startPrank(user2);
uint256 requestId2 = vault.withdrawYT(ytReceived2);
vm.stopPrank();
assertEq(requestId1, 0);
assertEq(requestId2, 1);
// 批量处理所有请求
vm.prank(manager);
vault.processBatchWithdrawals(10);
// wusdAmount = 10000 * 0.90 / 0.95 = 9473.684210526315789473
assertEq(wusdBack1, 9473684210526315789473);
// User2提取
vm.startPrank(user2);
uint256 wusdBack2 = vault.withdrawYT(ytReceived2);
vm.stopPrank();
assertEq(wusd.balanceOf(user1), user1WusdBefore + 9473684210526315789473);
// wusdAmount = 9166.666... * 0.90 / 0.95 = 8684.210526315789473684
// 允许1 wei的舍入误差
assertApproxEqAbs(wusdBack2, 8684210526315789473684, 1);
assertApproxEqAbs(wusd.balanceOf(user2), user2WusdBefore + 8684210526315789473684, 1);
}
// ==================== 暂停功能测试 ====================
@@ -1037,20 +1099,28 @@ contract VaultTest is Test {
// 暂停vault
factory.pauseVault(address(vault));
// 尝试提应该失败
// 尝试提交提现请求应该失败
vm.startPrank(user1);
vm.expectRevert(abi.encodeWithSignature("EnforcedPause()"));
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
// 恢复后应该可以提
// 恢复后应该可以提交请求和处理
factory.unpauseVault(address(vault));
uint256 user1WusdBefore = wusd.balanceOf(user1);
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(500 * 1e18);
uint256 requestId = vault.withdrawYT(500 * 1e18);
vm.stopPrank();
assertEq(wusdReceived, 500 * 1e18, "withdraw should work after unpause");
assertEq(requestId, 0);
// 批量处理
vm.prank(manager);
vault.processBatchWithdrawals(10);
assertEq(wusd.balanceOf(user1), user1WusdBefore + 500 * 1e18, "withdraw should work after unpause");
}
function test_45_CannotWithdrawForManagementWhenPaused() public {
@@ -1150,4 +1220,382 @@ contract VaultTest is Test {
assertEq(totalAssets, 1000 * 1e18, "getVaultInfo should work");
assertEq(idleAssets, 1000 * 1e18, "getVaultInfo should work");
}
// ==================== 排队提现机制测试 ====================
function test_48_WithdrawQueueBasic() public {
vault = _createVault();
// User1和User2存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.startPrank(user2);
wusd.approve(address(vault), 2000 * 1e18);
vault.depositYT(2000 * 1e18);
vm.stopPrank();
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// User1先提交请求
vm.prank(user1);
uint256 requestId1 = vault.withdrawYT(500 * 1e18);
// User2后提交请求
vm.prank(user2);
uint256 requestId2 = vault.withdrawYT(1000 * 1e18);
assertEq(requestId1, 0);
assertEq(requestId2, 1);
assertEq(vault.pendingRequestsCount(), 2);
// 查询队列进度
(uint256 currentIndex, uint256 totalRequests, uint256 pendingRequests) = vault.getQueueProgress();
assertEq(currentIndex, 0);
assertEq(totalRequests, 2);
assertEq(pendingRequests, 2);
}
function test_49_ProcessBatchWithdrawals() public {
vault = _createVault();
// 3个用户存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.startPrank(user2);
wusd.approve(address(vault), 2000 * 1e18);
vault.depositYT(2000 * 1e18);
vm.stopPrank();
address user3 = makeAddr("user3");
wusd.transfer(user3, 3000 * 1e18);
vm.startPrank(user3);
wusd.approve(address(vault), 3000 * 1e18);
vault.depositYT(3000 * 1e18);
vm.stopPrank();
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
uint256 user1WusdBefore = wusd.balanceOf(user1);
uint256 user2WusdBefore = wusd.balanceOf(user2);
uint256 user3WusdBefore = wusd.balanceOf(user3);
// 提交3个提现请求
vm.prank(user1);
vault.withdrawYT(500 * 1e18);
vm.prank(user2);
vault.withdrawYT(1000 * 1e18);
vm.prank(user3);
vault.withdrawYT(1500 * 1e18);
assertEq(vault.pendingRequestsCount(), 3);
// 批量处理所有请求
vm.prank(manager);
(uint256 processedCount, uint256 totalDistributed) = vault.processBatchWithdrawals(10);
assertEq(processedCount, 3);
assertEq(totalDistributed, 3000 * 1e18);
assertEq(vault.pendingRequestsCount(), 0);
// 验证用户收到WUSD
assertEq(wusd.balanceOf(user1), user1WusdBefore + 500 * 1e18);
assertEq(wusd.balanceOf(user2), user2WusdBefore + 1000 * 1e18);
assertEq(wusd.balanceOf(user3), user3WusdBefore + 1500 * 1e18);
}
function test_50_ProcessBatchWithLimit() public {
vault = _createVault();
// 准备5个用户和请求
address[] memory users = new address[](5);
for (uint i = 0; i < 5; i++) {
users[i] = makeAddr(string(abi.encodePacked("user", i)));
wusd.transfer(users[i], 1000 * 1e18);
vm.startPrank(users[i]);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
}
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 提交5个提现请求
for (uint i = 0; i < 5; i++) {
vm.prank(users[i]);
vault.withdrawYT(500 * 1e18);
}
assertEq(vault.pendingRequestsCount(), 5);
// 第一次批量处理只处理2个
vm.prank(manager);
(uint256 processedCount1, ) = vault.processBatchWithdrawals(2);
assertEq(processedCount1, 2);
assertEq(vault.pendingRequestsCount(), 3);
// 第二次批量处理处理剩余3个
vm.prank(manager);
(uint256 processedCount2, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount2, 3);
assertEq(vault.pendingRequestsCount(), 0);
}
function test_51_ProcessStopsWhenInsufficientFunds() public {
vault = _createVault();
// User1存款1000
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// User2存款2000
vm.startPrank(user2);
wusd.approve(address(vault), 2000 * 1e18);
vault.depositYT(2000 * 1e18);
vm.stopPrank();
// Manager提取大部分资金
vm.prank(manager);
vault.withdrawForManagement(manager, 2500 * 1e18);
// 现在vault只有500 WUSD
assertEq(vault.idleAssets(), 500 * 1e18);
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 两个用户提交提现请求
vm.prank(user1);
vault.withdrawYT(1000 * 1e18); // 需要1000 WUSD
vm.prank(user2);
vault.withdrawYT(2000 * 1e18); // 需要2000 WUSD
// 批量处理只能处理第一个请求500 WUSD不够
vm.prank(manager);
(uint256 processedCount, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount, 0); // 第一个请求需要1000但只有500
assertEq(vault.pendingRequestsCount(), 2);
// Manager归还资金
vm.startPrank(manager);
wusd.approve(address(vault), 2500 * 1e18);
vault.depositManagedAssets(2500 * 1e18);
vm.stopPrank();
// 现在可以处理所有请求
vm.prank(manager);
(uint256 processedCount2, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount2, 2);
assertEq(vault.pendingRequestsCount(), 0);
}
function test_52_GetUserRequestIds() public {
vault = _createVault();
// User1存款并提交多个请求
vm.startPrank(user1);
wusd.approve(address(vault), 3000 * 1e18);
vault.depositYT(3000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
vm.startPrank(user1);
vault.withdrawYT(500 * 1e18);
vault.withdrawYT(1000 * 1e18);
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
// 查询用户的所有请求ID
uint256[] memory userRequests = vault.getUserRequestIds(user1);
assertEq(userRequests.length, 3);
assertEq(userRequests[0], 0);
assertEq(userRequests[1], 1);
assertEq(userRequests[2], 2);
}
function test_53_GetRequestDetails() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
vm.prank(user1);
uint256 requestId = vault.withdrawYT(500 * 1e18);
// 查询请求详情
YTAssetVault.WithdrawRequest memory request = vault.getRequestDetails(requestId);
assertEq(request.user, user1);
assertEq(request.ytAmount, 500 * 1e18);
assertEq(request.wusdAmount, 500 * 1e18);
assertEq(request.queueIndex, 0);
assertFalse(request.processed);
}
function test_54_GetUserPendingRequests() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 3000 * 1e18);
vault.depositYT(3000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
// 提交3个请求
vm.startPrank(user1);
vault.withdrawYT(500 * 1e18);
vault.withdrawYT(1000 * 1e18);
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
// 查询待处理的请求
YTAssetVault.WithdrawRequest[] memory pendingRequests = vault.getUserPendingRequests(user1);
assertEq(pendingRequests.length, 3);
// 处理第一个请求
vm.prank(manager);
vault.processBatchWithdrawals(1);
// 再次查询
YTAssetVault.WithdrawRequest[] memory pendingRequests2 = vault.getUserPendingRequests(user1);
assertEq(pendingRequests2.length, 2);
}
function test_55_FactoryCanProcessWithdrawals() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
vm.prank(user1);
vault.withdrawYT(500 * 1e18);
// Factory也可以调用processBatchWithdrawals
vm.prank(address(factory));
(uint256 processedCount, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount, 1);
}
function test_56_OnlyManagerOrFactoryCanProcess() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
vm.prank(user1);
vault.withdrawYT(500 * 1e18);
// 普通用户不能调用processBatchWithdrawals
vm.prank(user2);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.processBatchWithdrawals(10);
}
function test_57_CannotProcessWithZeroBatchSize() public {
vault = _createVault();
vm.prank(manager);
vm.expectRevert(YTAssetVault.InvalidBatchSize.selector);
vault.processBatchWithdrawals(0);
}
function test_58_FIFOOrderGuarantee() public {
vault = _createVault();
// 3个用户按顺序存款
address user3 = makeAddr("user3");
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.startPrank(user2);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
wusd.transfer(user3, 1000 * 1e18);
vm.startPrank(user3);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// Manager提取资金只留下1500 WUSD
vm.prank(manager);
vault.withdrawForManagement(manager, 1500 * 1e18);
vm.warp(vault.nextRedemptionTime() + 1);
uint256 user1WusdBefore = wusd.balanceOf(user1);
uint256 user2WusdBefore = wusd.balanceOf(user2);
uint256 user3WusdBefore = wusd.balanceOf(user3);
// 按顺序提交请求
vm.prank(user1);
vault.withdrawYT(1000 * 1e18); // requestId = 0, 需要1000 WUSD
vm.prank(user2);
vault.withdrawYT(1000 * 1e18); // requestId = 1, 需要1000 WUSD
vm.prank(user3);
vault.withdrawYT(1000 * 1e18); // requestId = 2, 需要1000 WUSD
// 批量处理只有1500 WUSD应该按FIFO顺序处理
vm.prank(manager);
(uint256 processedCount, ) = vault.processBatchWithdrawals(10);
// 只能处理前1个user1第2个需要1000但只剩500
assertEq(processedCount, 1);
assertEq(wusd.balanceOf(user1), user1WusdBefore + 1000 * 1e18); // 已处理
assertEq(wusd.balanceOf(user2), user2WusdBefore); // 未处理
assertEq(wusd.balanceOf(user3), user3WusdBefore); // 未处理
// 归还资金后继续处理
vm.startPrank(manager);
wusd.approve(address(vault), 1500 * 1e18);
vault.depositManagedAssets(1500 * 1e18);
vm.stopPrank();
// 处理剩余请求
vm.prank(manager);
(uint256 processedCount2, ) = vault.processBatchWithdrawals(10);
assertEq(processedCount2, 2);
assertEq(wusd.balanceOf(user2), user2WusdBefore + 1000 * 1e18); // 现在已处理
assertEq(wusd.balanceOf(user3), user3WusdBefore + 1000 * 1e18); // 现在已处理
}
}