From d83cd90ca5b9b0c676f134db677de9b4b2408db6 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 7 Sep 2023 16:28:04 +0200 Subject: [PATCH 1/6] fix: some isues --- contracts/SupplyVault.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/SupplyVault.sol b/contracts/SupplyVault.sol index a0542e8a..0e6e2c74 100644 --- a/contracts/SupplyVault.sol +++ b/contracts/SupplyVault.sol @@ -354,7 +354,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { for (uint256 i; i < length; ++i) { Id id = supplyAllocationOrder[i]; - MarketParams memory marketParams = _config.at(_config.getMarket(id).rank); + MarketParams memory marketParams = _config.at(_config.getMarket(id).rank - 1); uint256 cap = marketCap(id); uint256 toDeposit = assets; @@ -425,9 +425,9 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { view returns (MarketParams memory marketParams, uint256 withdrawable) { - marketParams = _config.at(_config.getMarket(id).rank); + marketParams = _config.at(_config.getMarket(id).rank - 1); (uint256 totalSupply,, uint256 totalBorrow,) = MORPHO.expectedMarketBalances(marketParams); - uint256 available = totalBorrow - totalSupply; + uint256 available = totalSupply - totalBorrow; withdrawable = UtilsLib.min(available, assets); } From 064a2ac0a759de65440a8862a7f02be0527dfad1 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 7 Sep 2023 19:10:47 +0200 Subject: [PATCH 2/6] test: add tests one market --- test/forge/BaseTest.sol | 7 +- test/forge/VaultTest.sol | 171 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 test/forge/VaultTest.sol diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index 1cf64c01..c0f9a5a3 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -15,7 +15,7 @@ contract BaseTest is Test { using MarketParamsLib for MarketParams; uint256 internal constant HIGH_COLLATERAL_AMOUNT = 1e35; - uint256 internal constant MIN_TEST_AMOUNT = 100; + uint256 internal constant MIN_TEST_AMOUNT = 1e8; uint256 internal constant MAX_TEST_AMOUNT = 1e28; uint256 internal constant MIN_TEST_SHARES = MIN_TEST_AMOUNT * SharesMathLib.VIRTUAL_SHARES; uint256 internal constant MAX_TEST_SHARES = MAX_TEST_AMOUNT * SharesMathLib.VIRTUAL_SHARES; @@ -102,6 +102,11 @@ contract BaseTest is Test { vm.stopPrank(); vm.warp(block.timestamp + 1); + + deal(address(borrowableToken), SUPPLIER, type(uint256).max); + + vm.prank(SUPPLIER); + borrowableToken.approve(address(vault), type(uint256).max); } function _addrFromHashedString(string memory str) internal pure returns (address) { diff --git a/test/forge/VaultTest.sol b/test/forge/VaultTest.sol new file mode 100644 index 00000000..b95a67a1 --- /dev/null +++ b/test/forge/VaultTest.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {MorphoBalancesLib} from "@morpho-blue/libraries/periphery/MorphoBalancesLib.sol"; + +import "./BaseTest.sol"; + +contract VaultTest is BaseTest { + using MorphoBalancesLib for Morpho; + using MarketParamsLib for MarketParams; + + function setUp() public override { + super.setUp(); + + _submitAndEnableMarket(allMarkets[0], CAP); + } + + function tesMint(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + uint256 shares = vault.convertToShares(amount); + + vm.prank(SUPPLIER); + vault.mint(shares, RECEIVER); + + uint256 totalBalanceAfter = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + + assertEq(vault.balanceOf(RECEIVER), shares, "balance"); + assertGt(shares, 0, "shares is zero"); + assertEq(totalBalanceAfter, amount, "totalBalance"); + } + + function testDeposit(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + vault.deposit(amount, RECEIVER); + + uint256 totalBalanceAfter = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + + assertGt(vault.balanceOf(RECEIVER), 0, "balance is zero"); + assertEq(totalBalanceAfter, amount, "totalBalance"); + } + + function testShouldNotRedeemTooMuch(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + uint256 shares = vault.deposit(amount, RECEIVER); + + vm.prank(RECEIVER); + vm.expectRevert(bytes(ErrorsLib.WITHDRAW_ORDER_FAILED)); + vault.redeem(shares + 1, RECEIVER, RECEIVER); + } + + function testWithdrawAll(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + uint256 totalBalanceBefore = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + + vm.prank(SUPPLIER); + vault.deposit(amount, RECEIVER); + + vm.startPrank(RECEIVER); + vault.withdraw(vault.maxWithdraw(RECEIVER), RECEIVER, RECEIVER); + vm.stopPrank(); + + uint256 totalBalanceAfter = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + + assertEq(vault.balanceOf(RECEIVER), 0, "balance not zero"); + assertEq(ERC20(borrowableToken).balanceOf(RECEIVER), amount, "amount withdrawn != amount deposited"); + assertEq(totalBalanceAfter, totalBalanceBefore, "totalBalance"); + } + + function testRedeemAll(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + uint256 shares = vault.deposit(amount, RECEIVER); + + vm.prank(RECEIVER); + vault.redeem(shares, RECEIVER, RECEIVER); + + uint256 totalBalanceAfter = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + + assertEq(ERC20(borrowableToken).balanceOf(RECEIVER), amount, "amount withdrawn != amount deposited"); + assertEq(vault.balanceOf(SUPPLIER), 0, "balance not zero"); + assertEq(totalBalanceAfter, 0, "totalBalance"); + } + + function testShouldNotRedeemWhenNotDeposited(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.WITHDRAW_ORDER_FAILED)); + vault.redeem(amount, SUPPLIER, SUPPLIER); + } + + function testShouldNotRedeemIfNotApproved(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + uint256 shares = vault.deposit(amount, SUPPLIER); + + vm.prank(RECEIVER); + vm.expectRevert("ERC20: insufficient allowance"); + vault.redeem(shares, RECEIVER, SUPPLIER); + } + + function testShouldNotWithdrawIfNotApproved(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + vm.prank(RECEIVER); + vm.expectRevert("ERC20: insufficient allowance"); + vault.withdraw(amount, RECEIVER, SUPPLIER); + } + + function testTransferFrom(uint256 amount, uint256 toApprove) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.startPrank(SUPPLIER); + uint256 shares = vault.deposit(amount, SUPPLIER); + toApprove = bound(toApprove, 0, shares); + vault.approve(RECEIVER, toApprove); + vm.stopPrank(); + + vm.prank(RECEIVER); + vault.transferFrom(SUPPLIER, RECEIVER, toApprove); + + assertEq(vault.balanceOf(SUPPLIER), amount - toApprove, "balance supplier"); + assertEq(vault.balanceOf(RECEIVER), toApprove, "balance receiver"); + } + + function testShouldNotTransferFromIfNotApproved(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + uint256 shares = vault.deposit(amount, SUPPLIER); + + vm.prank(RECEIVER); + vm.expectRevert("ERC20: insufficient allowance"); + vault.transferFrom(SUPPLIER, RECEIVER, shares); + } + + function testShouldNotWithdrawTooMuch(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.WITHDRAW_ORDER_FAILED)); + vault.withdraw(amount + 1, SUPPLIER, SUPPLIER); + } + + function testTransfer(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + uint256 balance = vault.balanceOf(SUPPLIER); + vm.prank(SUPPLIER); + vault.transfer(RECEIVER, balance); + + assertEq(vault.balanceOf(SUPPLIER), 0); + assertEq(vault.balanceOf(RECEIVER), balance); + } +} From cc1962c729ae3f282191fbe4c84a34ed919fe98c Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 8 Sep 2023 10:54:57 +0200 Subject: [PATCH 3/6] test: add basic tests --- test/forge/BaseTest.sol | 4 +- test/forge/VaultSeveralMarketsTest.sol | 110 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 test/forge/VaultSeveralMarketsTest.sol diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index c0f9a5a3..bff57df1 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -104,9 +104,11 @@ contract BaseTest is Test { vm.warp(block.timestamp + 1); deal(address(borrowableToken), SUPPLIER, type(uint256).max); - vm.prank(SUPPLIER); borrowableToken.approve(address(vault), type(uint256).max); + deal(address(borrowableToken), RECEIVER, type(uint256).max); + vm.prank(RECEIVER); + borrowableToken.approve(address(vault), type(uint256).max); } function _addrFromHashedString(string memory str) internal pure returns (address) { diff --git a/test/forge/VaultSeveralMarketsTest.sol b/test/forge/VaultSeveralMarketsTest.sol new file mode 100644 index 00000000..5c022637 --- /dev/null +++ b/test/forge/VaultSeveralMarketsTest.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {MorphoBalancesLib} from "@morpho-blue/libraries/periphery/MorphoBalancesLib.sol"; + +import "./BaseTest.sol"; + +contract VaultSeveralMarketsTest is BaseTest { + using MorphoBalancesLib for Morpho; + using MarketParamsLib for MarketParams; + + function setUp() public override { + super.setUp(); + + _submitAndEnableMarket(allMarkets[0], CAP); + _submitAndEnableMarket(allMarkets[1], CAP); + _submitAndEnableMarket(allMarkets[2], CAP); + } + + function _setCaps(uint128 cap) internal { + vm.startPrank(OWNER); + vault.setCap(allMarkets[0], cap); + vault.setCap(allMarkets[1], cap); + vault.setCap(allMarkets[2], cap); + vm.stopPrank(); + } + + function testMintWithCaps(uint128 cap, uint256 amount) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); + uint256 shares = vault.convertToShares(amount); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.mint(shares, SUPPLIER); + + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); + assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount, "totalBalance"); + } + + function testDepositWithCaps(uint128 cap, uint256 amount) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(vault.balanceOf(SUPPLIER), amount, "balance"); + assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount, "totalBalance"); + } + + function testShouldNotMintMoreThanCaps(uint128 cap, uint256 amount) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT / 3)); + amount = bound(amount, 3 * cap + 1, MAX_TEST_AMOUNT); + uint256 shares = vault.convertToShares(amount); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.DEPOSIT_ORDER_FAILED)); + vault.mint(shares, SUPPLIER); + } + + function testShouldNotDepositMoreThanCaps(uint128 cap, uint256 amount) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT / 3)); + amount = bound(amount, 3 * cap + 1, MAX_TEST_AMOUNT); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.DEPOSIT_ORDER_FAILED)); + vault.deposit(amount, SUPPLIER); + } + + function testMintWithCapsWithSeveralUsers(uint128 cap, uint256 alreadyDeposited, uint256 amount) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + alreadyDeposited = bound(alreadyDeposited, 3 * cap / 2, 3 * cap); + amount = bound(amount, 0, 3 * cap - alreadyDeposited); + + _setCaps(cap); + + vm.prank(RECEIVER); + vault.deposit(alreadyDeposited, RECEIVER); + + uint256 shares = vault.convertToShares(amount); + + vm.prank(SUPPLIER); + vault.mint(shares, SUPPLIER); + + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); + assertEq( + totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, alreadyDeposited + amount, "totalBalance" + ); + } +} From f8df24253443bcc064dfa3d19ddebec0d32c0937 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 8 Sep 2023 11:42:27 +0200 Subject: [PATCH 4/6] test: add fail call test + refactor --- test/forge/VaultSeveralMarketsTest.sol | 80 +++++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/test/forge/VaultSeveralMarketsTest.sol b/test/forge/VaultSeveralMarketsTest.sol index 5c022637..67aa281e 100644 --- a/test/forge/VaultSeveralMarketsTest.sol +++ b/test/forge/VaultSeveralMarketsTest.sol @@ -25,6 +25,32 @@ contract VaultSeveralMarketsTest is BaseTest { vm.stopPrank(); } + function _assertBalances(uint128 cap, uint256 amount) internal { + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount, "totalBalance"); + + if (amount >= 3 * cap) { + assertEq(totalBalanceAfter0, cap, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, cap, "totalBalance2"); + } else if (amount >= 2 * cap) { + assertEq(totalBalanceAfter0, cap, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, amount % (2 * cap), "totalBalance2"); + } else if (amount >= cap) { + assertEq(totalBalanceAfter0, cap, "totalBalance0"); + assertEq(totalBalanceAfter1, amount % cap, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else { + assertEq(totalBalanceAfter0, amount, "totalBalance0"); + assertEq(totalBalanceAfter1, 0, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } + } + function testMintWithCaps(uint128 cap, uint256 amount) public { cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); @@ -35,12 +61,8 @@ contract VaultSeveralMarketsTest is BaseTest { vm.prank(SUPPLIER); vault.mint(shares, SUPPLIER); - uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); - uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); - uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); - assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); - assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount, "totalBalance"); + _assertBalances(cap, amount); } function testDepositWithCaps(uint128 cap, uint256 amount) public { @@ -52,12 +74,8 @@ contract VaultSeveralMarketsTest is BaseTest { vm.prank(SUPPLIER); vault.deposit(amount, SUPPLIER); - uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); - uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); - uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); - assertEq(vault.balanceOf(SUPPLIER), amount, "balance"); - assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount, "totalBalance"); + _assertBalances(cap, amount); } function testShouldNotMintMoreThanCaps(uint128 cap, uint256 amount) public { @@ -98,13 +116,47 @@ contract VaultSeveralMarketsTest is BaseTest { vm.prank(SUPPLIER); vault.mint(shares, SUPPLIER); + assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); + _assertBalances(cap, alreadyDeposited + amount); + } + + function testShouldNotMintMoreThanCapsWithSeveralUsers(uint128 cap, uint256 alreadyDeposited, uint256 amount) + public + { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT / 3)); + alreadyDeposited = bound(alreadyDeposited, 3 * cap / 2, 3 * cap); + amount = bound(amount, 3 * cap - alreadyDeposited + 1, MAX_TEST_AMOUNT); + + _setCaps(cap); + + vm.prank(RECEIVER); + vault.deposit(alreadyDeposited, RECEIVER); + + uint256 shares = vault.convertToShares(amount); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.DEPOSIT_ORDER_FAILED)); + vault.mint(shares, SUPPLIER); + } + + function testShouldSkipMarketWhenCallFail(uint256 amount) public { + amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); + uint256 shares = vault.convertToShares(amount); + + vm.prank(SUPPLIER); + vm.mockCallRevert( + address(morpho), 0, abi.encodeCall(morpho.supply, (allMarkets[0], amount, 0, address(vault), hex"")), hex"" + ); + vault.mint(shares, SUPPLIER); + + assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); - assertEq(vault.balanceOf(SUPPLIER), shares, "balance"); - assertEq( - totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, alreadyDeposited + amount, "totalBalance" - ); + assertEq(totalBalanceAfter0, 0, "totalBalance0"); + assertEq(totalBalanceAfter1, amount, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); } } From 14a8212e22de4e97a3fc551299b0b2a5d8f2f8ac Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 8 Sep 2023 14:13:30 +0200 Subject: [PATCH 5/6] test: add withdraw test --- test/forge/BaseTest.sol | 1 + test/forge/VaultSeveralMarketsTest.sol | 113 +++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index bff57df1..11c5b62d 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -5,6 +5,7 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import {IrmMock as Irm} from "contracts/mocks/IrmMock.sol"; +import {UtilsLib} from "@morpho-blue/libraries/UtilsLib.sol"; import {ERC20Mock as ERC20} from "contracts/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "contracts/mocks/OracleMock.sol"; diff --git a/test/forge/VaultSeveralMarketsTest.sol b/test/forge/VaultSeveralMarketsTest.sol index 67aa281e..de8ed496 100644 --- a/test/forge/VaultSeveralMarketsTest.sol +++ b/test/forge/VaultSeveralMarketsTest.sol @@ -15,6 +15,14 @@ contract VaultSeveralMarketsTest is BaseTest { _submitAndEnableMarket(allMarkets[0], CAP); _submitAndEnableMarket(allMarkets[1], CAP); _submitAndEnableMarket(allMarkets[2], CAP); + + Id[] memory withdrawAllocationOrder = new Id[](3); + withdrawAllocationOrder[0] = allMarkets[2].id(); + withdrawAllocationOrder[1] = allMarkets[1].id(); + withdrawAllocationOrder[2] = allMarkets[0].id(); + + vm.prank(ALLOCATOR); + vault.setWithdrawAllocationOrder(withdrawAllocationOrder); } function _setCaps(uint128 cap) internal { @@ -51,6 +59,8 @@ contract VaultSeveralMarketsTest is BaseTest { } } + /* MINT/DEPOSIT */ + function testMintWithCaps(uint128 cap, uint256 amount) public { cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); @@ -159,4 +169,107 @@ contract VaultSeveralMarketsTest is BaseTest { assertEq(totalBalanceAfter1, amount, "totalBalance1"); assertEq(totalBalanceAfter2, 0, "totalBalance2"); } + + /* REDEEM/WITHDRAW */ + + function testRedeemSeveralMarkets(uint128 cap, uint256 amount, uint256 toRedeem) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); + + _setCaps(cap); + + vm.startPrank(SUPPLIER); + uint256 shares = vault.deposit(amount, SUPPLIER); + toRedeem = bound(toRedeem, 0, shares); + uint256 withdrawn = vault.redeem(toRedeem, SUPPLIER, SUPPLIER); + vm.stopPrank(); + + _assertBalances(cap, amount - withdrawn); + } + + function testWithdrawSeveralMarkets(uint128 cap, uint256 amount, uint256 toWithdraw) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + amount = bound(amount, MIN_TEST_AMOUNT / 3, 3 * cap); + + _setCaps(cap); + + vm.startPrank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + toWithdraw = bound(toWithdraw, 0, amount); + vault.withdraw(toWithdraw, SUPPLIER, SUPPLIER); + vm.stopPrank(); + + _assertBalances(cap, amount - toWithdraw); + } + + function _borrow(MarketParams memory marketParams, uint256 amount) internal { + // vm.startPrank(SUPPLIER); + // borrowableToken.approve(address(morpho), type(uint256).max); + // morpho.supply(marketParams, amount, 0, SUPPLIER, hex""); + // vm.stopPrank(); + + deal(address(collateralToken), BORROWER, type(uint256).max); + + vm.startPrank(BORROWER); + collateralToken.approve(address(morpho), type(uint256).max); + morpho.supplyCollateral(marketParams, type(uint128).max, BORROWER, hex""); + morpho.borrow(marketParams, amount, 0, BORROWER, BORROWER); + vm.stopPrank(); + } + + function testWithdrawSeveralMarketsWithLessLiquidity( + uint128 cap, + uint256 amount, + uint256 toWithdraw, + uint256 borrowed + ) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + amount = 3 * cap; + borrowed = bound(borrowed, 1, cap); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + _borrow(allMarkets[1], borrowed); + + toWithdraw = bound(toWithdraw, 0, amount - borrowed); + + vm.prank(SUPPLIER); + vault.withdraw(toWithdraw, SUPPLIER, SUPPLIER); + + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount - toWithdraw, "totalBalance"); + + if (toWithdraw >= 3 * cap) { + assertEq(totalBalanceAfter0, 0, "totalBalance0"); + assertEq(totalBalanceAfter1, 0, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else if (toWithdraw >= 2 * cap) { + assertEq(totalBalanceAfter0, 3 * cap - toWithdraw - borrowed, "totalBalance0"); + assertEq(totalBalanceAfter1, borrowed, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else if (toWithdraw >= cap) { + uint256 bal0; + uint256 bal1; + if (toWithdraw - cap > cap - borrowed) { + bal0 = 3 * cap - borrowed - toWithdraw; + bal1 = borrowed; + } else { + bal0 = cap; + bal1 = 2 * cap - toWithdraw; + } + assertEq(totalBalanceAfter0, bal0, "totalBalance0"); + assertEq(totalBalanceAfter1, bal1, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else { + assertEq(totalBalanceAfter0, cap, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, cap - toWithdraw, "totalBalance2"); + } + } } From b860e605e66b2300c998f6bc3ef1651417d9199a Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 8 Sep 2023 16:21:34 +0200 Subject: [PATCH 6/6] test: add withdraw tests --- test/forge/VaultSeveralMarketsTest.sol | 100 +++++++++++++++++++++---- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/test/forge/VaultSeveralMarketsTest.sol b/test/forge/VaultSeveralMarketsTest.sol index de8ed496..218f9f08 100644 --- a/test/forge/VaultSeveralMarketsTest.sol +++ b/test/forge/VaultSeveralMarketsTest.sol @@ -149,7 +149,7 @@ contract VaultSeveralMarketsTest is BaseTest { vault.mint(shares, SUPPLIER); } - function testShouldSkipMarketWhenCallFail(uint256 amount) public { + function testMintShouldSkipMarketWhenCallFail(uint256 amount) public { amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT); uint256 shares = vault.convertToShares(amount); @@ -203,11 +203,6 @@ contract VaultSeveralMarketsTest is BaseTest { } function _borrow(MarketParams memory marketParams, uint256 amount) internal { - // vm.startPrank(SUPPLIER); - // borrowableToken.approve(address(morpho), type(uint256).max); - // morpho.supply(marketParams, amount, 0, SUPPLIER, hex""); - // vm.stopPrank(); - deal(address(collateralToken), BORROWER, type(uint256).max); vm.startPrank(BORROWER); @@ -217,14 +212,9 @@ contract VaultSeveralMarketsTest is BaseTest { vm.stopPrank(); } - function testWithdrawSeveralMarketsWithLessLiquidity( - uint128 cap, - uint256 amount, - uint256 toWithdraw, - uint256 borrowed - ) public { + function testWithdrawSeveralMarketsWithLessLiquidity(uint128 cap, uint256 toWithdraw, uint256 borrowed) public { cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); - amount = 3 * cap; + uint256 amount = 3 * cap; borrowed = bound(borrowed, 1, cap); _setCaps(cap); @@ -272,4 +262,88 @@ contract VaultSeveralMarketsTest is BaseTest { assertEq(totalBalanceAfter2, cap - toWithdraw, "totalBalance2"); } } + + function testWithdrawShouldSkipMarketWhenCallFail(uint128 cap, uint256 toWithdraw) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + uint256 amount = 3 * cap; + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + toWithdraw = bound(toWithdraw, 0, 2 * cap); + + vm.prank(SUPPLIER); + if (toWithdraw > cap) { + vm.mockCallRevert( + address(morpho), + 0, + abi.encodeCall(morpho.withdraw, (allMarkets[1], toWithdraw - cap, 0, address(vault), address(vault))), + hex"" + ); + } + vault.withdraw(toWithdraw, SUPPLIER, SUPPLIER); + + uint256 totalBalanceAfter0 = morpho.expectedSupplyBalance(allMarkets[0], address(vault)); + uint256 totalBalanceAfter1 = morpho.expectedSupplyBalance(allMarkets[1], address(vault)); + uint256 totalBalanceAfter2 = morpho.expectedSupplyBalance(allMarkets[2], address(vault)); + + assertEq(totalBalanceAfter0 + totalBalanceAfter1 + totalBalanceAfter2, amount - toWithdraw, "totalBalance"); + + if (toWithdraw == 2 * cap) { + assertEq(totalBalanceAfter0, 0, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else if (toWithdraw >= cap) { + assertEq(totalBalanceAfter0, 2 * cap - toWithdraw, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, 0, "totalBalance2"); + } else { + assertEq(totalBalanceAfter0, cap, "totalBalance0"); + assertEq(totalBalanceAfter1, cap, "totalBalance1"); + assertEq(totalBalanceAfter2, cap - toWithdraw, "totalBalance2"); + } + } + + function testWithdrawShouldRevertWhenNotEnoughLiquidity(uint128 cap, uint256 toWithdraw, uint256 borrowed) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + uint256 amount = 3 * cap; + borrowed = bound(borrowed, 1, cap); + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + _borrow(allMarkets[1], borrowed); + + toWithdraw = bound(toWithdraw, amount - borrowed + 1, amount); + + vm.prank(SUPPLIER); + vm.expectRevert(bytes(ErrorsLib.WITHDRAW_ORDER_FAILED)); + vault.withdraw(toWithdraw, SUPPLIER, SUPPLIER); + } + + function testWithdrawShouldRevertWhenSkipMarketNotEnoughLiquidity(uint128 cap, uint256 toWithdraw) public { + cap = uint128(bound(cap, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT)); + uint256 amount = 3 * cap; + + _setCaps(cap); + + vm.prank(SUPPLIER); + vault.deposit(amount, SUPPLIER); + + toWithdraw = bound(toWithdraw, 2 * cap + 1, amount); + + vm.prank(SUPPLIER); + vm.mockCallRevert( + address(morpho), + 0, + abi.encodeCall(morpho.withdraw, (allMarkets[1], cap, 0, address(vault), address(vault))), + hex"" + ); + vm.expectRevert(bytes(ErrorsLib.WITHDRAW_ORDER_FAILED)); + vault.withdraw(toWithdraw, SUPPLIER, SUPPLIER); + } }