From dda1ff90a5984f12a7e61b5f2baf54a01fa660d3 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 12:40:17 +0300 Subject: [PATCH 01/19] remove euler-xyz/euler-vault-kit --- .gitmodules | 3 --- lib/euler-vault-kit | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index 0e9f83a5..2e3aabb7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector -[submodule "lib/euler-vault-kit"] - path = lib/euler-vault-kit - url = https://github.com/euler-xyz/euler-vault-kit diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit deleted file mode 160000 index e7577002..00000000 --- a/lib/euler-vault-kit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e75770023e1b432a660828120cc166b7dc64a222 From e2ce9d514e301c4c46f52cdfa3dc8285066dfe41 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 12:48:55 +0300 Subject: [PATCH 02/19] forge install: euler-vault-kit --- .gitmodules | 3 +++ lib/euler-vault-kit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index 2e3aabb7..39ce9043 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector +[submodule "lib/euler-vault-kit"] + path = lib/euler-vault-kit + url = https://github.com/haythemsellami/euler-vault-kit diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit new file mode 160000 index 00000000..5832374a --- /dev/null +++ b/lib/euler-vault-kit @@ -0,0 +1 @@ +Subproject commit 5832374a9f0abd70506cb8ac2a3a324a27043669 From c904793679d66d036de3e5128655dd70750612e9 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 12:50:03 +0300 Subject: [PATCH 03/19] fix --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 39ce9043..fff97775 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,4 @@ [submodule "lib/euler-vault-kit"] path = lib/euler-vault-kit url = https://github.com/haythemsellami/euler-vault-kit + From 44b52e14bffc9835b68c9d899d3704dcc91ff258 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 12:57:54 +0300 Subject: [PATCH 04/19] ci: add coverage summary --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c33e4bc..a04ee8bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,5 +35,5 @@ jobs: - name: Run foundry fuzzing run: FOUNDRY_PROFILE=ci_fuzz forge test -vv - # - name: Run foundry coverage - # run: FOUNDRY_PROFILE=coverage forge coverage --report summary \ No newline at end of file + - name: Run foundry coverage + run: FOUNDRY_PROFILE=coverage forge coverage --report summary \ No newline at end of file From 15e9b03c6473ddf460617b873641e20d252660f5 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 13:36:31 +0300 Subject: [PATCH 05/19] add coverage script --- .gitignore | 4 ++++ coverage.sh | 36 ++++++++++++++++++++++++++++++++++++ script/Counter.s.sol | 12 ------------ 3 files changed, 40 insertions(+), 12 deletions(-) create mode 100755 coverage.sh delete mode 100644 script/Counter.s.sol diff --git a/.gitignore b/.gitignore index 85198aaa..565f4937 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ docs/ # Dotenv file .env + +# coverage +lcov.info +coverage \ No newline at end of file diff --git a/coverage.sh b/coverage.sh new file mode 100755 index 00000000..5eca127e --- /dev/null +++ b/coverage.sh @@ -0,0 +1,36 @@ +# Make sure this script is executable. On Unix based systems, run "chmod +x coverage.sh". +# To run this script, type "./coverage.sh". + +#!/bin/bash + +set -e # exit on error + +# check if lcov is installed, if not, execute installation +if ! command -v lcov &>/dev/null; then + echo "lcov is not installed. Installing..." + # check if its macos or linux. + if [ "$(uname)" == "Darwin" ]; then + brew install lcov + + else + sudo apt-get install lcov + fi +fi +lcov --version + +# generates lcov.info +FOUNDRY_PROFILE=coverage forge coverage --report lcov + +# Generate summary +lcov \ + --rc branch_coverage=1 \ + --ignore-errors inconsistent \ + --list lcov.info + +# Open more granular breakdown in browser +genhtml \ + --ignore-errors category \ + --rc branch_coverage=1 \ + --output-directory coverage \ + lcov.info +open coverage/index.html \ No newline at end of file diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index df9ee8b0..00000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} From d5604db919634a4ba28926a329fcfd8435f742d1 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 17 May 2024 14:15:01 +0300 Subject: [PATCH 06/19] rename --- ...ositRebalanceHarvestWithdrawE2ETest.t.sol} | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) rename test/e2e/{DepositRebalanceWithdrawE2ETest.t.sol => DepositRebalanceHarvestWithdrawE2ETest.t.sol} (86%) diff --git a/test/e2e/DepositRebalanceWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol similarity index 86% rename from test/e2e/DepositRebalanceWithdrawE2ETest.t.sol rename to test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index 437b73b6..e1a4deac 100644 --- a/test/e2e/DepositRebalanceWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -11,7 +11,7 @@ import { TestERC20 } from "../common/FourSixTwoSixAggBase.t.sol"; -contract DepositRebalanceWithdrawE2ETest is FourSixTwoSixAggBase { +contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -476,4 +476,75 @@ contract DepositRebalanceWithdrawE2ETest is FourSixTwoSixAggBase { ); } } + + function testSingleStrategy_WithYield_WithInterest() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + { + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints + / fourSixTwoSixAgg.totalAllocationPoints(); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + } + + vm.warp(block.timestamp + 86400); + // mock an increase of strategy balance by 10% + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); + uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; + uint256 yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; + assetTST.mint(address(eTST), yield); + eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + + // harvest + vm.prank(user1); + fourSixTwoSixAgg.harvest(address(eTST)); + vm.warp(block.timestamp + 2 weeks); + + // full withdraw, will have to withdraw from strategy as cash reserve is not enough + { + uint256 amountToWithdraw = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); + + vm.prank(user1); + fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + + // all yield is distributed + assertApproxEqAbs(eTST.balanceOf(address(fourSixTwoSixAgg)), 0, 1); + assertApproxEqAbs(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw, 1); + assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertApproxEqAbs(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToDeposit + yield, 1); + } + } } From 14468b1fe635942d94e91e1c821fecc537287f68 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:16:42 +0100 Subject: [PATCH 07/19] add test --- src/FourSixTwoSixAgg.sol | 11 ++- ...positRebalanceHarvestWithdrawE2ETest.t.sol | 98 ++++++++++++++----- 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 4074a31a..8252ee3f 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -8,6 +8,8 @@ import {ERC4626, IERC4626} from "openzeppelin-contracts/token/ERC20/extensions/E import {EVCUtil, IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {AccessControlEnumerable} from "openzeppelin-contracts/access/AccessControlEnumerable.sol"; +import {console2} from "forge-std/Test.sol"; + // @note Do NOT use with fee on transfer tokens // @note Do NOT use with rebasing tokens // @note Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol @@ -516,14 +518,17 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { // remove from withdrawalQueue uint256 lastStrategyIndex = withdrawalQueue.length - 1; - for (uint256 i; i <= lastStrategyIndex; ++i) { + + console2.log("lastStrategyIndex", lastStrategyIndex); + + for (uint256 i = 0; i <= lastStrategyIndex; i++) { if ((withdrawalQueue[i] == strategy) && (i != lastStrategyIndex)) { (withdrawalQueue[i], withdrawalQueue[lastStrategyIndex]) = (withdrawalQueue[lastStrategyIndex], withdrawalQueue[i]); } - - withdrawalQueue.pop(); } + + withdrawalQueue.pop(); } /// @notice Return the accrued interest diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index e1a4deac..b06bf03b 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -477,7 +477,16 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { } } - function testSingleStrategy_WithYield_WithInterest() public { + function testWithdraw_NotEnoughAssets() public { + IEVault eTSTsecondary; + { + eTSTsecondary = IEVault(coreProductLine.createVault(address(assetTST), address(oracle), unitOfAccount)); + eTSTsecondary.setInterestRateModel(address(new IRMTestDefault())); + + uint256 initialStrategyAllocationPoints = 1000e18; + _addStrategy(manager, address(eTSTsecondary), initialStrategyAllocationPoints); + } + uint256 amountToDeposit = 10000e18; // deposit into aggregator @@ -499,52 +508,87 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { } // rebalance into strategy + // 2500 total points; 1000 for reserve(40%), 500(20%) for eTST, 1000(40%) for eTSTsecondary + // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + FourSixTwoSixAgg.Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + FourSixTwoSixAgg.Strategy memory eTSTsecondarystrategyBefore = + fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + eTSTsecondarystrategyBefore.allocated + ); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedeTSTStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() + * eTSTstrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedeTSTsecondaryStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() + * eTSTsecondarystrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + + assertTrue(expectedeTSTStrategyCash != 0); + assertTrue(expectedeTSTsecondaryStrategyCash != 0); + address[] memory strategiesToRebalance = new address[](2); + strategiesToRebalance[0] = address(eTST); + strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + fourSixTwoSixAgg.rebalanceMultipleStrategies(strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + expectedeTSTsecondaryStrategyCash + ); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); + assertEq( + (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash + ); + assertEq( + assetTST.balanceOf(address(fourSixTwoSixAgg)), + amountToDeposit - (expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash) + ); } vm.warp(block.timestamp + 86400); - // mock an increase of strategy balance by 10% - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); - uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; - uint256 yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; - assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + uint256 eTSTYield; + uint256 eTSTsecondaryYield; + { + // mock an increase of aggregator balance due to yield + uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTShareBalance); + uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTsecondaryUnderlyingBalance = + eTST.convertToAssets(aggrCurrenteTSTsecondaryShareBalance); + uint256 aggrNeweTSTUnderlyingBalance = aggrCurrenteTSTUnderlyingBalance * 11e17 / 1e18; + uint256 aggrNeweTSTsecondaryUnderlyingBalance = aggrCurrenteTSTsecondaryUnderlyingBalance * 11e17 / 1e18; + eTSTYield = aggrNeweTSTUnderlyingBalance - aggrCurrenteTSTUnderlyingBalance; + eTSTsecondaryYield = aggrNeweTSTsecondaryUnderlyingBalance - aggrCurrenteTSTsecondaryUnderlyingBalance; + + assetTST.mint(address(eTST), eTSTYield); + assetTST.mint(address(eTSTsecondary), eTSTsecondaryYield); + eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTSTsecondary.skim(type(uint256).max, address(fourSixTwoSixAgg)); + } // harvest + address[] memory strategiesToHarvest = new address[](1); + strategiesToHarvest[0] = address(eTST); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + fourSixTwoSixAgg.harvestMultipleStrategies(strategiesToHarvest); vm.warp(block.timestamp + 2 weeks); - // full withdraw, will have to withdraw from strategy as cash reserve is not enough + vm.prank(manager); + fourSixTwoSixAgg.removeStrategy(address(eTSTsecondary)); + { uint256 amountToWithdraw = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); - uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); + vm.expectRevert(FourSixTwoSixAgg.NotEnoughAssets.selector); fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); - - // all yield is distributed - assertApproxEqAbs(eTST.balanceOf(address(fourSixTwoSixAgg)), 0, 1); - assertApproxEqAbs(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw, 1); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); - assertApproxEqAbs(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToDeposit + yield, 1); } } } From 84f4d127499dc5a7a975e156b6d5e9b8b84b28cd Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:19:23 +0100 Subject: [PATCH 08/19] remove the forked dependency --- .gitmodules | 4 ---- lib/euler-vault-kit | 1 - 2 files changed, 5 deletions(-) delete mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index fff97775..2e3aabb7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,3 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector -[submodule "lib/euler-vault-kit"] - path = lib/euler-vault-kit - url = https://github.com/haythemsellami/euler-vault-kit - diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit deleted file mode 160000 index 5832374a..00000000 --- a/lib/euler-vault-kit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5832374a9f0abd70506cb8ac2a3a324a27043669 From aa48e780b81cc8295e8e5e3edcc47877c1166254 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:27:35 +0100 Subject: [PATCH 09/19] forge install: euler-vault-kit --- .gitmodules | 3 +++ lib/euler-vault-kit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index 2e3aabb7..0e9f83a5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector +[submodule "lib/euler-vault-kit"] + path = lib/euler-vault-kit + url = https://github.com/euler-xyz/euler-vault-kit diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit new file mode 160000 index 00000000..8d8aec00 --- /dev/null +++ b/lib/euler-vault-kit @@ -0,0 +1 @@ +Subproject commit 8d8aec00723daa1cbb723d6f213944199d4bc26b From b4d29a6df82ca68cb92a457e4d13d49254269d9d Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:31:14 +0100 Subject: [PATCH 10/19] use factory instead of product line --- ...positRebalanceHarvestWithdrawE2ETest.t.sol | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index b06bf03b..2da74da6 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -173,8 +173,14 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { function testMultipleStrategy_WithYield() public { IEVault eTSTsecondary; { - eTSTsecondary = IEVault(coreProductLine.createVault(address(assetTST), address(oracle), unitOfAccount)); + eTSTsecondary = IEVault( + factory.createProxy( + address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount) + ) + ); eTSTsecondary.setInterestRateModel(address(new IRMTestDefault())); + eTSTsecondary.setMaxLiquidationDiscount(0.2e4); + eTSTsecondary.setFeeReceiver(feeReceiver); uint256 initialStrategyAllocationPoints = 1000e18; _addStrategy(manager, address(eTSTsecondary), initialStrategyAllocationPoints); @@ -355,8 +361,14 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { function testMultipleStrategy_WithYield_WithInterest() public { IEVault eTSTsecondary; { - eTSTsecondary = IEVault(coreProductLine.createVault(address(assetTST), address(oracle), unitOfAccount)); + eTSTsecondary = IEVault( + factory.createProxy( + address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount) + ) + ); eTSTsecondary.setInterestRateModel(address(new IRMTestDefault())); + eTSTsecondary.setMaxLiquidationDiscount(0.2e4); + eTSTsecondary.setFeeReceiver(feeReceiver); uint256 initialStrategyAllocationPoints = 1000e18; _addStrategy(manager, address(eTSTsecondary), initialStrategyAllocationPoints); @@ -480,8 +492,14 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { function testWithdraw_NotEnoughAssets() public { IEVault eTSTsecondary; { - eTSTsecondary = IEVault(coreProductLine.createVault(address(assetTST), address(oracle), unitOfAccount)); + eTSTsecondary = IEVault( + factory.createProxy( + address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount) + ) + ); eTSTsecondary.setInterestRateModel(address(new IRMTestDefault())); + eTSTsecondary.setMaxLiquidationDiscount(0.2e4); + eTSTsecondary.setFeeReceiver(feeReceiver); uint256 initialStrategyAllocationPoints = 1000e18; _addStrategy(manager, address(eTSTsecondary), initialStrategyAllocationPoints); From 323a31ef5534fb7a26056bc917c121a4d8f06530 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:31:49 +0100 Subject: [PATCH 11/19] remove dependency --- .gitmodules | 3 --- lib/euler-vault-kit | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index 0e9f83a5..2e3aabb7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector -[submodule "lib/euler-vault-kit"] - path = lib/euler-vault-kit - url = https://github.com/euler-xyz/euler-vault-kit diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit deleted file mode 160000 index 8d8aec00..00000000 --- a/lib/euler-vault-kit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d8aec00723daa1cbb723d6f213944199d4bc26b From 096e5724b5668f8c961e703678dc520790cbfa4a Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:32:12 +0100 Subject: [PATCH 12/19] remove dependency --- .gitmodules | 3 --- lib/ethereum-vault-connector | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/ethereum-vault-connector diff --git a/.gitmodules b/.gitmodules index 2e3aabb7..888d42dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/ethereum-vault-connector"] - path = lib/ethereum-vault-connector - url = https://github.com/euler-xyz/ethereum-vault-connector diff --git a/lib/ethereum-vault-connector b/lib/ethereum-vault-connector deleted file mode 160000 index 9bc0c17a..00000000 --- a/lib/ethereum-vault-connector +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9bc0c17afd6bed51cd9126d9f3b8fc31614d29a9 From a55e8ebf6265797ff8d0aa1ae421e7f982bf9294 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:34:17 +0100 Subject: [PATCH 13/19] forge install: ethereum-vault-connector --- .gitmodules | 3 +++ lib/ethereum-vault-connector | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/ethereum-vault-connector diff --git a/.gitmodules b/.gitmodules index 888d42dc..2e3aabb7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/ethereum-vault-connector"] + path = lib/ethereum-vault-connector + url = https://github.com/euler-xyz/ethereum-vault-connector diff --git a/lib/ethereum-vault-connector b/lib/ethereum-vault-connector new file mode 160000 index 00000000..f791f94e --- /dev/null +++ b/lib/ethereum-vault-connector @@ -0,0 +1 @@ +Subproject commit f791f94e6e790dd82041908983b57412dc04fb84 From 6494f89e62bf1203c713830f85449aa2e07f1a52 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 13:39:38 +0100 Subject: [PATCH 14/19] forge install: euler-vault-kit --- .gitmodules | 3 +++ lib/euler-vault-kit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/euler-vault-kit diff --git a/.gitmodules b/.gitmodules index 2e3aabb7..0e9f83a5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/ethereum-vault-connector"] path = lib/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector +[submodule "lib/euler-vault-kit"] + path = lib/euler-vault-kit + url = https://github.com/euler-xyz/euler-vault-kit diff --git a/lib/euler-vault-kit b/lib/euler-vault-kit new file mode 160000 index 00000000..8d8aec00 --- /dev/null +++ b/lib/euler-vault-kit @@ -0,0 +1 @@ +Subproject commit 8d8aec00723daa1cbb723d6f213944199d4bc26b From 7efbaac4f445e0b4be1989f7f2b0473f4166e912 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 14:34:22 +0100 Subject: [PATCH 15/19] update --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 0e9f83a5..1d3068ab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,4 @@ [submodule "lib/euler-vault-kit"] path = lib/euler-vault-kit url = https://github.com/euler-xyz/euler-vault-kit + From 469dac21998fe589e49adcb55e5c5fed5e3d63f8 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 14:57:12 +0100 Subject: [PATCH 16/19] clean --- src/FourSixTwoSixAgg.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 8252ee3f..55664699 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -8,8 +8,6 @@ import {ERC4626, IERC4626} from "openzeppelin-contracts/token/ERC20/extensions/E import {EVCUtil, IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {AccessControlEnumerable} from "openzeppelin-contracts/access/AccessControlEnumerable.sol"; -import {console2} from "forge-std/Test.sol"; - // @note Do NOT use with fee on transfer tokens // @note Do NOT use with rebasing tokens // @note Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol @@ -519,8 +517,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { // remove from withdrawalQueue uint256 lastStrategyIndex = withdrawalQueue.length - 1; - console2.log("lastStrategyIndex", lastStrategyIndex); - for (uint256 i = 0; i <= lastStrategyIndex; i++) { if ((withdrawalQueue[i] == strategy) && (i != lastStrategyIndex)) { (withdrawalQueue[i], withdrawalQueue[lastStrategyIndex]) = From c04ab49a89220d25fbc1633a6a969f1758de77aa Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 18:36:38 +0100 Subject: [PATCH 17/19] more tests --- src/FourSixTwoSixAgg.sol | 4 +- test/unit/RebalanceTest.t.sol | 324 +++++++++++++++++++++ test/unit/RemoveStrategy.t.sol | 26 +- test/unit/ReorderWithdrawalQueueTest.t.sol | 61 ++++ 4 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 test/unit/RebalanceTest.t.sol create mode 100644 test/unit/ReorderWithdrawalQueueTest.t.sol diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 55664699..cdba05dd 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -473,9 +473,7 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { revert SameIndexes(); } - address temp = withdrawalQueue[index1]; - withdrawalQueue[index1] = withdrawalQueue[index2]; - withdrawalQueue[index2] = temp; + (withdrawalQueue[index1], withdrawalQueue[index2]) = (withdrawalQueue[index2], withdrawalQueue[index1]); } /// @notice Add new strategy with it's allocation points. diff --git a/test/unit/RebalanceTest.t.sol b/test/unit/RebalanceTest.t.sol new file mode 100644 index 00000000..c4a8ba97 --- /dev/null +++ b/test/unit/RebalanceTest.t.sol @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import { + FourSixTwoSixAggBase, + FourSixTwoSixAgg, + console2, + EVault, + IEVault, + IRMTestDefault, + TestERC20 +} from "../common/FourSixTwoSixAggBase.t.sol"; + +contract RebalanceTest is FourSixTwoSixAggBase { + uint256 user1InitialBalance = 100000e18; + + function setUp() public virtual override { + super.setUp(); + + uint256 initialStrategyAllocationPoints = 500e18; + _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); + + assetTST.mint(user1, user1InitialBalance); + } + + function testRebalanceByDepositing() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints + / fourSixTwoSixAgg.totalAllocationPoints(); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq( + (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + expectedStrategyCash + ); + } + + function testRebalanceByDepositingWhenToDepositIsGreaterThanMaxDeposit() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints + / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedToDeposit = expectedStrategyCash - strategyBefore.allocated; + uint256 eTSTMaxDeposit = expectedToDeposit * 7e17 / 1e18; + // mock max deposit + vm.mockCall( + address(eTST), abi.encodeCall(eTST.maxDeposit, (address(fourSixTwoSixAgg))), abi.encode(eTSTMaxDeposit) + ); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTMaxDeposit); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTMaxDeposit); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + eTSTMaxDeposit); + } + + function testRebalanceByDepositingWhenToDepositIsGreaterThanCashAvailable() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into first strategy + vm.warp(block.timestamp + 86400); + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + // create new strategy & add it + IEVault eTSTsecondary; + uint256 eTSTsecondaryAllocationPoints = 1500e18; + { + eTSTsecondary = IEVault( + factory.createProxy( + address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount) + ) + ); + _addStrategy(manager, address(eTSTsecondary), eTSTsecondaryAllocationPoints); + } + + // rebalance into eTSTsecondary + vm.warp(block.timestamp + 86400); + { + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + strategyBefore.allocated + ); + + uint256 targetCash = fourSixTwoSixAgg.totalAssetsAllocatable() + * fourSixTwoSixAgg.getStrategy(address(0)).allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 currentCash = fourSixTwoSixAgg.totalAssetsAllocatable() - fourSixTwoSixAgg.totalAllocated(); + uint256 expectedStrategyCash = currentCash - targetCash; + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTSTsecondary)); + + // assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTsecondaryMaxDeposit); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash + ); + assertEq( + (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, + strategyBefore.allocated + expectedStrategyCash + ); + } + } + + function testRebalanceByDepositingWhenToDepositIsZero() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 eTSTMaxDeposit = 0; + // mock max deposit + vm.mockCall( + address(eTST), abi.encodeCall(eTST.maxDeposit, (address(fourSixTwoSixAgg))), abi.encode(eTSTMaxDeposit) + ); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated); + } + + function testRebalanceByWithdrawing() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + // decrease allocation points + uint256 newAllocationPoints = 300e18; + vm.prank(manager); + fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + + vm.warp(block.timestamp + 86400); + + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints + / fourSixTwoSixAgg.totalAllocationPoints(); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq( + (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, + strategyBefore.allocated - (strategyBefore.allocated - expectedStrategyCash) + ); + } + + function testRebalanceByWithdrawingWhenToWithdrawIsGreaterThanMaxWithdraw() public { + uint256 amountToDeposit = 10000e18; + + // deposit into aggregator + { + uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); + uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); + + vm.startPrank(user1); + assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); + fourSixTwoSixAgg.deposit(amountToDeposit, user1); + vm.stopPrank(); + + assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); + } + + // rebalance into strategy + vm.warp(block.timestamp + 86400); + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + // decrease allocation points + uint256 newAllocationPoints = 300e18; + vm.prank(manager); + fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + + vm.warp(block.timestamp + 86400); + + FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + + uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints + / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedToWithdraw = strategyBefore.allocated - expectedStrategyCash; + uint256 eTSTMaxWithdraw = expectedToWithdraw * 7e17 / 1e18; + // mock max withdraw + vm.mockCall( + address(eTST), abi.encodeCall(eTST.maxWithdraw, (address(fourSixTwoSixAgg))), abi.encode(eTSTMaxWithdraw) + ); + + vm.prank(user1); + fourSixTwoSixAgg.rebalance(address(eTST)); + + assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated - eTSTMaxWithdraw); + assertEq( + eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated - eTSTMaxWithdraw + ); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated - eTSTMaxWithdraw); + } +} diff --git a/test/unit/RemoveStrategy.t.sol b/test/unit/RemoveStrategy.t.sol index 8f9d1d4b..e471c02d 100644 --- a/test/unit/RemoveStrategy.t.sol +++ b/test/unit/RemoveStrategy.t.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault} from "../common/FourSixTwoSixAggBase.t.sol"; contract RemoveStrategyTest is FourSixTwoSixAggBase { uint256 strategyAllocationPoints; + IEVault anotherStrategy; + function setUp() public virtual override { super.setUp(); - strategyAllocationPoints = type(uint120).max; + strategyAllocationPoints = 1000e18; _addStrategy(manager, address(eTST), strategyAllocationPoints); } @@ -28,6 +30,26 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore - 1); } + function testRemoveStrategyWithMultipleStrategies() public { + anotherStrategy = IEVault( + factory.createProxy(address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount)) + ); + _addStrategy(manager, address(anotherStrategy), strategyAllocationPoints); + + uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 withdrawalQueueLengthBefore = fourSixTwoSixAgg.withdrawalQueueLength(); + + vm.prank(manager); + fourSixTwoSixAgg.removeStrategy(address(eTST)); + + FourSixTwoSixAgg.Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); + + assertEq(strategyAfter.active, false); + assertEq(strategyAfter.allocationPoints, 0); + assertEq(fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); + assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore - 1); + } + function testRemoveStrategy_fromUnauthorized() public { vm.prank(deployer); vm.expectRevert(); diff --git a/test/unit/ReorderWithdrawalQueueTest.t.sol b/test/unit/ReorderWithdrawalQueueTest.t.sol new file mode 100644 index 00000000..a68e8341 --- /dev/null +++ b/test/unit/ReorderWithdrawalQueueTest.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault} from "../common/FourSixTwoSixAggBase.t.sol"; + +contract ReorderWithdrawalQueueTest is FourSixTwoSixAggBase { + uint256 eTSTAllocationPoints = 500e18; + uint256 eTSTsecondaryAllocationPoints = 700e18; + + IEVault eTSTsecondary; + + function setUp() public virtual override { + super.setUp(); + + _addStrategy(manager, address(eTST), eTSTAllocationPoints); + + { + eTSTsecondary = IEVault( + factory.createProxy( + address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount) + ) + ); + } + _addStrategy(manager, address(eTSTsecondary), eTSTsecondaryAllocationPoints); + } + + function testReorderWithdrawalQueue() public { + assertEq( + fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(0)).allocationPoints, eTSTAllocationPoints + ); + assertEq( + fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(1)).allocationPoints, + eTSTsecondaryAllocationPoints + ); + + vm.prank(manager); + fourSixTwoSixAgg.reorderWithdrawalQueue(0, 1); + + assertEq( + fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(0)).allocationPoints, + eTSTsecondaryAllocationPoints + ); + assertEq( + fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(1)).allocationPoints, eTSTAllocationPoints + ); + } + + function testReorderWithdrawalQueueWhenOutOfBounds() public { + vm.startPrank(manager); + vm.expectRevert(FourSixTwoSixAgg.OutOfBounds.selector); + fourSixTwoSixAgg.reorderWithdrawalQueue(0, 3); + vm.stopPrank(); + } + + function testReorderWithdrawalQueueWhenSameIndex() public { + vm.startPrank(manager); + vm.expectRevert(FourSixTwoSixAgg.SameIndexes.selector); + fourSixTwoSixAgg.reorderWithdrawalQueue(1, 1); + vm.stopPrank(); + } +} From 23258895797693b812afb62440091d07b99c0057 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 18:54:19 +0100 Subject: [PATCH 18/19] chore: re-order functions --- src/FourSixTwoSixAgg.sol | 336 +++++++++++++++++++-------------------- 1 file changed, 168 insertions(+), 168 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index cdba05dd..758a71e3 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -139,6 +139,130 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { _setRoleAdmin(STRATEGY_REMOVER_ROLE, STRATEGY_REMOVER_ROLE_ADMIN_ROLE); } + /// @notice Rebalance strategy allocation. + /// @dev This function will first harvest yield, gulps and update interest. + /// @dev If current allocation is greater than target allocation, the aggregator will withdraw the excess assets. + /// If current allocation is less than target allocation, the aggregator will: + /// - Try to deposit the delta, if the cash is not sufficient, deposit all the available cash + /// - If all the available cash is greater than the max deposit, deposit the max deposit + function rebalance(address strategy) public nonReentrant { + _rebalance(strategy); + } + + function rebalanceMultipleStrategies(address[] calldata _strategies) external nonReentrant { + for (uint256 i; i < _strategies.length; ++i) { + _rebalance(_strategies[i]); + } + } + + /// @notice Harvest positive yield. + /// @param strategy address of strategy + function harvest(address strategy) external nonReentrant { + _harvest(strategy); + + _gulp(); + } + + function harvestMultipleStrategies(address[] calldata _strategies) external nonReentrant { + for (uint256 i; i < _strategies.length; ++i) { + _harvest(_strategies[i]); + + _gulp(); + } + } + + /// @notice Adjust a certain strategy's allocation points. + /// @dev Can only be called by an address that have the ALLOCATION_ADJUSTER_ROLE + /// @param strategy address of strategy + /// @param newPoints new strategy's points + function adjustAllocationPoints(address strategy, uint256 newPoints) + external + nonReentrant + onlyRole(ALLOCATION_ADJUSTER_ROLE) + { + Strategy memory strategyDataCache = strategies[strategy]; + uint256 totalAllocationPointsCache = totalAllocationPoints; + + if (!strategyDataCache.active) { + revert InactiveStrategy(); + } + + strategies[strategy].allocationPoints = uint120(newPoints); + + totalAllocationPoints = (newPoints > strategyDataCache.allocationPoints) + ? totalAllocationPointsCache + (newPoints - strategyDataCache.allocationPoints) + : totalAllocationPointsCache - (strategyDataCache.allocationPoints - newPoints); + } + + /// @notice Swap two strategies indexes in the withdrawal queue. + /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_REORDERER_ROLE. + /// @param index1 index of first strategy + /// @param index2 index of second strategy + function reorderWithdrawalQueue(uint8 index1, uint8 index2) + external + nonReentrant + onlyRole(WITHDRAW_QUEUE_REORDERER_ROLE) + { + if (index1 >= withdrawalQueue.length || index2 >= withdrawalQueue.length) { + revert OutOfBounds(); + } + + if (index1 == index2) { + revert SameIndexes(); + } + + (withdrawalQueue[index1], withdrawalQueue[index2]) = (withdrawalQueue[index2], withdrawalQueue[index1]); + } + + /// @notice Add new strategy with it's allocation points. + /// @dev Can only be called by an address that have STRATEGY_ADDER_ROLE. + /// @param strategy Address of the strategy + /// @param allocationPoints Strategy's allocation points + function addStrategy(address strategy, uint256 allocationPoints) + external + nonReentrant + onlyRole(STRATEGY_ADDER_ROLE) + { + if (IERC4626(strategy).asset() != asset()) { + revert InvalidStrategyAsset(); + } + + if (strategies[strategy].active) { + revert StrategyAlreadyExist(); + } + + strategies[strategy] = Strategy({allocated: 0, allocationPoints: uint120(allocationPoints), active: true}); + + totalAllocationPoints += allocationPoints; + withdrawalQueue.push(strategy); + } + + /// @notice Remove strategy and set its allocation points to zero. + /// @dev This function does not pull funds, `harvest()` needs to be called to withdraw + /// @dev Can only be called by an address that have the STRATEGY_REMOVER_ROLE + /// @param strategy Address of the strategy + function removeStrategy(address strategy) external nonReentrant onlyRole(STRATEGY_REMOVER_ROLE) { + if (!strategies[strategy].active) { + revert AlreadyRemoved(); + } + + strategies[strategy].active = false; + totalAllocationPoints -= strategies[strategy].allocationPoints; + strategies[strategy].allocationPoints = 0; + + // remove from withdrawalQueue + uint256 lastStrategyIndex = withdrawalQueue.length - 1; + + for (uint256 i = 0; i <= lastStrategyIndex; i++) { + if ((withdrawalQueue[i] == strategy) && (i != lastStrategyIndex)) { + (withdrawalQueue[i], withdrawalQueue[lastStrategyIndex]) = + (withdrawalQueue[lastStrategyIndex], withdrawalQueue[i]); + } + } + + withdrawalQueue.pop(); + } + /// @notice Get strategy params. /// @param _strategy strategy's address /// @return Strategy struct @@ -152,19 +276,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return withdrawalQueue.length; } - /// @notice Return the total amount of assets deposited, plus the accrued interest. - /// @return uint256 total amount - function totalAssets() public view override returns (uint256) { - return totalAssetsDeposited + interestAccrued(); - } - - /// @notice get the total assets allocatable - /// @dev the total assets allocatable is the amount of assets deposited into the aggregator + assets already deposited into strategies - /// @return uint256 total assets - function totalAssetsAllocatable() public view returns (uint256) { - return IERC20(asset()).balanceOf(address(this)) + totalAllocated; - } - /// @notice Transfers a certain amount of tokens to a recipient. /// @param to The recipient of the transfer. /// @param amount The amount shares to transfer. @@ -232,6 +343,50 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return super.redeem(shares, receiver, owner); } + function gulp() public nonReentrant { + _gulp(); + } + + function updateInterestAndReturnESRSlotCache() public returns (ESRSlot memory) { + ESRSlot memory esrSlotCache = esrSlot; + uint256 accruedInterest = interestAccruedFromCache(esrSlotCache); + + // it's safe to down-cast because the accrued interest is a fraction of interest left + esrSlotCache.interestLeft -= uint168(accruedInterest); + esrSlotCache.lastInterestUpdate = uint40(block.timestamp); + // write esrSlotCache back to storage in a single SSTORE + esrSlot = esrSlotCache; + // Move interest accrued to totalAssetsDeposited + totalAssetsDeposited += accruedInterest; + + return esrSlotCache; + } + + /// @notice Return the total amount of assets deposited, plus the accrued interest. + /// @return uint256 total amount + function totalAssets() public view override returns (uint256) { + return totalAssetsDeposited + interestAccrued(); + } + + /// @notice get the total assets allocatable + /// @dev the total assets allocatable is the amount of assets deposited into the aggregator + assets already deposited into strategies + /// @return uint256 total assets + function totalAssetsAllocatable() public view returns (uint256) { + return IERC20(asset()).balanceOf(address(this)) + totalAllocated; + } + + /// @notice Return the accrued interest + /// @return uint256 accrued interest + function interestAccrued() public view returns (uint256) { + return interestAccruedFromCache(esrSlot); + } + + /// @notice Return the ESRSlot struct + /// @return ESRSlot struct + function getESRSlot() public view returns (ESRSlot memory) { + return esrSlot; + } + /// @dev Increate the total assets deposited, and call IERC4626._deposit() /// @dev See {IERC4626-_deposit}. function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override { @@ -286,10 +441,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { super._withdraw(caller, receiver, owner, assets, shares); } - function gulp() public nonReentrant { - _gulp(); - } - function _gulp() internal { ESRSlot memory esrSlotCache = updateInterestAndReturnESRSlotCache(); uint256 toGulp = totalAssetsAllocatable() - totalAssetsDeposited - esrSlotCache.interestLeft; @@ -304,37 +455,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { esrSlot = esrSlotCache; } - function updateInterestAndReturnESRSlotCache() public returns (ESRSlot memory) { - ESRSlot memory esrSlotCache = esrSlot; - uint256 accruedInterest = interestAccruedFromCache(esrSlotCache); - - // it's safe to down-cast because the accrued interest is a fraction of interest left - esrSlotCache.interestLeft -= uint168(accruedInterest); - esrSlotCache.lastInterestUpdate = uint40(block.timestamp); - // write esrSlotCache back to storage in a single SSTORE - esrSlot = esrSlotCache; - // Move interest accrued to totalAssetsDeposited - totalAssetsDeposited += accruedInterest; - - return esrSlotCache; - } - - function rebalanceMultipleStrategies(address[] calldata _strategies) external nonReentrant { - for (uint256 i; i < _strategies.length; ++i) { - _rebalance(_strategies[i]); - } - } - - /// @notice Rebalance strategy allocation. - /// @dev This function will first harvest yield, gulps and update interest. - /// @dev If current allocation is greater than target allocation, the aggregator will withdraw the excess assets. - /// If current allocation is less than target allocation, the aggregator will: - /// - Try to deposit the delta, if the cash is not sufficient, deposit all the available cash - /// - If all the available cash is greater than the max deposit, deposit the max deposit - function rebalance(address strategy) public nonReentrant { - _rebalance(strategy); - } - function _rebalance(address _strategy) internal { if (_strategy == address(0)) { return; //nothing to rebalance as this is the cash reserve @@ -395,22 +515,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { } } - /// @notice Harvest positive yield. - /// @param strategy address of strategy - function harvest(address strategy) public nonReentrant { - _harvest(strategy); - - _gulp(); - } - - function harvestMultipleStrategies(address[] calldata _strategies) external nonReentrant { - for (uint256 i; i < _strategies.length; ++i) { - _harvest(_strategies[i]); - - _gulp(); - } - } - function _harvest(address strategy) internal { Strategy memory strategyData = strategies[strategy]; @@ -433,104 +537,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { } } - /// @notice Adjust a certain strategy's allocation points. - /// @dev Can only be called by an address that have the ALLOCATION_ADJUSTER_ROLE - /// @param strategy address of strategy - /// @param newPoints new strategy's points - function adjustAllocationPoints(address strategy, uint256 newPoints) - public - nonReentrant - onlyRole(ALLOCATION_ADJUSTER_ROLE) - { - Strategy memory strategyDataCache = strategies[strategy]; - uint256 totalAllocationPointsCache = totalAllocationPoints; - - if (!strategyDataCache.active) { - revert InactiveStrategy(); - } - - strategies[strategy].allocationPoints = uint120(newPoints); - - totalAllocationPoints = (newPoints > strategyDataCache.allocationPoints) - ? totalAllocationPointsCache + (newPoints - strategyDataCache.allocationPoints) - : totalAllocationPointsCache - (strategyDataCache.allocationPoints - newPoints); - } - - /// @notice Swap two strategies indexes in the withdrawal queue. - /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_REORDERER_ROLE. - /// @param index1 index of first strategy - /// @param index2 index of second strategy - function reorderWithdrawalQueue(uint8 index1, uint8 index2) - public - nonReentrant - onlyRole(WITHDRAW_QUEUE_REORDERER_ROLE) - { - if (index1 >= withdrawalQueue.length || index2 >= withdrawalQueue.length) { - revert OutOfBounds(); - } - - if (index1 == index2) { - revert SameIndexes(); - } - - (withdrawalQueue[index1], withdrawalQueue[index2]) = (withdrawalQueue[index2], withdrawalQueue[index1]); - } - - /// @notice Add new strategy with it's allocation points. - /// @dev Can only be called by an address that have STRATEGY_ADDER_ROLE. - /// @param strategy Address of the strategy - /// @param allocationPoints Strategy's allocation points - function addStrategy(address strategy, uint256 allocationPoints) - public - nonReentrant - onlyRole(STRATEGY_ADDER_ROLE) - { - if (IERC4626(strategy).asset() != asset()) { - revert InvalidStrategyAsset(); - } - - if (strategies[strategy].active) { - revert StrategyAlreadyExist(); - } - - strategies[strategy] = Strategy({allocated: 0, allocationPoints: uint120(allocationPoints), active: true}); - - totalAllocationPoints += allocationPoints; - withdrawalQueue.push(strategy); - } - - /// @notice Remove strategy and set its allocation points to zero. - /// @dev This function does not pull funds, `harvest()` needs to be called to withdraw - /// @dev Can only be called by an address that have the STRATEGY_REMOVER_ROLE - /// @param strategy Address of the strategy - function removeStrategy(address strategy) public nonReentrant onlyRole(STRATEGY_REMOVER_ROLE) { - if (!strategies[strategy].active) { - revert AlreadyRemoved(); - } - - strategies[strategy].active = false; - totalAllocationPoints -= strategies[strategy].allocationPoints; - strategies[strategy].allocationPoints = 0; - - // remove from withdrawalQueue - uint256 lastStrategyIndex = withdrawalQueue.length - 1; - - for (uint256 i = 0; i <= lastStrategyIndex; i++) { - if ((withdrawalQueue[i] == strategy) && (i != lastStrategyIndex)) { - (withdrawalQueue[i], withdrawalQueue[lastStrategyIndex]) = - (withdrawalQueue[lastStrategyIndex], withdrawalQueue[i]); - } - } - - withdrawalQueue.pop(); - } - - /// @notice Return the accrued interest - /// @return uint256 accrued interest - function interestAccrued() public view returns (uint256) { - return interestAccruedFromCache(esrSlot); - } - /// @dev Get accrued interest without updating it. /// @param esrSlotCache Cached esrSlot /// @return uint256 accrued interest @@ -552,12 +558,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return esrSlotCache.interestLeft * timePassed / totalDuration; } - /// @notice Return the ESRSlot struct - /// @return ESRSlot struct - function getESRSlot() public view returns (ESRSlot memory) { - return esrSlot; - } - /// @notice Retrieves the message sender in the context of the EVC. /// @dev This function returns the account on behalf of which the current operation is being performed, which is /// either msg.sender or the account authenticated by the EVC. From c1356205722aa1aecf775ae31d6a2d466a65352f Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 20 May 2024 18:57:10 +0100 Subject: [PATCH 19/19] change to external --- src/FourSixTwoSixAgg.sol | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 758a71e3..6ebeac5a 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -145,7 +145,7 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { /// If current allocation is less than target allocation, the aggregator will: /// - Try to deposit the delta, if the cash is not sufficient, deposit all the available cash /// - If all the available cash is greater than the max deposit, deposit the max deposit - function rebalance(address strategy) public nonReentrant { + function rebalance(address strategy) external nonReentrant { _rebalance(strategy); } @@ -263,6 +263,10 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { withdrawalQueue.pop(); } + function gulp() external nonReentrant { + _gulp(); + } + /// @notice Get strategy params. /// @param _strategy strategy's address /// @return Strategy struct @@ -276,6 +280,18 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return withdrawalQueue.length; } + /// @notice Return the ESRSlot struct + /// @return ESRSlot struct + function getESRSlot() external view returns (ESRSlot memory) { + return esrSlot; + } + + /// @notice Return the accrued interest + /// @return uint256 accrued interest + function interestAccrued() external view returns (uint256) { + return interestAccruedFromCache(esrSlot); + } + /// @notice Transfers a certain amount of tokens to a recipient. /// @param to The recipient of the transfer. /// @param amount The amount shares to transfer. @@ -343,10 +359,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return super.redeem(shares, receiver, owner); } - function gulp() public nonReentrant { - _gulp(); - } - function updateInterestAndReturnESRSlotCache() public returns (ESRSlot memory) { ESRSlot memory esrSlotCache = esrSlot; uint256 accruedInterest = interestAccruedFromCache(esrSlotCache); @@ -365,7 +377,7 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { /// @notice Return the total amount of assets deposited, plus the accrued interest. /// @return uint256 total amount function totalAssets() public view override returns (uint256) { - return totalAssetsDeposited + interestAccrued(); + return totalAssetsDeposited + interestAccruedFromCache(esrSlot); } /// @notice get the total assets allocatable @@ -375,18 +387,6 @@ contract FourSixTwoSixAgg is EVCUtil, ERC4626, AccessControlEnumerable { return IERC20(asset()).balanceOf(address(this)) + totalAllocated; } - /// @notice Return the accrued interest - /// @return uint256 accrued interest - function interestAccrued() public view returns (uint256) { - return interestAccruedFromCache(esrSlot); - } - - /// @notice Return the ESRSlot struct - /// @return ESRSlot struct - function getESRSlot() public view returns (ESRSlot memory) { - return esrSlot; - } - /// @dev Increate the total assets deposited, and call IERC4626._deposit() /// @dev See {IERC4626-_deposit}. function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override {