Files
assetxContracts/test/Vault.t.sol
2025-12-18 13:07:35 +08:00

1154 lines
38 KiB
Solidity
Raw 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/vault/YTAssetVault.sol";
import "../contracts/vault/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);
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 {
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, false, false, true);
emit Sell(user1, withdrawAmount, expectedWusd);
uint256 wusdReceived = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
// 验证结果
assertEq(wusdReceived, expectedWusd);
assertEq(vault.balanceOf(user1), depositAmount - withdrawAmount);
assertEq(vault.totalSupply(), depositAmount - withdrawAmount);
assertEq(wusd.balanceOf(user1), user1WusdBefore + expectedWusd);
}
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);
// 提款500 YT
uint256 withdrawAmount = 500 * 1e18;
// wusdAmount = 500 * 1.05 / 0.98 = 535.714285714285714285 WUSD
uint256 expectedWusd = (withdrawAmount * 1050000000000000000000000000000) / 980000000000000000000000000000;
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
assertEq(wusdReceived, expectedWusd);
assertEq(wusdReceived, 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_CannotWithdrawWhenInsufficientWUSD() 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);
vm.expectRevert(YTAssetVault.InsufficientWUSD.selector);
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
}
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提取部分YT
uint256 user1YtBalance = vault.balanceOf(user1);
uint256 withdrawYtAmount = 5000 * 1e18;
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(withdrawYtAmount);
vm.stopPrank();
// 按新价格计算: 5000 * 1.10 / 1.05 = 5238.095238095238095238 WUSD
uint256 expectedWusd = (withdrawYtAmount * 1100000000000000000000000000000) / 1050000000000000000000000000000;
assertEq(wusdReceived, expectedWusd);
assertEq(wusdReceived, 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);
// User1提取
vm.startPrank(user1);
uint256 wusdBack1 = vault.withdrawYT(ytReceived1);
vm.stopPrank();
// wusdAmount = 10000 * 0.90 / 0.95 = 9473.684210526315789473
assertEq(wusdBack1, 9473684210526315789473);
// User2提取
vm.startPrank(user2);
uint256 wusdBack2 = vault.withdrawYT(ytReceived2);
vm.stopPrank();
// wusdAmount = 9166.666... * 0.90 / 0.95 = 8684.210526315789473684
// 允许1 wei的舍入误差
assertApproxEqAbs(wusdBack2, 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));
vm.startPrank(user1);
uint256 wusdReceived = vault.withdrawYT(500 * 1e18);
vm.stopPrank();
assertEq(wusdReceived, 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");
}
}