Skip to content

Commit

Permalink
Add more tests. (#22)
Browse files Browse the repository at this point in the history
* test: add more test for `totalAssets()`.

* feat: add modifiers to simulate gain or loss assets in ARM.

* fix: use boolean instead of int256.

* fix: use Foundry stdError lib for expectRevert.

* test: add setter test for LCP.

* chore: fix lcov name in .gitignore

* test: add tests for collectFees().
  • Loading branch information
clement-ux authored Sep 25, 2024
1 parent f1c9ee4 commit 5130d6c
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies/
soldeer.lock

# Coverage
lock.info*
lcov.info*

# Defender Actions
dist
Expand Down
67 changes: 67 additions & 0 deletions test/fork/LidoFixedPriceMultiLpARM/CollectFees.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

// Test imports
import {Fork_Shared_Test_} from "test/fork/shared/Shared.sol";

// Contracts
import {IERC20} from "contracts/Interfaces.sol";
import {PerformanceFee} from "contracts/PerformanceFee.sol";

contract Fork_Concrete_LidoFixedPriceMultiLpARM_CollectFees_Test_ is Fork_Shared_Test_ {
//////////////////////////////////////////////////////
/// --- SETUP
//////////////////////////////////////////////////////
function setUp() public override {
super.setUp();
}

//////////////////////////////////////////////////////
/// --- REVERTING TESTS
//////////////////////////////////////////////////////
/// @notice This test is expected to revert as almost all the liquidity is in stETH
function test_RevertWhen_CollectFees_Because_InsufficientLiquidity()
public
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT, address(steth), true)
{
vm.expectRevert("ARM: insufficient liquidity");
lidoFixedPriceMulltiLpARM.collectFees();
}

//////////////////////////////////////////////////////
/// --- PASSING TESTS
//////////////////////////////////////////////////////
function test_CollectFees_Once()
public
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT, address(weth), true)
{
address feeCollector = lidoFixedPriceMulltiLpARM.feeCollector();
uint256 fee = DEFAULT_AMOUNT * 20 / 100;

// Expected Events
vm.expectEmit({emitter: address(weth)});
emit IERC20.Transfer(address(lidoFixedPriceMulltiLpARM), feeCollector, fee);
vm.expectEmit({emitter: address(lidoFixedPriceMulltiLpARM)});
emit PerformanceFee.FeeCollected(feeCollector, fee);

// Main call
uint256 claimedFee = lidoFixedPriceMulltiLpARM.collectFees();

// Assertions after
assertEq(claimedFee, fee);
assertEq(lidoFixedPriceMulltiLpARM.feesAccrued(), 0);
}

function test_CollectFees_Twice()
public
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT, address(weth), true)
collectFeesOnLidoFixedPriceMultiLpARM
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT, address(weth), true)
{
// Main call
uint256 claimedFee = lidoFixedPriceMulltiLpARM.collectFees();

// Assertions after
assertEq(claimedFee, DEFAULT_AMOUNT * 20 / 100); // This test should pass!
}
}
25 changes: 25 additions & 0 deletions test/fork/LidoFixedPriceMultiLpARM/Setters.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {Fork_Shared_Test_} from "test/fork/shared/Shared.sol";
import {IERC20} from "contracts/Interfaces.sol";
import {MultiLP} from "contracts/MultiLP.sol";
import {PerformanceFee} from "contracts/PerformanceFee.sol";
import {LiquidityProviderControllerARM} from "contracts/LiquidityProviderControllerARM.sol";

contract Fork_Concrete_lidoFixedPriceMulltiLpARM_Setters_Test_ is Fork_Shared_Test_ {
//////////////////////////////////////////////////////
Expand Down Expand Up @@ -150,4 +151,28 @@ contract Fork_Concrete_lidoFixedPriceMulltiLpARM_Setters_Test_ is Fork_Shared_Te
vm.expectRevert("ARM: Only owner can call this function.");
lidoFixedPriceMulltiLpARM.setOperator(address(0));
}

//////////////////////////////////////////////////////
/// --- LIQUIIDITY PROVIDER CONTROLLER - REVERTING TESTS
//////////////////////////////////////////////////////
function test_RevertWhen_LiquidityProviderController_SetLiquidityProvider_Because_NotOwner()
public
asRandomAddress
{
vm.expectRevert("ARM: Only owner can call this function.");
lidoFixedPriceMulltiLpARM.setLiquidityProviderController(address(0));
}

//////////////////////////////////////////////////////
/// --- LIQUIIDITY PROVIDER CONTROLLER - PASSING TESTS
//////////////////////////////////////////////////////
function test_LiquidityProviderController_SetLiquidityProvider() public asLidoFixedPriceMultiLpARMOwner {
address newLiquidityProviderController = vm.randomAddress();

vm.expectEmit({emitter: address(lidoFixedPriceMulltiLpARM)});
emit LiquidityProviderControllerARM.LiquidityProviderControllerUpdated(newLiquidityProviderController);
lidoFixedPriceMulltiLpARM.setLiquidityProviderController(newLiquidityProviderController);

assertEq(lidoFixedPriceMulltiLpARM.liquidityProviderController(), newLiquidityProviderController);
}
}
52 changes: 52 additions & 0 deletions test/fork/LidoFixedPriceMultiLpARM/TotalAssets.t.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

// Foundry
import {stdError} from "forge-std/StdError.sol";

// Test imports
import {Fork_Shared_Test_} from "test/fork/shared/Shared.sol";

Expand All @@ -24,6 +27,23 @@ contract Fork_Concrete_LidoFixedPriceMultiLpARM_TotalAssets_Test_ is Fork_Shared

