Skip to content

Commit

Permalink
add unit test for RedemptionAssetsVault
Browse files Browse the repository at this point in the history
  • Loading branch information
danoctavian committed Oct 31, 2024
1 parent 44fec2a commit 8e95cd8
Showing 1 changed file with 272 additions and 0 deletions.
272 changes: 272 additions & 0 deletions test/unit/RedemptionAssetsVault_unit.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// SPDX-License-Identifier: BSD 3-Clause
pragma solidity ^0.8.24;

import {Test} from "forge-std/Test.sol";
import {RedemptionAssetsVault} from "src/ynEIGEN/RedemptionAssetsVault.sol";
import {IAssetRegistry} from "src/interfaces/IAssetRegistry.sol";
import {IynEigen} from "src/interfaces/IynEigen.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {TransparentUpgradeableProxy} from "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {ETH_ASSET} from "src/Constants.sol";

contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}

contract MockAssetRegistry is IAssetRegistry {
mapping(address => bool) public supportedAssets;
IERC20[] public assets;

mapping(address => uint256) public assetRates;

function addAsset(IERC20 asset) external {
supportedAssets[address(asset)] = true;
assets.push(asset);
// Default rate of 1:1
assetRates[address(asset)] = 1e18;
}

function setAssetRate(IERC20 asset, uint256 rate) external {
require(supportedAssets[address(asset)], "Asset not supported");
assetRates[address(asset)] = rate;
}

function assetIsSupported(IERC20 asset) external view returns (bool) {
return supportedAssets[address(asset)];
}

function getAssets() external view returns (IERC20[] memory) {
return assets;
}

function convertToUnitOfAccount(IERC20 asset, uint256 amount) external view returns (uint256) {
require(supportedAssets[address(asset)], "Asset not supported");
return (amount * assetRates[address(asset)]) / 1e18;
}

function convertFromUnitOfAccount(IERC20 asset, uint256 amount) external view returns (uint256) {
require(supportedAssets[address(asset)], "Asset not supported");
return (amount * 1e18) / assetRates[address(asset)];
}

function assetData(IERC20) external pure returns (AssetData memory) {
revert("unexpected call to assetData");
}

function disableAsset(IERC20) external pure {
revert("unexpected call to disableAsset");
}

function deleteAsset(IERC20) external pure {
revert("unexpected call to deleteAsset");
}

function totalAssets() external view returns (uint256) {
return assets.length;
}
}

