Files
assetxContracts/test/YtVault.t.sol
2025-12-23 14:05:41 +08:00

1602 lines
53 KiB
Solidity
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../contracts/ytVault/YTAssetVault.sol";
import "../contracts/ytVault/YTAssetFactory.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
// Mock WUSD token for testing
contract MockWUSD is ERC20 {
constructor() ERC20("Wrapped USD", "WUSD") {
_mint(msg.sender, 10000000 * 1e18); // 铸造1000万WUSD用于测试
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract VaultTest is Test {
YTAssetFactory public factory;
YTAssetVault public vaultImplementation;
YTAssetVault public vault;
MockWUSD public wusd;
address public owner;
address public manager;
address public user1;
address public user2;
// 常量
uint256 constant PRICE_PRECISION = 1e30;
uint256 constant INITIAL_WUSD_PRICE = 1e30; // 1.0
uint256 constant INITIAL_YT_PRICE = 1e30; // 1.0
uint256 constant HARD_CAP = 1000000 * 1e18; // 100万YT
event VaultCreated(
address indexed vault,
address indexed manager,
string name,
string symbol,
uint256 hardCap,
uint256 index
);
event Buy(address indexed user, uint256 wusdAmount, uint256 ytAmount);
event Sell(address indexed user, uint256 ytAmount, uint256 wusdAmount);
event PriceUpdated(uint256 wusdPrice, uint256 ytPrice, uint256 timestamp);
event AssetsWithdrawn(address indexed to, uint256 amount);
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 {
// 设置测试账户
owner = address(this);
manager = makeAddr("manager");
user1 = makeAddr("user1");
user2 = makeAddr("user2");
// 部署Mock WUSD
wusd = new MockWUSD();
// 部署实现合约
vaultImplementation = new YTAssetVault();
// 部署并初始化Factory
YTAssetFactory factoryImpl = new YTAssetFactory();
bytes memory factoryInitData = abi.encodeWithSelector(
YTAssetFactory.initialize.selector,
address(vaultImplementation),
HARD_CAP // 默认硬顶
);
ERC1967Proxy factoryProxy = new ERC1967Proxy(address(factoryImpl), factoryInitData);
factory = YTAssetFactory(address(factoryProxy));
// 给测试用户分配WUSD
wusd.transfer(user1, 100000 * 1e18); // 10万WUSD
wusd.transfer(user2, 100000 * 1e18); // 10万WUSD
wusd.transfer(manager, 100000 * 1e18); // 10万WUSD给manager
}
function _createVault() internal returns (YTAssetVault) {
uint256 redemptionTime = block.timestamp + 30 days;
address vaultAddr = factory.createVault(
"YT-A Token",
"YT-A",
manager,
HARD_CAP,
address(wusd),
redemptionTime,
INITIAL_WUSD_PRICE,
INITIAL_YT_PRICE
);
return YTAssetVault(vaultAddr);
}
function test_01_FactoryInitialization() public view {
assertEq(factory.vaultImplementation(), address(vaultImplementation));
assertEq(factory.defaultHardCap(), HARD_CAP);
assertEq(factory.owner(), owner);
}
function test_02_CreateVault() public {
uint256 redemptionTime = block.timestamp + 30 days;
vm.expectEmit(false, true, false, true);
emit VaultCreated(
address(0), // vault地址未知所以用0
manager,
"YT-A Token",
"YT-A",
HARD_CAP,
0 // 第一个vault索引为0
);
address vaultAddr = factory.createVault(
"YT-A Token",
"YT-A",
manager,
HARD_CAP,
address(wusd),
redemptionTime,
INITIAL_WUSD_PRICE,
INITIAL_YT_PRICE
);
vault = YTAssetVault(vaultAddr);
// 验证vault基本信息
assertEq(vault.name(), "YT-A Token");
assertEq(vault.symbol(), "YT-A");
assertEq(vault.manager(), manager);
assertEq(vault.hardCap(), HARD_CAP);
assertEq(vault.wusdAddress(), address(wusd));
assertEq(vault.wusdPrice(), INITIAL_WUSD_PRICE);
assertEq(vault.ytPrice(), INITIAL_YT_PRICE);
assertEq(vault.nextRedemptionTime(), redemptionTime);
assertEq(vault.factory(), address(factory));
// 验证factory记录
assertEq(factory.getVaultCount(), 1);
assertTrue(factory.isVault(vaultAddr));
}
function test_03_CreateVaultWithCustomPrices() public {
uint256 customWusdPrice = 1050000000000000000000000000000; // 1.05
uint256 customYtPrice = 1020000000000000000000000000000; // 1.02
uint256 redemptionTime = block.timestamp + 60 days;
address vaultAddr = factory.createVault(
"YT-B Token",
"YT-B",
manager,
HARD_CAP,
address(wusd),
redemptionTime,
customWusdPrice,
customYtPrice
);
YTAssetVault customVault = YTAssetVault(vaultAddr);
assertEq(customVault.wusdPrice(), customWusdPrice);
assertEq(customVault.ytPrice(), customYtPrice);
}
function test_04_CreateVaultWithZeroPrices() public {
// 传入0价格应该使用默认值
uint256 redemptionTime = block.timestamp + 30 days;
address vaultAddr = factory.createVault(
"YT-C Token",
"YT-C",
manager,
HARD_CAP,
address(wusd),
redemptionTime,
0, // 使用默认价格
0 // 使用默认价格
);
YTAssetVault defaultVault = YTAssetVault(vaultAddr);
assertEq(defaultVault.wusdPrice(), PRICE_PRECISION); // 1.0
assertEq(defaultVault.ytPrice(), PRICE_PRECISION); // 1.0
}
function test_05_CannotCreateVaultWithZeroManager() public {
vm.expectRevert(YTAssetFactory.InvalidAddress.selector);
factory.createVault(
"YT-D Token",
"YT-D",
address(0), // 无效的manager地址
HARD_CAP,
address(wusd),
block.timestamp + 30 days,
INITIAL_WUSD_PRICE,
INITIAL_YT_PRICE
);
}
function test_06_CreateVaultOnlyOwner() public {
vm.prank(user1);
vm.expectRevert(abi.encodeWithSignature("OwnableUnauthorizedAccount(address)", user1));
factory.createVault(
"YT-E Token",
"YT-E",
manager,
HARD_CAP,
address(wusd),
block.timestamp + 30 days,
INITIAL_WUSD_PRICE,
INITIAL_YT_PRICE
);
}
function test_07_DepositYT() public {
vault = _createVault();
uint256 depositAmount = 1000 * 1e18; // 1000 WUSD
uint256 expectedYtAmount = 1000 * 1e18; // 价格1:1获得1000 YT
// 授权
vm.startPrank(user1);
wusd.approve(address(vault), depositAmount);
// 预览购买
uint256 previewAmount = vault.previewBuy(depositAmount);
assertEq(previewAmount, expectedYtAmount);
// 存款
vm.expectEmit(true, false, false, true);
emit Buy(user1, depositAmount, expectedYtAmount);
uint256 ytReceived = vault.depositYT(depositAmount);
vm.stopPrank();
// 验证结果
assertEq(ytReceived, expectedYtAmount);
assertEq(vault.balanceOf(user1), expectedYtAmount);
assertEq(vault.totalSupply(), expectedYtAmount);
assertEq(wusd.balanceOf(address(vault)), depositAmount);
assertEq(vault.totalAssets(), depositAmount);
assertEq(vault.idleAssets(), depositAmount);
}
function test_08_DepositYTWithDifferentPrices() public {
vault = _createVault();
// 更新价格: WUSD = 1.05, YT = 1.02
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000, // 1.05
1020000000000000000000000000000 // 1.02
);
uint256 depositAmount = 1000 * 1e18; // 1000 WUSD
// ytAmount = 1000 * 1.05 / 1.02 = 1029.411764705882352941 YT
uint256 expectedYtAmount = (depositAmount * 1050000000000000000000000000000) / 1020000000000000000000000000000;
vm.startPrank(user1);
wusd.approve(address(vault), depositAmount);
uint256 ytReceived = vault.depositYT(depositAmount);
vm.stopPrank();
// 精确验证计算结果
assertEq(ytReceived, expectedYtAmount);
assertEq(ytReceived, 1029411764705882352941); // 约1029.41 YT
}
function test_09_DepositYTMultipleUsers() public {
vault = _createVault();
uint256 amount1 = 1000 * 1e18;
uint256 amount2 = 2000 * 1e18;
// User1存款
vm.startPrank(user1);
wusd.approve(address(vault), amount1);
vault.depositYT(amount1);
vm.stopPrank();
// User2存款
vm.startPrank(user2);
wusd.approve(address(vault), amount2);
vault.depositYT(amount2);
vm.stopPrank();
// 验证余额
assertEq(vault.balanceOf(user1), amount1);
assertEq(vault.balanceOf(user2), amount2);
assertEq(vault.totalSupply(), amount1 + amount2);
assertEq(vault.totalAssets(), amount1 + amount2);
}
function test_10_CannotDepositZeroAmount() public {
vault = _createVault();
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InvalidAmount.selector);
vault.depositYT(0);
vm.stopPrank();
}
function test_11_DepositYTHardCapEnforcement() public {
vault = _createVault();
// 尝试存款超过硬顶
uint256 overCapAmount = HARD_CAP + 1000 * 1e18;
vm.startPrank(user1);
wusd.mint(user1, overCapAmount); // 铸造足够的WUSD
wusd.approve(address(vault), overCapAmount);
vm.expectRevert(YTAssetVault.HardCapExceeded.selector);
vault.depositYT(overCapAmount);
vm.stopPrank();
}
function test_12_DepositYTExactlyAtHardCap() public {
vault = _createVault();
vm.startPrank(user1);
wusd.mint(user1, HARD_CAP);
wusd.approve(address(vault), HARD_CAP);
vault.depositYT(HARD_CAP);
vm.stopPrank();
assertEq(vault.totalSupply(), HARD_CAP);
assertEq(vault.balanceOf(user1), HARD_CAP);
}
function test_13_WithdrawYT() public {
vault = _createVault();
// 先存款
uint256 depositAmount = 1000 * 1e18;
vm.startPrank(user1);
wusd.approve(address(vault), depositAmount);
vault.depositYT(depositAmount);
vm.stopPrank();
// 快进到赎回时间之后
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, true, false, true);
emit WithdrawRequestCreated(0, user1, withdrawAmount, expectedWusd, 0);
uint256 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
// 验证请求创建
assertEq(requestId, 0);
assertEq(vault.balanceOf(user1), depositAmount - withdrawAmount); // YT已销毁
assertEq(vault.totalSupply(), depositAmount - withdrawAmount);
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 {
vault = _createVault();
// 存款
uint256 depositAmount = 1000 * 1e18;
vm.startPrank(user1);
wusd.approve(address(vault), depositAmount);
vault.depositYT(depositAmount);
vm.stopPrank();
// 更新价格: WUSD = 0.98, YT = 1.05 (YT升值)
factory.updateVaultPrices(
address(vault),
980000000000000000000000000000, // 0.98
1050000000000000000000000000000 // 1.05
);
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 提交提现请求
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 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
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 {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
// 尝试在赎回时间前提款
vm.expectRevert(YTAssetVault.StillInLockPeriod.selector);
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
}
function test_16_CannotWithdrawZeroAmount() public {
vault = _createVault();
vm.warp(vault.nextRedemptionTime() + 1);
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InvalidAmount.selector);
vault.withdrawYT(0);
vm.stopPrank();
}
function test_17_CannotWithdrawMoreThanBalance() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.warp(vault.nextRedemptionTime() + 1);
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InsufficientYTA.selector);
vault.withdrawYT(2000 * 1e18);
vm.stopPrank();
}
function test_18_CannotProcessWhenInsufficientWUSD() public {
vault = _createVault();
// User1存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// Manager提取所有WUSD
vm.prank(manager);
vault.withdrawForManagement(manager, 1000 * 1e18);
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// User1可以提交提现请求即使vault中没有WUSD
vm.startPrank(user1);
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 {
vault = _createVault();
uint256 newWusdPrice = 1050000000000000000000000000000; // 1.05
uint256 newYtPrice = 1020000000000000000000000000000; // 1.02
vm.expectEmit(false, false, false, true);
emit PriceUpdated(newWusdPrice, newYtPrice, block.timestamp);
factory.updateVaultPrices(address(vault), newWusdPrice, newYtPrice);
assertEq(vault.wusdPrice(), newWusdPrice);
assertEq(vault.ytPrice(), newYtPrice);
}
function test_20_UpdatePricesMultipleTimes() public {
vault = _createVault();
// 第一次更新
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000,
1020000000000000000000000000000
);
assertEq(vault.wusdPrice(), 1050000000000000000000000000000);
assertEq(vault.ytPrice(), 1020000000000000000000000000000);
// 第二次更新
factory.updateVaultPrices(
address(vault),
1030000000000000000000000000000,
1010000000000000000000000000000
);
assertEq(vault.wusdPrice(), 1030000000000000000000000000000);
assertEq(vault.ytPrice(), 1010000000000000000000000000000);
}
function test_21_UpdatePricesOnlyFactory() public {
vault = _createVault();
// 测试非factory调用者包括manager无法直接调用
vm.prank(user1);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.updatePrices(1050000000000000000000000000000, 1020000000000000000000000000000);
// manager也不能直接调用
vm.prank(manager);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.updatePrices(1050000000000000000000000000000, 1020000000000000000000000000000);
}
function test_22_UpdatePricesFactoryCanCall() public {
vault = _createVault();
// Factory也可以调用updatePrices通过factory
vm.prank(owner);
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000,
1020000000000000000000000000000
);
assertEq(vault.wusdPrice(), 1050000000000000000000000000000);
assertEq(vault.ytPrice(), 1020000000000000000000000000000);
}
function test_23_CannotUpdatePricesWithZero() public {
vault = _createVault();
vm.expectRevert(YTAssetVault.InvalidPrice.selector);
factory.updateVaultPrices(address(vault), 0, 1020000000000000000000000000000);
vm.expectRevert(YTAssetVault.InvalidPrice.selector);
factory.updateVaultPrices(address(vault), 1050000000000000000000000000000, 0);
}
function test_24_WithdrawForManagement() public {
vault = _createVault();
// 先存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取用于投资
uint256 withdrawAmount = 5000 * 1e18;
uint256 managerBalanceBefore = wusd.balanceOf(manager);
vm.expectEmit(true, false, false, true);
emit AssetsWithdrawn(manager, withdrawAmount);
vm.prank(manager);
vault.withdrawForManagement(manager, withdrawAmount);
// 验证
assertEq(vault.managedAssets(), withdrawAmount);
assertEq(vault.idleAssets(), 5000 * 1e18);
assertEq(vault.totalAssets(), 10000 * 1e18); // totalAssets = idle + managed
assertEq(wusd.balanceOf(manager), managerBalanceBefore + withdrawAmount);
}
function test_25_DepositManagedAssetsFullReturn() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取
vm.prank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
// Manager归还全部无盈亏
vm.startPrank(manager);
wusd.approve(address(vault), 5000 * 1e18);
vm.expectEmit(false, false, false, true);
emit AssetsDeposited(5000 * 1e18);
vault.depositManagedAssets(5000 * 1e18);
vm.stopPrank();
// 验证
assertEq(vault.managedAssets(), 0);
assertEq(vault.idleAssets(), 10000 * 1e18);
assertEq(vault.totalAssets(), 10000 * 1e18);
}
function test_26_DepositManagedAssetsWithProfit() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取
vm.prank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
// Manager归还本金+利润
uint256 returnAmount = 6000 * 1e18; // 赚了1000 WUSD
vm.startPrank(manager);
wusd.approve(address(vault), returnAmount);
vault.depositManagedAssets(returnAmount);
vm.stopPrank();
// 验证
assertEq(vault.managedAssets(), 0);
assertEq(vault.idleAssets(), 11000 * 1e18); // 5000 + 6000
assertEq(vault.totalAssets(), 11000 * 1e18); // 增加了1000的利润
}
function test_27_DepositManagedAssetsPartialReturn() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取
vm.prank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
// Manager部分归还
uint256 returnAmount = 3000 * 1e18;
vm.startPrank(manager);
wusd.approve(address(vault), returnAmount);
vault.depositManagedAssets(returnAmount);
vm.stopPrank();
// 验证
assertEq(vault.managedAssets(), 2000 * 1e18); // 还有2000在外面
assertEq(vault.idleAssets(), 8000 * 1e18); // 5000 + 3000
assertEq(vault.totalAssets(), 10000 * 1e18); // 总资产不变
}
function test_28_WithdrawForManagementOnlyManager() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.prank(user1);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.withdrawForManagement(user1, 500 * 1e18);
}
function test_29_CannotWithdrawMoreThanAvailable() public {
vault = _createVault();
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
vm.prank(manager);
vm.expectRevert(YTAssetVault.InvalidAmount.selector);
vault.withdrawForManagement(manager, 2000 * 1e18);
}
function test_30_SetHardCap() public {
vault = _createVault();
uint256 newHardCap = 2000000 * 1e18;
vm.expectEmit(false, false, false, true);
emit HardCapSet(newHardCap);
factory.setHardCap(address(vault), newHardCap);
assertEq(vault.hardCap(), newHardCap);
}
function test_31_CannotSetHardCapBelowTotalSupply() public {
vault = _createVault();
// 先存款
vm.startPrank(user1);
wusd.approve(address(vault), 100000 * 1e18);
vault.depositYT(100000 * 1e18);
vm.stopPrank();
// 尝试设置低于当前总供应量的硬顶
vm.expectRevert(YTAssetVault.InvalidHardCap.selector);
factory.setHardCap(address(vault), 50000 * 1e18);
}
function test_32_SetNextRedemptionTime() public {
vault = _createVault();
uint256 newRedemptionTime = block.timestamp + 90 days;
vm.expectEmit(false, false, false, true);
emit NextRedemptionTimeSet(newRedemptionTime);
factory.setVaultNextRedemptionTime(address(vault), newRedemptionTime);
assertEq(vault.nextRedemptionTime(), newRedemptionTime);
}
function test_33_BatchUpdatePrices() public {
// 创建多个vault
address vault1 = factory.createVault(
"YT-A", "YT-A", manager, HARD_CAP, address(wusd),
block.timestamp + 30 days, INITIAL_WUSD_PRICE, INITIAL_YT_PRICE
);
address vault2 = factory.createVault(
"YT-B", "YT-B", manager, HARD_CAP, address(wusd),
block.timestamp + 30 days, INITIAL_WUSD_PRICE, INITIAL_YT_PRICE
);
address[] memory vaults = new address[](2);
vaults[0] = vault1;
vaults[1] = vault2;
uint256[] memory wusdPrices = new uint256[](2);
wusdPrices[0] = 1050000000000000000000000000000;
wusdPrices[1] = 1030000000000000000000000000000;
uint256[] memory ytPrices = new uint256[](2);
ytPrices[0] = 1020000000000000000000000000000;
ytPrices[1] = 1010000000000000000000000000000;
factory.updateVaultPricesBatch(vaults, wusdPrices, ytPrices);
assertEq(YTAssetVault(vault1).wusdPrice(), wusdPrices[0]);
assertEq(YTAssetVault(vault1).ytPrice(), ytPrices[0]);
assertEq(YTAssetVault(vault2).wusdPrice(), wusdPrices[1]);
assertEq(YTAssetVault(vault2).ytPrice(), ytPrices[1]);
}
function test_34_GetVaultInfo() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取部分资金
vm.prank(manager);
vault.withdrawForManagement(manager, 3000 * 1e18);
(
uint256 totalAssets,
uint256 idleAssets,
uint256 managedAssets,
uint256 totalSupply,
uint256 hardCap,
uint256 wusdPrice,
uint256 ytPrice,
uint256 nextRedemptionTime
) = vault.getVaultInfo();
assertEq(totalAssets, 10000 * 1e18);
assertEq(idleAssets, 7000 * 1e18);
assertEq(managedAssets, 3000 * 1e18);
assertEq(totalSupply, 10000 * 1e18);
assertEq(hardCap, HARD_CAP);
assertEq(wusdPrice, INITIAL_WUSD_PRICE);
assertEq(ytPrice, INITIAL_YT_PRICE);
assertEq(nextRedemptionTime, vault.nextRedemptionTime());
}
function test_35_GetFactoryVaultInfo() public {
vault = _createVault();
(
bool exists,
uint256 totalAssets,
,, // idleAssets, managedAssets
, // totalSupply
uint256 hardCap,
,, // wusdPrice, ytPrice
// nextRedemptionTime
) = factory.getVaultInfo(address(vault));
assertTrue(exists);
assertEq(totalAssets, 0);
assertEq(hardCap, HARD_CAP);
}
function test_36_PreviewFunctions() public {
vault = _createVault();
// 更新价格
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000, // WUSD = 1.05
1020000000000000000000000000000 // YT = 1.02
);
// 预览买入
uint256 wusdAmount = 1000 * 1e18;
uint256 expectedYt = (wusdAmount * 1050000000000000000000000000000) / 1020000000000000000000000000000;
uint256 previewBuyAmount = vault.previewBuy(wusdAmount);
assertEq(previewBuyAmount, expectedYt);
assertEq(previewBuyAmount, 1029411764705882352941);
// 预览卖出
uint256 ytAmount = 1000 * 1e18;
uint256 expectedWusd = (ytAmount * 1020000000000000000000000000000) / 1050000000000000000000000000000;
uint256 previewSellAmount = vault.previewSell(ytAmount);
assertEq(previewSellAmount, expectedWusd);
assertEq(previewSellAmount, 971428571428571428571);
}
function test_37_CanRedeemNow() public {
vault = _createVault();
// 赎回时间前
assertFalse(vault.canRedeemNow());
// 赎回时间后
vm.warp(vault.nextRedemptionTime() + 1);
assertTrue(vault.canRedeemNow());
}
function test_38_GetTimeUntilNextRedemption() public {
vault = _createVault();
uint256 redemptionTime = vault.nextRedemptionTime();
uint256 currentTime = block.timestamp;
assertEq(vault.getTimeUntilNextRedemption(), redemptionTime - currentTime);
// 快进到赎回时间后
vm.warp(redemptionTime + 1);
assertEq(vault.getTimeUntilNextRedemption(), 0);
}
function test_39_CompleteLifecycle() public {
vault = _createVault();
// 1. 初始状态验证
assertEq(vault.totalSupply(), 0);
assertEq(vault.totalAssets(), 0);
// 2. User1和User2存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
vm.startPrank(user2);
wusd.approve(address(vault), 5000 * 1e18);
vault.depositYT(5000 * 1e18);
vm.stopPrank();
assertEq(vault.totalSupply(), 15000 * 1e18);
assertEq(vault.totalAssets(), 15000 * 1e18);
// 3. Manager提取资金进行投资
vm.prank(manager);
vault.withdrawForManagement(manager, 8000 * 1e18);
assertEq(vault.managedAssets(), 8000 * 1e18);
assertEq(vault.idleAssets(), 7000 * 1e18);
assertEq(vault.totalAssets(), 15000 * 1e18);
// 4. 价格更新(模拟市场变化)
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000, // WUSD涨到1.05
1100000000000000000000000000000 // YT涨到1.10
);
// 5. Manager归还资金+利润
vm.startPrank(manager);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositManagedAssets(10000 * 1e18); // 归还本金+2000利润
vm.stopPrank();
assertEq(vault.managedAssets(), 0);
assertEq(vault.idleAssets(), 17000 * 1e18); // 增加了2000利润
assertEq(vault.totalAssets(), 17000 * 1e18);
// 6. 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 7. User1提交提现请求
uint256 user1YtBalance = vault.balanceOf(user1);
uint256 withdrawYtAmount = 5000 * 1e18;
uint256 user1WusdBefore = wusd.balanceOf(user1);
vm.startPrank(user1);
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(wusd.balanceOf(user1), user1WusdBefore + expectedWusd);
assertEq(expectedWusd, 5238095238095238095238);
// 验证最终状态
assertEq(vault.balanceOf(user1), user1YtBalance - withdrawYtAmount);
assertEq(vault.totalSupply(), 10000 * 1e18);
}
function test_40_PriceFluctuationScenario() public {
vault = _createVault();
// 初始存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
uint256 ytReceived1 = vault.depositYT(10000 * 1e18);
vm.stopPrank();
assertEq(ytReceived1, 10000 * 1e18); // 1:1
// 价格上涨
factory.updateVaultPrices(
address(vault),
1100000000000000000000000000000, // 1.10
1200000000000000000000000000000 // 1.20
);
// User2此时存款会获得更少的YT
vm.startPrank(user2);
wusd.approve(address(vault), 10000 * 1e18);
uint256 ytReceived2 = vault.depositYT(10000 * 1e18);
vm.stopPrank();
// ytAmount = 10000 * 1.10 / 1.20 = 9166.666666666666666666
assertEq(ytReceived2, 9166666666666666666666);
// 价格下跌
factory.updateVaultPrices(
address(vault),
950000000000000000000000000000, // 0.95
900000000000000000000000000000 // 0.90
);
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
uint256 user1WusdBefore = wusd.balanceOf(user1);
uint256 user2WusdBefore = wusd.balanceOf(user2);
// User1提交提现请求
vm.startPrank(user1);
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(wusd.balanceOf(user1), user1WusdBefore + 9473684210526315789473);
// wusdAmount = 9166.666... * 0.90 / 0.95 = 8684.210526315789473684
// 允许1 wei的舍入误差
assertApproxEqAbs(wusd.balanceOf(user2), user2WusdBefore + 8684210526315789473684, 1);
}
// ==================== 暂停功能测试 ====================
function test_41_PauseByFactory() public {
vault = _createVault();
// Factory可以暂停
factory.pauseVault(address(vault));
assertTrue(vault.paused(), "vault should be paused");
// Factory可以恢复
factory.unpauseVault(address(vault));
assertFalse(vault.paused(), "vault should be unpaused");
}
function test_42_OnlyFactoryCanPause() public {
vault = _createVault();
// User不能暂停
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.pause();
vm.stopPrank();
// Manager也不能暂停
vm.startPrank(manager);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.pause();
vm.stopPrank();
}
function test_43_CannotDepositWhenPaused() public {
vault = _createVault();
// 暂停vault
factory.pauseVault(address(vault));
// 尝试存款应该失败
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vm.expectRevert(abi.encodeWithSignature("EnforcedPause()"));
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// 恢复后应该可以存款
factory.unpauseVault(address(vault));
vm.startPrank(user1);
uint256 ytReceived = vault.depositYT(1000 * 1e18);
vm.stopPrank();
assertEq(ytReceived, 1000 * 1e18, "deposit should work after unpause");
}
function test_44_CannotWithdrawWhenPaused() public {
vault = _createVault();
// 先存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 暂停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 requestId = vault.withdrawYT(500 * 1e18);
vm.stopPrank();
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 {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// 暂停vault
factory.pauseVault(address(vault));
// Manager尝试提取应该失败
vm.startPrank(manager);
vm.expectRevert(abi.encodeWithSignature("EnforcedPause()"));
vault.withdrawForManagement(manager, 5000 * 1e18);
vm.stopPrank();
// 恢复后应该可以提取
factory.unpauseVault(address(vault));
vm.startPrank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
vm.stopPrank();
assertEq(vault.managedAssets(), 5000 * 1e18, "withdraw for management should work after unpause");
}
function test_46_CannotDepositManagedAssetsWhenPaused() public {
vault = _createVault();
// 存款并提取
vm.startPrank(user1);
wusd.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
vm.startPrank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
vm.stopPrank();
// 暂停vault
factory.pauseVault(address(vault));
// Manager尝试归还应该失败
vm.startPrank(manager);
wusd.approve(address(vault), 5000 * 1e18);
vm.expectRevert(abi.encodeWithSignature("EnforcedPause()"));
vault.depositManagedAssets(5000 * 1e18);
vm.stopPrank();
// 恢复后应该可以归还
factory.unpauseVault(address(vault));
vm.startPrank(manager);
vault.depositManagedAssets(5000 * 1e18);
vm.stopPrank();
assertEq(vault.managedAssets(), 0, "deposit managed assets should work after unpause");
}
function test_47_QueryFunctionsWorkWhenPaused() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
wusd.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// 暂停vault
factory.pauseVault(address(vault));
// 查询函数应该仍然可用
assertEq(vault.totalSupply(), 1000 * 1e18, "totalSupply should work");
assertEq(vault.balanceOf(user1), 1000 * 1e18, "balanceOf should work");
assertEq(vault.totalAssets(), 1000 * 1e18, "totalAssets should work");
assertEq(vault.idleAssets(), 1000 * 1e18, "idleAssets should work");
// 预览函数应该可用
uint256 previewBuy = vault.previewBuy(100 * 1e18);
assertEq(previewBuy, 100 * 1e18, "previewBuy should work");
uint256 previewSell = vault.previewSell(100 * 1e18);
assertEq(previewSell, 100 * 1e18, "previewSell should work");
// getVaultInfo应该可用
(
uint256 totalAssets,
uint256 idleAssets,
,,,,,
) = vault.getVaultInfo();
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); // 现在已处理
}
}