// Approve STETH for Lido
lidoFixedPriceMulltiLpARM.approveStETH();

deal(address(weth), address(this), 1_000 ether);
weth.approve(address(lidoFixedPriceMulltiLpARM), type(uint256).max);
}

//////////////////////////////////////////////////////
/// --- REVERTING TEST
//////////////////////////////////////////////////////
function test_RevertWhen_TotalAssets_Because_MathError()
public
depositInLidoFixedPriceMultiLpARM(address(this), DEFAULT_AMOUNT)
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT, address(weth), true)
requestRedeemFromLidoFixedPriceMultiLpARM(address(this), DEFAULT_AMOUNT)
simulateAssetGainInLidoFixedPriceMultiLpARM(DEFAULT_AMOUNT * 2, address(weth), false)
{
vm.expectRevert(stdError.arithmeticError);
lidoFixedPriceMulltiLpARM.totalAssets();
}

//////////////////////////////////////////////////////
Expand Down Expand Up @@ -133,4 +153,36 @@ contract Fork_Concrete_LidoFixedPriceMultiLpARM_TotalAssets_Test_ is Fork_Shared
// Check total assets after withdrawal is the same as before
assertApproxEqAbs(lidoFixedPriceMulltiLpARM.totalAssets(), totalAssetsBefore, STETH_ERROR_ROUNDING);
}

function test_TotalAssets_With_FeeAccrued_NotNull() public {
uint256 assetGain = DEFAULT_AMOUNT;
// Simulate asset gain
deal(
address(weth),
address(lidoFixedPriceMulltiLpARM),
weth.balanceOf(address(lidoFixedPriceMulltiLpARM)) + assetGain
);

// User deposit, this will trigger a fee calculation
lidoFixedPriceMulltiLpARM.deposit(DEFAULT_AMOUNT);

// Assert fee accrued is not null
assertEq(lidoFixedPriceMulltiLpARM.feesAccrued(), assetGain * 20 / 100);

assertEq(
lidoFixedPriceMulltiLpARM.totalAssets(),
MIN_TOTAL_SUPPLY + DEFAULT_AMOUNT + assetGain - assetGain * 20 / 100
);
}

function test_TotalAssets_When_ARMIsInsolvent()
public
depositInLidoFixedPriceMultiLpARM(address(this), DEFAULT_AMOUNT)
requestRedeemFromLidoFixedPriceMultiLpARM(address(this), DEFAULT_AMOUNT)
{
// Simulate a loss of assets
deal(address(weth), address(lidoFixedPriceMulltiLpARM), DEFAULT_AMOUNT - 1);

assertEq(lidoFixedPriceMulltiLpARM.totalAssets(), 0);
}
}
40 changes: 40 additions & 0 deletions test/fork/utils/Modifiers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {VmSafe} from "forge-std/Vm.sol";
import {Helpers} from "test/fork/utils/Helpers.sol";
import {MockCall} from "test/fork/utils/MockCall.sol";

// Contracts
import {IERC20} from "contracts/Interfaces.sol";

abstract contract Modifiers is Helpers {
/// @notice Impersonate Alice.
modifier asAlice() {
Expand Down Expand Up @@ -72,6 +75,7 @@ abstract contract Modifiers is Helpers {
_;
}

/// @notice Deposit WETH into the LidoFixedPriceMultiLpARM contract.
modifier depositInLidoFixedPriceMultiLpARM(address user, uint256 amount) {
// Todo: extend this logic to other modifier if needed
(VmSafe.CallerMode mode, address _address, address _origin) = vm.readCallers();
Expand Down Expand Up @@ -113,6 +117,7 @@ abstract contract Modifiers is Helpers {
_;
}

/// @notice Claim redeem from LidoFixedPriceMultiLpARM contract.
modifier claimRequestOnLidoFixedPriceMultiLpARM(address user, uint256 requestId) {
// Todo: extend this logic to other modifier if needed
(VmSafe.CallerMode mode, address _address, address _origin) = vm.readCallers();
Expand All @@ -130,6 +135,41 @@ abstract contract Modifiers is Helpers {
_;
}

/// @notice Simulate asset gain or loss in LidoFixedPriceMultiLpARM contract.
modifier simulateAssetGainInLidoFixedPriceMultiLpARM(uint256 assetGain, address token, bool gain) {
// Todo: extend this logic to other modifier if needed
(VmSafe.CallerMode mode, address _address, address _origin) = vm.readCallers();
vm.stopPrank();

if (gain) {
deal(
token,
address(lidoFixedPriceMulltiLpARM),
IERC20(token).balanceOf(address(lidoFixedPriceMulltiLpARM)) + uint256(assetGain)
);
} else {
deal(
token,
address(lidoFixedPriceMulltiLpARM),
IERC20(token).balanceOf(address(lidoFixedPriceMulltiLpARM)) - uint256(assetGain)
);
}

if (mode == VmSafe.CallerMode.Prank) {
vm.prank(_address, _origin);
} else if (mode == VmSafe.CallerMode.RecurrentPrank) {
vm.startPrank(_address, _origin);
}
_;
}

/// @notice Collect fees on LidoFixedPriceMultiLpARM contract.
modifier collectFeesOnLidoFixedPriceMultiLpARM() {
lidoFixedPriceMulltiLpARM.collectFees();
_;
}

/// @notice Skip time by a given delay.
modifier skipTime(uint256 delay) {
skip(delay);
_;
Expand Down

0 comments on commit 5130d6c

Please sign in to comment.