contract RedemptionAssetsVaultUnitTest is Test {
RedemptionAssetsVault public vault;
MockAssetRegistry public assetRegistry;
MockERC20 public token1;
MockERC20 public token2;
MockERC20 public token3;
IynEigen public mockYnEigen;

address public admin = address(0x1);
address public redeemer = address(0x2);

function setUp() public {
// Deploy mock tokens
token1 = new MockERC20("Mock Token 1", "MT1");
token2 = new MockERC20("Mock Token 2", "MT2");
token3 = new MockERC20("Mock Token 3", "MT3");

// Deploy and setup asset registry
assetRegistry = new MockAssetRegistry();
assetRegistry.addAsset(IERC20(address(token1)));
assetRegistry.addAsset(IERC20(address(token2)));
assetRegistry.addAsset(IERC20(address(token3)));

mockYnEigen = IynEigen(address(0x3));

// Deploy vault implementation and proxy
RedemptionAssetsVault vaultImplementation = new RedemptionAssetsVault();
TransparentUpgradeableProxy vaultProxy = new TransparentUpgradeableProxy(
address(vaultImplementation),
admin,
""
);
vault = RedemptionAssetsVault(address(vaultProxy));

// Initialize vault
RedemptionAssetsVault.Init memory init = RedemptionAssetsVault.Init({
admin: admin,
redeemer: redeemer,
ynEigen: mockYnEigen,
assetRegistry: IAssetRegistry(address(assetRegistry))
});

vault.initialize(init);
}

function test_deposit(uint256 depositAmount) public {
vm.assume(depositAmount > 0 && depositAmount <= 1000 ether);

// Mint tokens to this contract
token1.mint(address(this), depositAmount);

// Approve vault to spend tokens
token1.approve(address(vault), depositAmount);

// Initial balance should be 0
assertEq(vault.balances(address(token1)), 0);

// Deposit tokens
vault.deposit(depositAmount, address(token1));

// Check vault balance is updated
assertEq(vault.balances(address(token1)), depositAmount);

// Check tokens were transferred
assertEq(token1.balanceOf(address(vault)), depositAmount);
}

function test_deposit_revertIfAssetNotSupported() public {
uint256 depositAmount = 1 ether;
MockERC20 unsupportedToken = new MockERC20("Unsupported Token", "UT");

// Mint tokens to this contract
unsupportedToken.mint(address(this), depositAmount);

// Approve vault to spend tokens
unsupportedToken.approve(address(vault), depositAmount);

// Try to deposit unsupported token
vm.expectRevert(RedemptionAssetsVault.AssetNotSupported.selector);
vault.deposit(depositAmount, address(unsupportedToken));
}

function test_previewClaim_multipleAssets(
uint256 amount1,
uint256 amount2,
uint256 amount3,
uint256 claimAmount
) public {
vm.assume(amount1 > 0 && amount1 <= 1000 ether);
vm.assume(amount2 > 0 && amount2 <= 1000 ether);
vm.assume(amount3 > 0 && amount3 <= 1000 ether);

uint256 totalAmount = amount1 + amount2 + amount3;
vm.assume(claimAmount > 0 && claimAmount <= totalAmount);

token1.mint(address(this), amount1);
token2.mint(address(this), amount2);
token3.mint(address(this), amount3);

token1.approve(address(vault), amount1);
token2.approve(address(vault), amount2);
token3.approve(address(vault), amount3);

vault.deposit(amount1, address(token1));
vault.deposit(amount2, address(token2));
vault.deposit(amount3, address(token3));

// Preview claim for claim amount
(IERC20[] memory assets, uint256[] memory amounts) = vault.previewClaim(claimAmount);

// Should return amounts based on available balances
assertEq(assets.length, 3);
assertEq(amounts.length, 3);

uint256 remainingClaim = claimAmount;
if (amount1 >= remainingClaim) {
assertEq(amounts[0], remainingClaim);
assertEq(amounts[1], 0);
assertEq(amounts[2], 0);
} else {
assertEq(amounts[0], amount1);
remainingClaim -= amount1;
if (amount2 >= remainingClaim) {
assertEq(amounts[1], remainingClaim);
assertEq(amounts[2], 0);
} else {
assertEq(amounts[1], amount2);
remainingClaim -= amount2;
assertEq(amounts[2], remainingClaim);
}
}
}

function test_previewClaim_revertInsufficientBalance() public {
uint256 depositAmount = 5 ether;
uint256 claimAmount = 10 ether;

// Deposit token1
token1.mint(address(this), depositAmount);
token1.approve(address(vault), depositAmount);
vault.deposit(depositAmount, address(token1));

// Try to preview claim for more than available
vm.expectRevert(abi.encodeWithSelector(
RedemptionAssetsVault.InsufficientAssetBalance.selector,
ETH_ASSET,
claimAmount,
depositAmount
));
vault.previewClaim(claimAmount);
}

function test_transferRedemptionAssets_matchesPreview(
uint256 amount1,
uint256 amount2,
uint256 amount3,
uint256 claimAmount
) public {
vm.assume(amount1 > 0 && amount1 <= 1000 ether);
vm.assume(amount2 > 0 && amount2 <= 1000 ether);
vm.assume(amount3 > 0 && amount3 <= 1000 ether);

uint256 totalAmount = amount1 + amount2 + amount3;
vm.assume(claimAmount > 0 && claimAmount <= totalAmount);

token1.mint(address(this), amount1);
token2.mint(address(this), amount2);
token3.mint(address(this), amount3);

token1.approve(address(vault), amount1);
token2.approve(address(vault), amount2);
token3.approve(address(vault), amount3);

vault.deposit(amount1, address(token1));
vault.deposit(amount2, address(token2));
vault.deposit(amount3, address(token3));


// Get preview of claim
(IERC20[] memory previewAssets, uint256[] memory previewAmounts) = vault.previewClaim(claimAmount);

// Do actual withdrawal
vm.prank(redeemer);
vault.transferRedemptionAssets(redeemer, claimAmount, "");


// Get actual withdrawal amounts by checking redeemer balances
uint256[] memory withdrawnAmounts = new uint256[](3);
withdrawnAmounts[0] = token1.balanceOf(redeemer);
withdrawnAmounts[1] = token2.balanceOf(redeemer);
withdrawnAmounts[2] = token3.balanceOf(redeemer);

assertEq(withdrawnAmounts.length, previewAmounts.length, "Amount array lengths should match");

for(uint256 i = 0; i < withdrawnAmounts.length; i++) {
assertEq(withdrawnAmounts[i], previewAmounts[i], "Withdrawn amounts should match preview");
}
}
}

0 comments on commit 8e95cd8

Please sign in to comment.