Files
assetxContracts/test/YtVault.t.sol
2025-12-24 16:41:26 +08:00

1040 lines
34 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/ytVault/YTAssetVault.sol";
import "../contracts/ytVault/YTAssetFactory.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
// Mock USDC token for testing (18 decimals like on BSC)
contract MockUSDC is ERC20 {
constructor() ERC20("USD Coin", "USDC") {
_mint(msg.sender, 100000000 * 1e18); // 铸造1亿USDC用于测试
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
// Mock Chainlink Price Feed
contract MockChainlinkPriceFeed is AggregatorV3Interface {
int256 private _price;
uint8 private _decimals;
constructor(int256 initialPrice) {
_price = initialPrice;
_decimals = 8; // Chainlink standard
}
function decimals() external view override returns (uint8) {
return _decimals;
}
function description() external pure override returns (string memory) {
return "Mock USDC/USD Price Feed";
}
function version() external pure override returns (uint256) {
return 1;
}
function getRoundData(uint80)
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (0, _price, block.timestamp, block.timestamp, 0);
}
function latestRoundData()
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
return (0, _price, block.timestamp, block.timestamp, 0);
}
// Helper function to update price in tests
function updatePrice(int256 newPrice) external {
_price = newPrice;
}
}
contract VaultTest is Test {
YTAssetFactory public factory;
YTAssetVault public vaultImplementation;
YTAssetVault public vault;
MockUSDC public usdc;
MockChainlinkPriceFeed public usdcPriceFeed;
address public owner;
address public manager;
address public user1;
address public user2;
// 常量
uint256 constant PRICE_PRECISION = 1e30;
uint256 constant CHAINLINK_PRECISION = 1e8;
uint256 constant INITIAL_USDC_PRICE = 1e8; // $1.00 in Chainlink format (1e8)
uint256 constant INITIAL_YT_PRICE = 1e30; // 1.0 in PRICE_PRECISION
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 usdcAmount, uint256 ytAmount);
event Sell(address indexed user, uint256 ytAmount, uint256 usdcAmount);
event PriceUpdated(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 usdcAmount, uint256 queueIndex);
event WithdrawRequestProcessed(uint256 indexed requestId, address indexed user, uint256 usdcAmount);
event BatchProcessed(uint256 startIndex, uint256 endIndex, uint256 processedCount, uint256 totalUsdcDistributed);
function setUp() public {
// 设置测试账户
owner = address(this);
manager = makeAddr("manager");
user1 = makeAddr("user1");
user2 = makeAddr("user2");
// 部署Mock USDC (18 decimals)
usdc = new MockUSDC();
// 部署Mock Chainlink Price Feed
usdcPriceFeed = new MockChainlinkPriceFeed(int256(INITIAL_USDC_PRICE));
// 部署实现合约
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));
// 给测试用户分配USDC
usdc.transfer(user1, 100000 * 1e18); // 10万USDC
usdc.transfer(user2, 100000 * 1e18); // 10万USDC
usdc.transfer(manager, 100000 * 1e18); // 10万USDC给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(usdc),
redemptionTime,
INITIAL_YT_PRICE,
address(usdcPriceFeed)
);
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(usdc),
redemptionTime,
INITIAL_YT_PRICE,
address(usdcPriceFeed)
);
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.usdcAddress(), address(usdc));
assertEq(vault.ytPrice(), INITIAL_YT_PRICE);
assertEq(vault.nextRedemptionTime(), redemptionTime);
assertEq(vault.factory(), address(factory));
assertEq(vault.usdcDecimals(), 18); // BSC USDC uses 18 decimals
// 验证factory记录
assertEq(factory.getVaultCount(), 1);
assertTrue(factory.isVault(vaultAddr));
}
function test_03_CreateVaultWithCustomPrice() public {
uint256 customYtPrice = 1020000000000000000000000000000; // 1.02
uint256 redemptionTime = block.timestamp + 60 days;
address vaultAddr = factory.createVault(
"YT-B Token",
"YT-B",
manager,
HARD_CAP,
address(usdc),
redemptionTime,
customYtPrice,
address(usdcPriceFeed)
);
YTAssetVault customVault = YTAssetVault(vaultAddr);
assertEq(customVault.ytPrice(), customYtPrice);
}
function test_04_CreateVaultWithZeroPrice() public {
// 传入0价格应该使用默认值
uint256 redemptionTime = block.timestamp + 30 days;
address vaultAddr = factory.createVault(
"YT-C Token",
"YT-C",
manager,
HARD_CAP,
address(usdc),
redemptionTime,
0, // 使用默认价格
address(usdcPriceFeed)
);
YTAssetVault defaultVault = YTAssetVault(vaultAddr);
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(usdc),
block.timestamp + 30 days,
INITIAL_YT_PRICE,
address(usdcPriceFeed)
);
}
function test_06_CannotCreateVaultWithInvalidPriceFeed() public {
vm.expectRevert(YTAssetVault.InvalidPriceFeed.selector);
factory.createVault(
"YT-E Token",
"YT-E",
manager,
HARD_CAP,
address(usdc),
block.timestamp + 30 days,
INITIAL_YT_PRICE,
address(0) // 无效的价格feed
);
}
function test_07_CreateVaultOnlyOwner() public {
vm.prank(user1);
vm.expectRevert(abi.encodeWithSignature("OwnableUnauthorizedAccount(address)", user1));
factory.createVault(
"YT-E Token",
"YT-E",
manager,
HARD_CAP,
address(usdc),
block.timestamp + 30 days,
INITIAL_YT_PRICE,
address(usdcPriceFeed)
);
}
function test_08_DepositYT() public {
vault = _createVault();
uint256 depositAmount = 1000 * 1e18; // 1000 USDC
uint256 expectedYtAmount = 1000 * 1e18; // 价格1:1获得1000 YT
// 授权
vm.startPrank(user1);
usdc.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(usdc.balanceOf(address(vault)), depositAmount);
assertEq(vault.totalAssets(), depositAmount);
assertEq(vault.idleAssets(), depositAmount);
}
function test_09_DepositYTWithDifferentPrices() public {
vault = _createVault();
// 更新YT价格为 1.02USDC保持 $1.00
factory.updateVaultPrices(
address(vault),
1020000000000000000000000000000 // 1.02
);
uint256 depositAmount = 1000 * 1e18; // 1000 USDC
// ytAmount = 1000 USDC * $1.00 / $1.02 = 980.392156862745098039 YT
// 使用公式: ytAmount = depositAmount * usdcPrice * conversionFactor / ytPrice
// conversionFactor = 10^18 * 10^30 / (10^18 * 10^8) = 10^22
uint256 expectedYtAmount = (depositAmount * INITIAL_USDC_PRICE * 1e22) / 1020000000000000000000000000000;
vm.startPrank(user1);
usdc.approve(address(vault), depositAmount);
uint256 ytReceived = vault.depositYT(depositAmount);
vm.stopPrank();
// 精确验证计算结果
assertEq(ytReceived, expectedYtAmount);
assertEq(ytReceived, 980392156862745098039); // 约980.39 YT
}
function test_10_DepositYTMultipleUsers() public {
vault = _createVault();
uint256 amount1 = 1000 * 1e18;
uint256 amount2 = 2000 * 1e18;
// User1存款
vm.startPrank(user1);
usdc.approve(address(vault), amount1);
vault.depositYT(amount1);
vm.stopPrank();
// User2存款
vm.startPrank(user2);
usdc.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_11_CannotDepositZeroAmount() public {
vault = _createVault();
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InvalidAmount.selector);
vault.depositYT(0);
vm.stopPrank();
}
function test_12_DepositYTHardCapEnforcement() public {
vault = _createVault();
// 尝试存款超过硬顶
uint256 overCapAmount = HARD_CAP + 1000 * 1e18;
vm.startPrank(user1);
usdc.mint(user1, overCapAmount); // 铸造足够的USDC
usdc.approve(address(vault), overCapAmount);
vm.expectRevert(YTAssetVault.HardCapExceeded.selector);
vault.depositYT(overCapAmount);
vm.stopPrank();
}
function test_13_DepositYTExactlyAtHardCap() public {
vault = _createVault();
vm.startPrank(user1);
usdc.mint(user1, HARD_CAP);
usdc.approve(address(vault), HARD_CAP);
vault.depositYT(HARD_CAP);
vm.stopPrank();
assertEq(vault.totalSupply(), HARD_CAP);
assertEq(vault.balanceOf(user1), HARD_CAP);
}
function test_14_WithdrawYT() public {
vault = _createVault();
// 先存款
uint256 depositAmount = 1000 * 1e18;
vm.startPrank(user1);
usdc.approve(address(vault), depositAmount);
vault.depositYT(depositAmount);
vm.stopPrank();
// 快进到赎回时间之后
vm.warp(vault.nextRedemptionTime() + 1);
// 提交提现请求
uint256 withdrawAmount = 500 * 1e18; // 提取500 YT
uint256 expectedUsdc = 500 * 1e18; // 价格1:1获得500 USDC
uint256 user1UsdcBefore = usdc.balanceOf(user1);
vm.startPrank(user1);
vm.expectEmit(true, true, false, true);
emit WithdrawRequestCreated(0, user1, withdrawAmount, expectedUsdc, 0);
uint256 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
// 验证请求创建
assertEq(requestId, 0);
assertEq(vault.balanceOf(user1), depositAmount - withdrawAmount); // YT已销毁
assertEq(vault.totalSupply(), depositAmount - withdrawAmount);
assertEq(usdc.balanceOf(user1), user1UsdcBefore); // USDC还未发放
assertEq(vault.pendingRequestsCount(), 1);
// 批量处理提现请求
vm.prank(manager);
(uint256 processedCount, uint256 totalDistributed) = vault.processBatchWithdrawals(10);
// 验证结果
assertEq(processedCount, 1);
assertEq(totalDistributed, expectedUsdc);
assertEq(usdc.balanceOf(user1), user1UsdcBefore + expectedUsdc); // 现在收到了USDC
assertEq(vault.pendingRequestsCount(), 0);
}
function test_15_WithdrawYTWithDifferentPrices() public {
vault = _createVault();
// 存款
uint256 depositAmount = 1000 * 1e18;
vm.startPrank(user1);
usdc.approve(address(vault), depositAmount);
vault.depositYT(depositAmount);
vm.stopPrank();
// 更新YT价格为 1.05 (YT升值)USDC价格更新为 0.98
factory.updateVaultPrices(
address(vault),
1050000000000000000000000000000 // 1.05
);
usdcPriceFeed.updatePrice(98000000); // $0.98 in Chainlink format
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// 提交提现请求
uint256 withdrawAmount = 500 * 1e18;
// usdcAmount = 500 YT * $1.05 / $0.98 = 535.714285714285714285 USDC
// 使用公式: usdcAmount = ytAmount * ytPrice / (usdcPrice * conversionFactor)
uint256 expectedUsdc = (withdrawAmount * 1050000000000000000000000000000) / (98000000 * 1e22);
uint256 user1BalanceBefore = usdc.balanceOf(user1);
vm.startPrank(user1);
uint256 requestId = vault.withdrawYT(withdrawAmount);
vm.stopPrank();
assertEq(requestId, 0);
// 批量处理
vm.prank(manager);
vault.processBatchWithdrawals(10);
// 验证用户收到的USDC余额增加量
assertEq(usdc.balanceOf(user1), user1BalanceBefore + expectedUsdc);
assertEq(expectedUsdc, 535714285714285714285); // 约535.71 USDC
}
function test_16_CannotWithdrawBeforeRedemptionTime() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
usdc.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
// 尝试在赎回时间前提款
vm.expectRevert(YTAssetVault.StillInLockPeriod.selector);
vault.withdrawYT(500 * 1e18);
vm.stopPrank();
}
function test_17_CannotWithdrawZeroAmount() public {
vault = _createVault();
vm.warp(vault.nextRedemptionTime() + 1);
vm.startPrank(user1);
vm.expectRevert(YTAssetVault.InvalidAmount.selector);
vault.withdrawYT(0);
vm.stopPrank();
}
function test_18_CannotWithdrawMoreThanBalance() public {
vault = _createVault();
vm.startPrank(user1);
usdc.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_19_ProcessStopsWhenInsufficientUSDC() public {
vault = _createVault();
// User1存款
vm.startPrank(user1);
usdc.approve(address(vault), 1000 * 1e18);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
// Manager提取所有USDC
vm.prank(manager);
vault.withdrawForManagement(manager, 1000 * 1e18);
// 快进到赎回时间
vm.warp(vault.nextRedemptionTime() + 1);
// User1可以提交提现请求即使vault中没有USDC
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);
usdc.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_20_UpdatePrices() public {
vault = _createVault();
uint256 newYtPrice = 1020000000000000000000000000000; // 1.02
vm.expectEmit(false, false, false, true);
emit PriceUpdated(newYtPrice, block.timestamp);
factory.updateVaultPrices(address(vault), newYtPrice);
assertEq(vault.ytPrice(), newYtPrice);
}
function test_21_UpdatePricesOnlyFactory() public {
vault = _createVault();
// 测试非factory调用者包括manager无法直接调用
vm.prank(user1);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.updatePrices(1020000000000000000000000000000);
// manager也不能直接调用
vm.prank(manager);
vm.expectRevert(YTAssetVault.Forbidden.selector);
vault.updatePrices(1020000000000000000000000000000);
}
function test_22_CannotUpdatePricesWithZero() public {
vault = _createVault();
vm.expectRevert(YTAssetVault.InvalidPrice.selector);
factory.updateVaultPrices(address(vault), 0);
}
function test_23_WithdrawForManagement() public {
vault = _createVault();
// 先存款
vm.startPrank(user1);
usdc.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取用于投资
uint256 withdrawAmount = 5000 * 1e18;
uint256 managerBalanceBefore = usdc.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(usdc.balanceOf(manager), managerBalanceBefore + withdrawAmount);
}
function test_24_DepositManagedAssetsFullReturn() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
usdc.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
// Manager提取
vm.prank(manager);
vault.withdrawForManagement(manager, 5000 * 1e18);
// Manager归还全部无盈亏
vm.startPrank(manager);
usdc.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_25_DepositManagedAssetsWithProfit() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
usdc.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 USDC
vm.startPrank(manager);
usdc.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_26_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_27_CannotSetHardCapBelowTotalSupply() public {
vault = _createVault();
// 先存款
vm.startPrank(user1);
usdc.approve(address(vault), 100000 * 1e18);
vault.depositYT(100000 * 1e18);
vm.stopPrank();
// 尝试设置低于当前总供应量的硬顶
vm.expectRevert(YTAssetVault.InvalidHardCap.selector);
factory.setHardCap(address(vault), 50000 * 1e18);
}
function test_28_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_29_PauseByFactory() public {
vault = _createVault();
// Factory可以暂停
factory.pauseVault(address(vault));
assertTrue(vault.paused());
// Factory可以恢复
factory.unpauseVault(address(vault));
assertFalse(vault.paused());
}
function test_30_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_31_CannotDepositWhenPaused() public {
vault = _createVault();
// 暂停vault
factory.pauseVault(address(vault));
// 尝试存款应该失败
vm.startPrank(user1);
usdc.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);
}
function test_32_GetVaultInfo() public {
vault = _createVault();
// 存款
vm.startPrank(user1);
usdc.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 usdcPrice,
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(usdcPrice, INITIAL_USDC_PRICE);
assertEq(ytPrice_, INITIAL_YT_PRICE);
assertEq(nextRedemptionTime_, vault.nextRedemptionTime());
}
function test_33_PreviewFunctions() public {
vault = _createVault();
// 更新价格
factory.updateVaultPrices(
address(vault),
1020000000000000000000000000000 // YT = 1.02
);
// 预览买入
uint256 usdcAmount = 1000 * 1e18;
uint256 expectedYt = (usdcAmount * INITIAL_USDC_PRICE * 1e22) / 1020000000000000000000000000000;
uint256 previewBuyAmount = vault.previewBuy(usdcAmount);
assertEq(previewBuyAmount, expectedYt);
assertEq(previewBuyAmount, 980392156862745098039);
// 预览卖出
uint256 ytAmount = 1000 * 1e18;
uint256 expectedUsdc = (ytAmount * 1020000000000000000000000000000) / (INITIAL_USDC_PRICE * 1e22);
uint256 previewSellAmount = vault.previewSell(ytAmount);
assertEq(previewSellAmount, expectedUsdc);
assertEq(previewSellAmount, 1020000000000000000000);
}
function test_34_CanRedeemNow() public {
vault = _createVault();
// 赎回时间前
assertFalse(vault.canRedeemNow());
// 赎回时间后
vm.warp(vault.nextRedemptionTime() + 1);
assertTrue(vault.canRedeemNow());
}
function test_35_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_36_CompleteLifecycle() public {
vault = _createVault();
// 1. 初始状态验证
assertEq(vault.totalSupply(), 0);
assertEq(vault.totalAssets(), 0);
// 2. User1和User2存款
vm.startPrank(user1);
usdc.approve(address(vault), 10000 * 1e18);
vault.depositYT(10000 * 1e18);
vm.stopPrank();
vm.startPrank(user2);
usdc.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. 价格更新YT涨到1.10
factory.updateVaultPrices(
address(vault),
1100000000000000000000000000000 // YT涨到1.10
);
// 5. Manager归还资金+利润
vm.startPrank(manager);
usdc.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 user1UsdcBefore = usdc.balanceOf(user1);
vm.startPrank(user1);
uint256 requestId = vault.withdrawYT(withdrawYtAmount);
vm.stopPrank();
assertEq(requestId, 0);
// 8. 批量处理提现
vm.prank(manager);
vault.processBatchWithdrawals(10);
// 按新价格计算: 5000 YT * $1.10 / $1.00 = 5500 USDC
uint256 expectedUsdc = (withdrawYtAmount * 1100000000000000000000000000000) / (INITIAL_USDC_PRICE * 1e22);
assertEq(usdc.balanceOf(user1), user1UsdcBefore + expectedUsdc);
assertEq(expectedUsdc, 5500000000000000000000);
// 验证最终状态
assertEq(vault.balanceOf(user1), user1YtBalance - withdrawYtAmount);
assertEq(vault.totalSupply(), 10000 * 1e18);
}
function test_37_ChainlinkPriceIntegration() public {
vault = _createVault();
// 测试不同的USDC价格
usdcPriceFeed.updatePrice(105000000); // $1.05
uint256 depositAmount = 1000 * 1e18;
// ytAmount = 1000 * 1.05 / 1.00 = 1050 YT
uint256 expectedYt = (depositAmount * 105000000 * 1e22) / INITIAL_YT_PRICE;
vm.startPrank(user1);
usdc.approve(address(vault), depositAmount);
uint256 ytReceived = vault.depositYT(depositAmount);
vm.stopPrank();
assertEq(ytReceived, expectedYt);
assertEq(ytReceived, 1050 * 1e18);
}
function test_38_ChainlinkNegativePriceReverts() public {
vault = _createVault();
// 设置负价格
usdcPriceFeed.updatePrice(-1);
vm.startPrank(user1);
usdc.approve(address(vault), 1000 * 1e18);
// 应该revert
vm.expectRevert(YTAssetVault.InvalidChainlinkPrice.selector);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
}
function test_39_ChainlinkZeroPriceReverts() public {
vault = _createVault();
// 设置零价格
usdcPriceFeed.updatePrice(0);
vm.startPrank(user1);
usdc.approve(address(vault), 1000 * 1e18);
// 应该revert
vm.expectRevert(YTAssetVault.InvalidChainlinkPrice.selector);
vault.depositYT(1000 * 1e18);
vm.stopPrank();
}
function test_40_BatchProcessWithMultipleRequests() 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)));
usdc.transfer(users[i], 1000 * 1e18);
vm.startPrank(users[i]);
usdc.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);
}
}