From a6052657b38e3060ad0239a0f06270bf6cf8b80c Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:01:02 +0300 Subject: [PATCH 01/19] refactor --- src/FourSixTwoSixAgg.sol | 134 ++++++------------ src/interface/IFourSixTwoSixAgg.sol | 4 +- test/common/FourSixTwoSixAggBase.t.sol | 58 +++++--- test/e2e/BalanceForwarderE2ETest.t.sol | 36 +++-- ...positRebalanceHarvestWithdrawE2ETest.t.sol | 5 +- .../fuzz/AdjustAllocationPointsFuzzTest.t.sol | 4 +- test/unit/AddStrategyTest.t.sol | 12 +- test/unit/AdjustAllocationPointsTest.t.sol | 4 +- test/unit/RemoveStrategy.t.sol | 8 +- test/unit/ReorderWithdrawalQueueTest.t.sol | 32 ++--- 10 files changed, 137 insertions(+), 160 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index adc22130..8ba1de5c 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -12,8 +12,9 @@ import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; // internal dep import {Hooks} from "./Hooks.sol"; -import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; import {BalanceForwarder, IBalanceForwarder} from "./BalanceForwarder.sol"; +import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; /// @dev Do NOT use with fee on transfer tokens /// @dev Do NOT use with rebasing tokens @@ -26,11 +27,8 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 error Reentrancy(); error ArrayLengthMismatch(); error InitialAllocationPointsZero(); - error NotEnoughAssets(); error NegativeYield(); error InactiveStrategy(); - error OutOfBounds(); - error SameIndexes(); error InvalidStrategyAsset(); error StrategyAlreadyExist(); error AlreadyRemoved(); @@ -47,8 +45,6 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 // Roles bytes32 public constant STRATEGY_MANAGER = keccak256("STRATEGY_MANAGER"); bytes32 public constant STRATEGY_MANAGER_ADMIN = keccak256("STRATEGY_MANAGER_ADMIN"); - bytes32 public constant WITHDRAW_QUEUE_MANAGER = keccak256("WITHDRAW_QUEUE_MANAGER"); - bytes32 public constant WITHDRAW_QUEUE_MANAGER_ADMIN = keccak256("WITHDRAW_QUEUE_MANAGER_ADMIN"); bytes32 public constant STRATEGY_ADDER = keccak256("STRATEGY_ADDER"); bytes32 public constant STRATEGY_ADDER_ADMIN = keccak256("STRATEGY_ADDER_ADMIN"); bytes32 public constant STRATEGY_REMOVER = keccak256("STRATEGY_REMOVER"); @@ -76,8 +72,7 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @dev fee recipient address address public feeRecipient; - /// @dev An array of strategy addresses to withdraw from - address[] public withdrawalQueue; + address public withdrawalQueue; /// @dev Mapping between strategy address and it's allocation config mapping(address => Strategy) internal strategies; @@ -101,7 +96,6 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 event Gulp(uint256 interestLeft, uint256 interestSmearEnd); event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); - event ReorderWithdrawalQueue(uint8 index1, uint8 index2); event AddStrategy(address indexed strategy, uint256 allocationPoints); event RemoveStrategy(address indexed _strategy); event AccruePerformanceFee(address indexed feeRecipient, uint256 yield, uint256 feeAssets); @@ -128,6 +122,9 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 constructor( address _evc, address _balanceTracker, + address _withdrawalQueue, + address _rebalancer, + address _owner, address _asset, string memory _name, string memory _symbol, @@ -140,6 +137,8 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 if (_initialStrategies.length != _initialStrategiesAllocationPoints.length) revert ArrayLengthMismatch(); if (_initialCashAllocationPoints == 0) revert InitialAllocationPointsZero(); + withdrawalQueue = _withdrawalQueue; + strategies[address(0)] = Strategy({allocated: 0, allocationPoints: _initialCashAllocationPoints.toUint120(), active: true, cap: 0}); @@ -160,19 +159,22 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 }); cachedTotalAllocationPoints += _initialStrategiesAllocationPoints[i]; - withdrawalQueue.push(_initialStrategies[i]); } totalAllocationPoints = cachedTotalAllocationPoints; + IWithdrawalQueue(withdrawalQueue).initWithdrawalQueue(_owner, _initialStrategies); // Setup DEFAULT_ADMIN - _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + _grantRole(DEFAULT_ADMIN_ROLE, _owner); + // By default, the Rebalancer contract is assigned the REBALANCER role + _grantRole(REBALANCER, _rebalancer); // Setup role admins _setRoleAdmin(STRATEGY_MANAGER, STRATEGY_MANAGER_ADMIN); - _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); + // _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); _setRoleAdmin(STRATEGY_ADDER, STRATEGY_ADDER_ADMIN); _setRoleAdmin(STRATEGY_REMOVER, STRATEGY_REMOVER_ADMIN); _setRoleAdmin(MANAGER, MANAGER_ADMIN); + _setRoleAdmin(REBALANCER, REBALANCER_ADMIN); } /// @notice Set performance fee recipient address @@ -247,7 +249,8 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @notice Harvest strategy. /// @param strategy address of strategy - function harvest(address strategy) external nonReentrant { + //TODO: is this safe without the reentrancy check + function harvest(address strategy) external { _harvest(strategy); _gulp(); @@ -319,29 +322,6 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 emit SetStrategyCap(_strategy, _cap); } - /// @notice Swap two strategies indexes in the withdrawal queue. - /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER. - /// @param _index1 index of first strategy - /// @param _index2 index of second strategy - function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) - external - nonReentrant - onlyRole(WITHDRAW_QUEUE_MANAGER) - { - uint256 length = withdrawalQueue.length; - if (_index1 >= length || _index2 >= length) { - revert OutOfBounds(); - } - - if (_index1 == _index2) { - revert SameIndexes(); - } - - (withdrawalQueue[_index1], withdrawalQueue[_index2]) = (withdrawalQueue[_index2], withdrawalQueue[_index1]); - - emit ReorderWithdrawalQueue(_index1, _index2); - } - /// @notice Add new strategy with it's allocation points. /// @dev Can only be called by an address that have STRATEGY_ADDER. /// @param _strategy Address of the strategy @@ -361,7 +341,7 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 Strategy({allocated: 0, allocationPoints: _allocationPoints.toUint120(), active: true, cap: 0}); totalAllocationPoints += _allocationPoints; - withdrawalQueue.push(_strategy); + IWithdrawalQueue(withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); emit AddStrategy(_strategy, _allocationPoints); } @@ -386,17 +366,7 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 strategyStorage.allocationPoints = 0; // remove from withdrawalQueue - uint256 lastStrategyIndex = withdrawalQueue.length - 1; - - for (uint256 i = 0; i < lastStrategyIndex; ++i) { - if (withdrawalQueue[i] == _strategy) { - withdrawalQueue[i] = withdrawalQueue[lastStrategyIndex]; - - break; - } - } - - withdrawalQueue.pop(); + IWithdrawalQueue(withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); emit RemoveStrategy(_strategy); } @@ -419,12 +389,6 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 return strategies[_strategy]; } - /// @notice Return the withdrawal queue length. - /// @return uint256 length - function withdrawalQueueLength() external view returns (uint256) { - return withdrawalQueue.length; - } - /// @notice Return the ESR struct /// @return ESR struct function getESRSlot() external view returns (ESR memory) { @@ -533,47 +497,43 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 totalAssetsDeposited -= assets; uint256 assetsRetrieved = IERC20(asset()).balanceOf(address(this)); - if (assetsRetrieved < assets) assetsRetrieved = _withdrawFromStrategies(assetsRetrieved, assets); if (assetsRetrieved < assets) { - revert NotEnoughAssets(); + IWithdrawalQueue(withdrawalQueue).executeWithdrawFromQueue( + caller, receiver, owner, assets, shares, assetsRetrieved + ); + } else { + _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); } - - _gulp(); - - super._withdraw(caller, receiver, owner, assets, shares); } - /// @dev Withdraw needed asset amount from strategies. - /// @param _currentBalance Aggregator asset balance. - /// @param _targetBalance target balance. - /// @return uint256 current balance after withdraw. - function _withdrawFromStrategies(uint256 _currentBalance, uint256 _targetBalance) internal returns (uint256) { - uint256 numStrategies = withdrawalQueue.length; - for (uint256 i; i < numStrategies; ++i) { - IERC4626 strategy = IERC4626(withdrawalQueue[i]); - - _harvest(address(strategy)); - - uint256 underlyingBalance = strategy.maxWithdraw(address(this)); - uint256 desiredAssets = _targetBalance - _currentBalance; - uint256 withdrawAmount = (underlyingBalance > desiredAssets) ? desiredAssets : underlyingBalance; + // TODO: add access control + function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { + // Update allocated assets + strategies[_strategy].allocated -= uint120(_withdrawAmount); + totalAllocated -= _withdrawAmount; - // Update allocated assets - strategies[address(strategy)].allocated -= uint120(withdrawAmount); - totalAllocated -= withdrawAmount; - - // update assetsRetrieved - _currentBalance += withdrawAmount; + // Do actual withdraw from strategy + IERC4626(_strategy).withdraw(_withdrawAmount, address(this), address(this)); + } - // Do actual withdraw from strategy - strategy.withdraw(withdrawAmount, address(this), address(this)); + // TODO: add access control + function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) + external + { + _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + } - if (_currentBalance >= _targetBalance) { - break; - } - } + // TODO: add access control + function _executeWithdrawFromReserve( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares + ) internal { + _gulp(); - return _currentBalance; + super._withdraw(caller, receiver, owner, assets, shares); } /// @dev gulp positive yield and increment the left interest diff --git a/src/interface/IFourSixTwoSixAgg.sol b/src/interface/IFourSixTwoSixAgg.sol index b8323236..c8fdbc84 100644 --- a/src/interface/IFourSixTwoSixAgg.sol +++ b/src/interface/IFourSixTwoSixAgg.sol @@ -17,7 +17,9 @@ interface IFourSixTwoSixAgg { function rebalance(address _strategy, uint256 _amountToRebalance, bool _isDeposit) external; function gulp() external; function harvest(address strategy) external; - + function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external; + function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) + external; function getStrategy(address _strategy) external view returns (Strategy memory); function totalAllocationPoints() external view returns (uint256); function totalAllocated() external view returns (uint256); diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index fa71ddb0..a6160abb 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -6,6 +6,9 @@ import {FourSixTwoSixAgg} from "../../src/FourSixTwoSixAgg.sol"; import {Rebalancer} from "../../src/Rebalancer.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; import {Hooks} from "../../src/Hooks.sol"; +import {FourSixTwoSixAggFactory} from "../../src/FourSixTwoSixAggFactory.sol"; +import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; +import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; contract FourSixTwoSixAggBase is EVaultTestBase { uint256 public constant CASH_RESERVE_ALLOCATION_POINTS = 1000e18; @@ -15,9 +18,12 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address user2; address manager; - FourSixTwoSixAgg fourSixTwoSixAgg; + FourSixTwoSixAggFactory fourSixTwoSixAggFactory; Rebalancer rebalancer; + FourSixTwoSixAgg fourSixTwoSixAgg; + WithdrawalQueue withdrawalQueue; + function setUp() public virtual override { super.setUp(); @@ -27,34 +33,34 @@ contract FourSixTwoSixAggBase is EVaultTestBase { vm.startPrank(deployer); rebalancer = new Rebalancer(); - fourSixTwoSixAgg = new FourSixTwoSixAgg( - address(evc), - address(0), - address(assetTST), - "assetTST_Agg", - "assetTST_Agg", - CASH_RESERVE_ALLOCATION_POINTS, - new address[](0), - new uint256[](0) + fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), address(0), address(rebalancer)); + + fourSixTwoSixAgg = FourSixTwoSixAgg( + fourSixTwoSixAggFactory.deployEulerAggregationLayer( + address(assetTST), + "assetTST_Agg", + "assetTST_Agg", + CASH_RESERVE_ALLOCATION_POINTS, + new address[](0), + new uint256[](0) + ) ); + withdrawalQueue = WithdrawalQueue(fourSixTwoSixAgg.withdrawalQueue()); // grant admin roles to deployer fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.REBALANCER_ADMIN(), deployer); + withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); // grant roles to manager fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER(), manager); - - // grant rebalancing role - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.REBALANCER(), address(rebalancer)); + withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager); vm.stopPrank(); } @@ -70,10 +76,6 @@ contract FourSixTwoSixAggBase is EVaultTestBase { fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_MANAGER()), fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN() ); - assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER()), - fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN() - ); assertEq( fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_ADDER()), fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN() ); @@ -82,18 +84,22 @@ contract FourSixTwoSixAggBase is EVaultTestBase { fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN() ); assertEq(fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.MANAGER()), fourSixTwoSixAgg.MANAGER_ADMIN()); + assertEq( + withdrawalQueue.getRoleAdmin(withdrawalQueue.WITHDRAW_QUEUE_MANAGER()), + withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN() + ); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer)); + assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER(), manager)); + assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager)); } function _addStrategy(address from, address strategy, uint256 allocationPoints) internal { @@ -101,12 +107,18 @@ contract FourSixTwoSixAggBase is EVaultTestBase { fourSixTwoSixAgg.addStrategy(strategy, allocationPoints); } + function _getWithdrawalQueueLength() internal view returns (uint256) { + uint256 length = withdrawalQueue.withdrawalQueueLength(); + + return length; + } + function _getWithdrawalQueue() internal view returns (address[] memory) { - uint256 length = fourSixTwoSixAgg.withdrawalQueueLength(); + uint256 length = withdrawalQueue.withdrawalQueueLength(); address[] memory queue = new address[](length); for (uint256 i = 0; i < length; ++i) { - queue[i] = fourSixTwoSixAgg.withdrawalQueue(i); + queue[i] = withdrawalQueue.withdrawalQueue(i); } return queue; } diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 5e99ea0e..3f5ce3b1 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -8,7 +8,8 @@ import { EVault, IEVault, IRMTestDefault, - TestERC20 + TestERC20, + FourSixTwoSixAggFactory } from "../common/FourSixTwoSixAggBase.t.sol"; import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; @@ -23,27 +24,38 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { vm.startPrank(deployer); trackingReward = address(new TrackingRewardStreams(address(evc), 2 weeks)); - fourSixTwoSixAgg = new FourSixTwoSixAgg( - address(evc), - trackingReward, - address(assetTST), - "assetTST_Agg", - "assetTST_Agg", - CASH_RESERVE_ALLOCATION_POINTS, - new address[](0), - new uint256[](0) + // fourSixTwoSixAgg = new FourSixTwoSixAgg( + // address(evc), + // trackingReward, + // address(assetTST), + // "assetTST_Agg", + // "assetTST_Agg", + // CASH_RESERVE_ALLOCATION_POINTS, + // new address[](0), + // new uint256[](0) + // ); + fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), trackingReward, address(rebalancer)); + fourSixTwoSixAgg = FourSixTwoSixAgg( + fourSixTwoSixAggFactory.deployEulerAggregationLayer( + address(assetTST), + "assetTST_Agg", + "assetTST_Agg", + CASH_RESERVE_ALLOCATION_POINTS, + new address[](0), + new uint256[](0) + ) ); // grant admin roles to deployer fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); + // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer); // grant roles to manager fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager); + // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER(), manager); diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index 8aae2567..281e441f 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -8,7 +8,8 @@ import { EVault, IEVault, IRMTestDefault, - TestERC20 + TestERC20, + WithdrawalQueue } from "../common/FourSixTwoSixAggBase.t.sol"; contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { @@ -611,7 +612,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { uint256 amountToWithdraw = fourSixTwoSixAgg.balanceOf(user1); vm.prank(user1); - vm.expectRevert(FourSixTwoSixAgg.NotEnoughAssets.selector); + vm.expectRevert(WithdrawalQueue.NotEnoughAssets.selector); fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); } } diff --git a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol index c5415507..11cfc703 100644 --- a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol +++ b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol @@ -16,7 +16,7 @@ contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { uint256 strategyAllocationPoints = (fourSixTwoSixAgg.getStrategy(address(eTST))).allocationPoints; uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - uint256 withdrawalQueueLengthBefore = fourSixTwoSixAgg.withdrawalQueueLength(); + uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), _newAllocationPoints); @@ -34,7 +34,7 @@ contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { totalAllocationPointsBefore + (_newAllocationPoints - strategyAllocationPoints) ); } - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore); + assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore); assertEq(strategy.allocationPoints, _newAllocationPoints); } } diff --git a/test/unit/AddStrategyTest.t.sol b/test/unit/AddStrategyTest.t.sol index 97c9893b..493c13e9 100644 --- a/test/unit/AddStrategyTest.t.sol +++ b/test/unit/AddStrategyTest.t.sol @@ -12,18 +12,18 @@ contract AddStrategyTest is FourSixTwoSixAggBase { uint256 allocationPoints = 500e18; uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 0); + assertEq(_getWithdrawalQueueLength(), 0); _addStrategy(manager, address(eTST), allocationPoints); assertEq(fourSixTwoSixAgg.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 1); + assertEq(_getWithdrawalQueueLength(), 1); } function testAddStrategy_FromUnauthorizedAddress() public { uint256 allocationPoints = 500e18; - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 0); + assertEq(_getWithdrawalQueueLength(), 0); vm.expectRevert(); _addStrategy(deployer, address(eTST), allocationPoints); @@ -32,7 +32,7 @@ contract AddStrategyTest is FourSixTwoSixAggBase { function testAddStrategy_WithInvalidAsset() public { uint256 allocationPoints = 500e18; - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 0); + assertEq(_getWithdrawalQueueLength(), 0); vm.expectRevert(); _addStrategy(manager, address(eTST2), allocationPoints); @@ -42,12 +42,12 @@ contract AddStrategyTest is FourSixTwoSixAggBase { uint256 allocationPoints = 500e18; uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 0); + assertEq(_getWithdrawalQueueLength(), 0); _addStrategy(manager, address(eTST), allocationPoints); assertEq(fourSixTwoSixAgg.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), 1); + assertEq(_getWithdrawalQueueLength(), 1); vm.expectRevert(); _addStrategy(manager, address(eTST), allocationPoints); diff --git a/test/unit/AdjustAllocationPointsTest.t.sol b/test/unit/AdjustAllocationPointsTest.t.sol index da9ef58c..6e7f66fd 100644 --- a/test/unit/AdjustAllocationPointsTest.t.sol +++ b/test/unit/AdjustAllocationPointsTest.t.sol @@ -15,7 +15,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { function testAdjustAllocationPoints() public { uint256 newAllocationPoints = 859e18; uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - uint256 withdrawalQueueLengthBefore = fourSixTwoSixAgg.withdrawalQueueLength(); + uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); @@ -26,7 +26,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore + (newAllocationPoints - initialStrategyAllocationPoints) ); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore); + assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore); assertEq(strategy.allocationPoints, newAllocationPoints); } diff --git a/test/unit/RemoveStrategy.t.sol b/test/unit/RemoveStrategy.t.sol index 9bfb2f66..d90ee41b 100644 --- a/test/unit/RemoveStrategy.t.sol +++ b/test/unit/RemoveStrategy.t.sol @@ -17,7 +17,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { function testRemoveStrategy() public { uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - uint256 withdrawalQueueLengthBefore = fourSixTwoSixAgg.withdrawalQueueLength(); + uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); fourSixTwoSixAgg.removeStrategy(address(eTST)); @@ -27,7 +27,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); assertEq(fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore - 1); + assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore - 1); } function testRemoveStrategyWithMultipleStrategies() public { @@ -37,7 +37,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { _addStrategy(manager, address(anotherStrategy), strategyAllocationPoints); uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); - uint256 withdrawalQueueLengthBefore = fourSixTwoSixAgg.withdrawalQueueLength(); + uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); fourSixTwoSixAgg.removeStrategy(address(eTST)); @@ -47,7 +47,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); assertEq(fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); - assertEq(fourSixTwoSixAgg.withdrawalQueueLength(), withdrawalQueueLengthBefore - 1); + assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore - 1); } function testRemoveStrategy_WithdrawalQueueOrdering() public { diff --git a/test/unit/ReorderWithdrawalQueueTest.t.sol b/test/unit/ReorderWithdrawalQueueTest.t.sol index a68e8341..019ee516 100644 --- a/test/unit/ReorderWithdrawalQueueTest.t.sol +++ b/test/unit/ReorderWithdrawalQueueTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault, WithdrawalQueue} from "../common/FourSixTwoSixAggBase.t.sol"; contract ReorderWithdrawalQueueTest is FourSixTwoSixAggBase { uint256 eTSTAllocationPoints = 500e18; @@ -25,37 +25,27 @@ contract ReorderWithdrawalQueueTest is FourSixTwoSixAggBase { } function testReorderWithdrawalQueue() public { - assertEq( - fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(0)).allocationPoints, eTSTAllocationPoints - ); - assertEq( - fourSixTwoSixAgg.getStrategy(fourSixTwoSixAgg.withdrawalQueue(1)).allocationPoints, - eTSTsecondaryAllocationPoints - ); + assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTAllocationPoints); + assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[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 - ); + withdrawalQueue.reorderWithdrawalQueue(0, 1); + + assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTsecondaryAllocationPoints); + assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[1]).allocationPoints, eTSTAllocationPoints); } function testReorderWithdrawalQueueWhenOutOfBounds() public { vm.startPrank(manager); - vm.expectRevert(FourSixTwoSixAgg.OutOfBounds.selector); - fourSixTwoSixAgg.reorderWithdrawalQueue(0, 3); + vm.expectRevert(WithdrawalQueue.OutOfBounds.selector); + withdrawalQueue.reorderWithdrawalQueue(0, 3); vm.stopPrank(); } function testReorderWithdrawalQueueWhenSameIndex() public { vm.startPrank(manager); - vm.expectRevert(FourSixTwoSixAgg.SameIndexes.selector); - fourSixTwoSixAgg.reorderWithdrawalQueue(1, 1); + vm.expectRevert(WithdrawalQueue.SameIndexes.selector); + withdrawalQueue.reorderWithdrawalQueue(0, 0); vm.stopPrank(); } } From 81f619cc54b1845a39b8f80ba8f1e516c7119257 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:02:39 +0300 Subject: [PATCH 02/19] add files --- src/FourSixTwoSixAggFactory.sol | 46 +++++++++++ src/WithdrawalQueue.sol | 123 +++++++++++++++++++++++++++++ src/interface/IWithdrawalQueue.sol | 20 +++++ 3 files changed, 189 insertions(+) create mode 100644 src/FourSixTwoSixAggFactory.sol create mode 100644 src/WithdrawalQueue.sol create mode 100644 src/interface/IWithdrawalQueue.sol diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol new file mode 100644 index 00000000..5b169b7e --- /dev/null +++ b/src/FourSixTwoSixAggFactory.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {FourSixTwoSixAgg} from "./FourSixTwoSixAgg.sol"; +import {WithdrawalQueue} from "./WithdrawalQueue.sol"; + +contract FourSixTwoSixAggFactory { + address public immutable evc; + address public immutable balanceTracker; + address public immutable rebalancer; + + constructor(address _evc, address _balanceTracker, address _rebalancer) { + evc = _evc; + balanceTracker = _balanceTracker; + rebalancer = _rebalancer; + } + + // TODO: decrease bytecode size, use clones or something + function deployEulerAggregationLayer( + address _asset, + string memory _name, + string memory _symbol, + uint256 _initialCashAllocationPoints, + address[] memory _initialStrategies, + uint256[] memory _initialStrategiesAllocationPoints + ) external returns (address) { + address withdrawalQueueAddr = address(new WithdrawalQueue()); + address fourSixTwoSixAggAddr = address( + new FourSixTwoSixAgg( + evc, + balanceTracker, + withdrawalQueueAddr, + rebalancer, + msg.sender, + _asset, + _name, + _symbol, + _initialCashAllocationPoints, + _initialStrategies, + _initialStrategiesAllocationPoints + ) + ); + + return fourSixTwoSixAggAddr; + } +} diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol new file mode 100644 index 00000000..1ea0c269 --- /dev/null +++ b/src/WithdrawalQueue.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +// external dep +import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol"; +import {IERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; +// internal dep +import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; + +contract WithdrawalQueue is AccessControlEnumerable { + error OutOfBounds(); + error SameIndexes(); + error NotEnoughAssets(); + + bytes32 public constant WITHDRAW_QUEUE_MANAGER = keccak256("WITHDRAW_QUEUE_MANAGER"); + bytes32 public constant WITHDRAW_QUEUE_MANAGER_ADMIN = keccak256("WITHDRAW_QUEUE_MANAGER_ADMIN"); + + address public eulerAggregationVault; + + /// @dev An array of strategy addresses to withdraw from + address[] public withdrawalQueue; + + bool private isInitialized_; + + event ReorderWithdrawalQueue(uint8 index1, uint8 index2); + + function initWithdrawalQueue(address _owner, address[] calldata _initialStrategies) external { + if (isInitialized_) revert(); + + isInitialized_ = true; + + for (uint256 i; i < _initialStrategies.length; ++i) { + withdrawalQueue.push(_initialStrategies[i]); + } + + eulerAggregationVault = msg.sender; + + // Setup DEFAULT_ADMIN + _grantRole(DEFAULT_ADMIN_ROLE, _owner); + _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); + } + + // TODO: add access control + function addStrategyToWithdrawalQueue(address _strategy) external { + withdrawalQueue.push(_strategy); + } + + // TODO: add access control + function removeStrategyFromWithdrawalQueue(address _strategy) external { + uint256 lastStrategyIndex = withdrawalQueue.length - 1; + + for (uint256 i = 0; i < lastStrategyIndex; ++i) { + if (withdrawalQueue[i] == _strategy) { + withdrawalQueue[i] = withdrawalQueue[lastStrategyIndex]; + + break; + } + } + + withdrawalQueue.pop(); + } + + // TODO: add access control + function executeWithdrawFromQueue( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares, + uint256 availableAssets + ) external { + uint256 numStrategies = withdrawalQueue.length; + for (uint256 i; i < numStrategies; ++i) { + IERC4626 strategy = IERC4626(withdrawalQueue[i]); + + IFourSixTwoSixAgg(eulerAggregationVault).harvest(address(strategy)); + + uint256 underlyingBalance = strategy.maxWithdraw(eulerAggregationVault); + uint256 desiredAssets = assets - availableAssets; + uint256 withdrawAmount = (underlyingBalance > desiredAssets) ? desiredAssets : underlyingBalance; + + IFourSixTwoSixAgg(eulerAggregationVault).withdrawFromStrategy(address(strategy), withdrawAmount); + + // update assetsRetrieved + availableAssets += withdrawAmount; + + if (availableAssets >= assets) { + break; + } + } + + if (availableAssets < assets) { + revert NotEnoughAssets(); + } + + IFourSixTwoSixAgg(eulerAggregationVault).executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + } + + /// @notice Swap two strategies indexes in the withdrawal queue. + /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER. + /// @param _index1 index of first strategy + /// @param _index2 index of second strategy + function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { + uint256 length = withdrawalQueue.length; + if (_index1 >= length || _index2 >= length) { + revert OutOfBounds(); + } + + if (_index1 == _index2) { + revert SameIndexes(); + } + + (withdrawalQueue[_index1], withdrawalQueue[_index2]) = (withdrawalQueue[_index2], withdrawalQueue[_index1]); + + emit ReorderWithdrawalQueue(_index1, _index2); + } + + /// @notice Return the withdrawal queue length. + /// @return uint256 length + function withdrawalQueueLength() external view returns (uint256) { + return withdrawalQueue.length; + } +} diff --git a/src/interface/IWithdrawalQueue.sol b/src/interface/IWithdrawalQueue.sol new file mode 100644 index 00000000..d51c0550 --- /dev/null +++ b/src/interface/IWithdrawalQueue.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +interface IWithdrawalQueue { + function initWithdrawalQueue(address _owner, address[] calldata _initialStrategies) external; + function addStrategyToWithdrawalQueue(address _strategy) external; + function removeStrategyFromWithdrawalQueue(address _strategy) external; + function executeWithdrawFromQueue( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares, + uint256 availableAssets + ) external; + function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external; + + function withdrawalQueueLength() external view returns (uint256); + function withdrawalQueue(uint256 _index) external view returns (address); +} From f7886c3358f29ca6220f9a9c2d8f4156edb44931 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:11:47 +0300 Subject: [PATCH 03/19] forge install: openzeppelin-contracts-upgradeable v5.0.2 --- .gitmodules | 3 +++ lib/openzeppelin-contracts-upgradeable | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/openzeppelin-contracts-upgradeable diff --git a/.gitmodules b/.gitmodules index ae5ee093..5352aeba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ [submodule "lib/reward-streams"] path = lib/reward-streams url = https://github.com/euler-xyz/reward-streams +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 00000000..723f8cab --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 From d0193798a5bccc2b9affeb899f2ebaffce0124b6 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:05:11 +0300 Subject: [PATCH 04/19] refactor --- remappings.txt | 3 ++- src/FourSixTwoSixAgg.sol | 16 +++++++++------- src/Rebalancer.sol | 2 +- src/WithdrawalQueue.sol | 6 +++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/remappings.txt b/remappings.txt index 1ad598ca..644293a7 100644 --- a/remappings.txt +++ b/remappings.txt @@ -5,4 +5,5 @@ forge-std/=lib/forge-std/src/ evk/=lib/euler-vault-kit/ reward-streams=lib/reward-streams/src openzeppelin-contracts/=lib/reward-streams/lib/openzeppelin-contracts/contracts -@openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/ +@openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts +@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ \ No newline at end of file diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 8ba1de5c..b0f77ac1 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -2,13 +2,15 @@ pragma solidity ^0.8.0; // external dep -import {Context} from "@openzeppelin/utils/Context.sol"; -import {ERC20, IERC20} from "@openzeppelin/token/ERC20/ERC20.sol"; -import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; -import {ERC4626, IERC4626, Math} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; -import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol"; -import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol"; +import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +// import {ERC4626, IERC4626, Math} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; +// import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol"; +import {ERC4626Upgradeable, IERC4626, ERC20Upgradeable, Math} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; // internal dep import {Hooks} from "./Hooks.sol"; @@ -20,7 +22,7 @@ import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; /// @dev Do NOT use with rebasing tokens /// @dev Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol /// @dev inspired by Yearn v3 ❤️ -contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC4626, AccessControlEnumerable, Hooks { +contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC4626Upgradeable, AccessControlEnumerableUpgradeable, Hooks { using SafeERC20 for IERC20; using SafeCast for uint256; diff --git a/src/Rebalancer.sol b/src/Rebalancer.sol index 089ba335..6c5f554a 100644 --- a/src/Rebalancer.sol +++ b/src/Rebalancer.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; -import {IERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; +import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; contract Rebalancer { event ExecuteRebalance( diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index 1ea0c269..d2f5ac7e 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.0; // external dep -import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol"; -import {IERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; +import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; // internal dep import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; -contract WithdrawalQueue is AccessControlEnumerable { +contract WithdrawalQueue is AccessControlEnumerableUpgradeable { error OutOfBounds(); error SameIndexes(); error NotEnoughAssets(); From 9ebd543469d5954ff0af2b89c94009575eae66fa Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:05:25 +0300 Subject: [PATCH 05/19] forge install: openzeppelin-contracts v5.0.2 --- .gitmodules | 3 +++ lib/openzeppelin-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index 5352aeba..02ccc320 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 00000000..dbb6104c --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 From 0d8133ceb13d61cddfb496745dda6f7f812f4640 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:10:21 +0300 Subject: [PATCH 06/19] unstructured storage --- remappings.txt | 2 +- src/BalanceForwarder.sol | 65 +++- src/FourSixTwoSixAgg.sol | 403 +++++++++++++++++-------- src/FourSixTwoSixAggFactory.sol | 30 +- src/Hooks.sol | 30 +- src/WithdrawalQueue.sol | 78 +++-- src/interface/IWithdrawalQueue.sol | 2 +- test/common/FourSixTwoSixAggBase.t.sol | 2 +- test/e2e/BalanceForwarderE2ETest.t.sol | 12 +- test/e2e/PerformanceFeeE2ETest.t.sol | 13 +- test/unit/GulpTest.t.sol | 12 +- 11 files changed, 440 insertions(+), 209 deletions(-) diff --git a/remappings.txt b/remappings.txt index 644293a7..4d63589c 100644 --- a/remappings.txt +++ b/remappings.txt @@ -5,5 +5,5 @@ forge-std/=lib/forge-std/src/ evk/=lib/euler-vault-kit/ reward-streams=lib/reward-streams/src openzeppelin-contracts/=lib/reward-streams/lib/openzeppelin-contracts/contracts -@openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts +@openzeppelin/=lib/openzeppelin-contracts/ @openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ \ No newline at end of file diff --git a/src/BalanceForwarder.sol b/src/BalanceForwarder.sol index 1ceac9c6..8e082fba 100644 --- a/src/BalanceForwarder.sol +++ b/src/BalanceForwarder.sol @@ -13,16 +13,28 @@ abstract contract BalanceForwarder is IBalanceForwarder { error AlreadyEnabled(); error AlreadyDisabled(); - IBalanceTracker public immutable balanceTracker; + /// @custom:storage-location erc7201:euler_aggregation_vault.storage.BalanceForwarder + struct BalanceForwarderStorage { + IBalanceTracker balanceTracker; + mapping(address => bool) isBalanceForwarderEnabled; + } + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) + + bytes32 private constant BalanceForwarderStorageLocation = + 0xf0af7cc3986d6cac468ac54c07f5f08359b3a00d65f8e2a86f2676ec79073f00; - mapping(address => bool) internal isBalanceForwarderEnabled; + function _getBalanceForwarderStorage() private pure returns (BalanceForwarderStorage storage $) { + assembly { + $.slot := BalanceForwarderStorageLocation + } + } event EnableBalanceForwarder(address indexed _user); event DisableBalanceForwarder(address indexed _user); - constructor(address _balanceTracker) { - balanceTracker = IBalanceTracker(_balanceTracker); - } + // constructor(address _balanceTracker) { + // balanceTracker = IBalanceTracker(_balanceTracker); + // } /// @notice Enables balance forwarding for the authenticated account /// @dev Only the authenticated account can enable balance forwarding for itself @@ -37,22 +49,26 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address function balanceTrackerAddress() external view returns (address) { - return address(balanceTracker); + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + + return address($.balanceTracker); } /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract /// @param _account Address to query /// @return True if balance forwarder is enabled function balanceForwarderEnabled(address _account) external view returns (bool) { - return isBalanceForwarderEnabled[_account]; + return _balanceForwarderEnabled(_account); } function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { - if (address(balanceTracker) == address(0)) revert NotSupported(); - if (isBalanceForwarderEnabled[_sender]) revert AlreadyEnabled(); + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + + if (address($.balanceTracker) == address(0)) revert NotSupported(); + if ($.isBalanceForwarderEnabled[_sender]) revert AlreadyEnabled(); - isBalanceForwarderEnabled[_sender] = true; - balanceTracker.balanceTrackerHook(_sender, _senderBalance, false); + $.isBalanceForwarderEnabled[_sender] = true; + $.balanceTracker.balanceTrackerHook(_sender, _senderBalance, false); emit EnableBalanceForwarder(_sender); } @@ -61,12 +77,31 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @dev Only the authenticated account can disable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the account's balance of 0 function _disableBalanceForwarder(address _sender) internal { - if (address(balanceTracker) == address(0)) revert NotSupported(); - if (!isBalanceForwarderEnabled[_sender]) revert AlreadyDisabled(); + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); - isBalanceForwarderEnabled[_sender] = false; - balanceTracker.balanceTrackerHook(_sender, 0, false); + if (address($.balanceTracker) == address(0)) revert NotSupported(); + if (!$.isBalanceForwarderEnabled[_sender]) revert AlreadyDisabled(); + + $.isBalanceForwarderEnabled[_sender] = false; + $.balanceTracker.balanceTrackerHook(_sender, 0, false); emit DisableBalanceForwarder(_sender); } + + /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract + /// @param _account Address to query + /// @return True if balance forwarder is enabled + function _balanceForwarderEnabled(address _account) internal view returns (bool) { + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + + return $.isBalanceForwarderEnabled[_account]; + } + + /// @notice Retrieve the instance of rewards contract, tracking changes in account's balances + /// @return The balance tracker contract + function _balanceTracker() internal view returns (IBalanceTracker) { + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + + return $.balanceTracker; + } } diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index b0f77ac1..f8f30447 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -4,17 +4,21 @@ pragma solidity ^0.8.0; // external dep import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -// import {ERC4626, IERC4626, Math} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; -// import {AccessControlEnumerable} from "@openzeppelin/access/AccessControlEnumerable.sol"; -import {ERC4626Upgradeable, IERC4626, ERC20Upgradeable, Math} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; -import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import { + ERC4626Upgradeable, + IERC4626, + ERC20Upgradeable, + Math +} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol"; +import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; // internal dep import {Hooks} from "./Hooks.sol"; -import {BalanceForwarder, IBalanceForwarder} from "./BalanceForwarder.sol"; +import {BalanceForwarder, IBalanceForwarder, IBalanceTracker} from "./BalanceForwarder.sol"; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; @@ -22,7 +26,13 @@ import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; /// @dev Do NOT use with rebasing tokens /// @dev Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol /// @dev inspired by Yearn v3 ❤️ -contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC4626Upgradeable, AccessControlEnumerableUpgradeable, Hooks { +contract FourSixTwoSixAgg is + ERC4626Upgradeable, + AccessControlEnumerableUpgradeable, + BalanceForwarder, + Hooks, + IFourSixTwoSixAgg +{ using SafeERC20 for IERC20; using SafeCast for uint256; @@ -60,37 +70,62 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 uint256 internal constant MAX_PERFORMANCE_FEE = 0.5e18; uint256 public constant INTEREST_SMEAR = 2 weeks; - /// @dev store the interest rate smearing params - ESR internal esrSlot; - - /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract - uint256 public totalAssetsDeposited; - /// @dev Total amount of _asset deposited across all strategies. - uint256 public totalAllocated; - /// @dev Total amount of allocation points across all strategies including the cash reserve. - uint256 public totalAllocationPoints; - /// @dev fee rate - uint256 public performanceFee; - /// @dev fee recipient address - address public feeRecipient; - - address public withdrawalQueue; - - /// @dev Mapping between strategy address and it's allocation config - mapping(address => Strategy) internal strategies; + /// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVault + struct AggregationVaultStorage { + IEVC evc; + /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract + uint256 totalAssetsDeposited; + /// @dev Total amount of _asset deposited across all strategies. + uint256 totalAllocated; + /// @dev Total amount of allocation points across all strategies including the cash reserve. + uint256 totalAllocationPoints; + /// @dev fee rate + uint256 performanceFee; + /// @dev fee recipient address + address feeRecipient; + /// @dev Withdrawal queue contract's address + address withdrawalQueue; + /// @dev Mapping between strategy address and it's allocation config + mapping(address => Strategy) strategies; + } + + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant AggregationVaultStorageLocation = + 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; + + function _getAggregationVaultStorage() private pure returns (AggregationVaultStorage storage $) { + assembly { + $.slot := AggregationVaultStorageLocation + } + } /// @dev Euler saving rate struct /// lastInterestUpdate: last timestamo where interest was updated. /// interestSmearEnd: timestamp when the smearing of interest end. /// interestLeft: amount of interest left to smear. /// locked: if locked or not for update. - struct ESR { + /// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVaultSavingRate + struct AggregationVaultSavingRateStorage { uint40 lastInterestUpdate; uint40 interestSmearEnd; uint168 interestLeft; uint8 locked; } + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVaultSavingRate")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant AggregationVaultSavingRateStorageLocation = + 0xdb2394914f0f6fb18662eb905f211539008437f354f711cd6b8106daf3f40500; + + function _getAggregationVaultSavingRateStorage() + private + pure + returns (AggregationVaultSavingRateStorage storage $) + { + assembly { + $.slot := AggregationVaultSavingRateStorageLocation + } + } + event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); event SetPerformanceFee(uint256 oldFee, uint256 newFee); event OptInStrategyRewards(address indexed strategy); @@ -106,22 +141,37 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @dev Non reentrancy modifier for interest rate updates modifier nonReentrant() { - if (esrSlot.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - esrSlot.locked = REENTRANCYLOCK__LOCKED; + if (avsrCache.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); + + avsrCache.locked = REENTRANCYLOCK__LOCKED; _; - esrSlot.locked = REENTRANCYLOCK__UNLOCKED; - } - - /// @dev Constructor - /// @param _evc EVC address - /// @param _asset Aggregator's asset address - /// @param _name Aggregator's name - /// @param _symbol Aggregator's symbol - /// @param _initialCashAllocationPoints Initial points to be allocated to the cash reserve - /// @param _initialStrategies An array of initial strategies addresses - /// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points - constructor( + avsrCache.locked = REENTRANCYLOCK__UNLOCKED; + } + + // /// @dev Constructor + // /// @param _evc EVC address + // /// @param _asset Aggregator's asset address + // /// @param _name Aggregator's name + // /// @param _symbol Aggregator's symbol + // /// @param _initialCashAllocationPoints Initial points to be allocated to the cash reserve + // /// @param _initialStrategies An array of initial strategies addresses + // /// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points + + function _lock() private onlyInitializing { + AggregationVaultSavingRateStorage storage $ = _getAggregationVaultSavingRateStorage(); + + $.locked = REENTRANCYLOCK__UNLOCKED; + } + + function _setWithdrawalQueue(address _withdrawalQueue) private { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + $.withdrawalQueue = _withdrawalQueue; + } + + function init( address _evc, address _balanceTracker, address _withdrawalQueue, @@ -133,15 +183,20 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 uint256 _initialCashAllocationPoints, address[] memory _initialStrategies, uint256[] memory _initialStrategiesAllocationPoints - ) BalanceForwarder(_balanceTracker) EVCUtil(_evc) ERC4626(IERC20(_asset)) ERC20(_name, _symbol) { - esrSlot.locked = REENTRANCYLOCK__UNLOCKED; + ) external initializer { + __ERC4626_init_unchained(IERC20(_asset)); + __ERC20_init_unchained(_name, _symbol); + + _lock(); if (_initialStrategies.length != _initialStrategiesAllocationPoints.length) revert ArrayLengthMismatch(); if (_initialCashAllocationPoints == 0) revert InitialAllocationPointsZero(); - withdrawalQueue = _withdrawalQueue; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + $.withdrawalQueue = _withdrawalQueue; - strategies[address(0)] = + $.strategies[address(0)] = Strategy({allocated: 0, allocationPoints: _initialCashAllocationPoints.toUint120(), active: true, cap: 0}); uint256 cachedTotalAllocationPoints = _initialCashAllocationPoints; @@ -151,9 +206,9 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 revert InvalidStrategyAsset(); } - if (strategies[_initialStrategies[i]].active) revert DuplicateInitialStrategy(); + if ($.strategies[_initialStrategies[i]].active) revert DuplicateInitialStrategy(); - strategies[_initialStrategies[i]] = Strategy({ + $.strategies[_initialStrategies[i]] = Strategy({ allocated: 0, allocationPoints: _initialStrategiesAllocationPoints[i].toUint120(), active: true, @@ -162,8 +217,8 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 cachedTotalAllocationPoints += _initialStrategiesAllocationPoints[i]; } - totalAllocationPoints = cachedTotalAllocationPoints; - IWithdrawalQueue(withdrawalQueue).initWithdrawalQueue(_owner, _initialStrategies); + $.totalAllocationPoints = cachedTotalAllocationPoints; + IWithdrawalQueue($.withdrawalQueue).init(_owner, _initialStrategies); // Setup DEFAULT_ADMIN _grantRole(DEFAULT_ADMIN_ROLE, _owner); @@ -182,29 +237,35 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) { - if (_newFeeRecipient == feeRecipient) revert FeeRecipientAlreadySet(); + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - emit SetFeeRecipient(feeRecipient, _newFeeRecipient); + if (_newFeeRecipient == $.feeRecipient) revert FeeRecipientAlreadySet(); - feeRecipient = _newFeeRecipient; + emit SetFeeRecipient($.feeRecipient, _newFeeRecipient); + + $.feeRecipient = _newFeeRecipient; } /// @notice Set performance fee (1e18 == 100%) /// @notice @param _newFee Fee rate function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + if (_newFee > MAX_PERFORMANCE_FEE) revert MaxPerformanceFeeExceeded(); - if (feeRecipient == address(0)) revert FeeRecipientNotSet(); - if (_newFee == performanceFee) revert PerformanceFeeAlreadySet(); + if ($.feeRecipient == address(0)) revert FeeRecipientNotSet(); + if (_newFee == $.performanceFee) revert PerformanceFeeAlreadySet(); - emit SetPerformanceFee(performanceFee, _newFee); + emit SetPerformanceFee($.performanceFee, _newFee); - performanceFee = _newFee; + $.performanceFee = _newFee; } /// @notice Opt in to strategy rewards /// @param _strategy Strategy address function optInStrategyRewards(address _strategy) external onlyRole(MANAGER) nonReentrant { - if (!strategies[_strategy].active) revert InactiveStrategy(); + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + if (!$.strategies[_strategy].active) revert InactiveStrategy(); IBalanceForwarder(_strategy).enableBalanceForwarder(); @@ -272,18 +333,20 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 nonReentrant onlyRole(REBALANCER) { - Strategy memory strategyData = strategies[_strategy]; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + Strategy memory strategyData = $.strategies[_strategy]; if (_isDeposit) { // Do required approval (safely) and deposit IERC20(asset()).safeIncreaseAllowance(_strategy, _amountToRebalance); IERC4626(_strategy).deposit(_amountToRebalance, address(this)); - strategies[_strategy].allocated = uint120(strategyData.allocated + _amountToRebalance); - totalAllocated += _amountToRebalance; + $.strategies[_strategy].allocated = uint120(strategyData.allocated + _amountToRebalance); + $.totalAllocated += _amountToRebalance; } else { IERC4626(_strategy).withdraw(_amountToRebalance, address(this), address(this)); - strategies[_strategy].allocated = (strategyData.allocated - _amountToRebalance).toUint120(); - totalAllocated -= _amountToRebalance; + $.strategies[_strategy].allocated = (strategyData.allocated - _amountToRebalance).toUint120(); + $.totalAllocated -= _amountToRebalance; } emit Rebalance(_strategy, _amountToRebalance, _isDeposit); @@ -298,14 +361,16 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 nonReentrant onlyRole(STRATEGY_MANAGER) { - Strategy memory strategyDataCache = strategies[_strategy]; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + Strategy memory strategyDataCache = $.strategies[_strategy]; if (!strategyDataCache.active) { revert InactiveStrategy(); } - strategies[_strategy].allocationPoints = _newPoints.toUint120(); - totalAllocationPoints = totalAllocationPoints + _newPoints - strategyDataCache.allocationPoints; + $.strategies[_strategy].allocationPoints = _newPoints.toUint120(); + $.totalAllocationPoints = $.totalAllocationPoints + _newPoints - strategyDataCache.allocationPoints; emit AdjustAllocationPoints(_strategy, strategyDataCache.allocationPoints, _newPoints); } @@ -315,11 +380,13 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @param _strategy Strategy address. /// @param _cap Cap amount function setStrategyCap(address _strategy, uint256 _cap) external nonReentrant onlyRole(STRATEGY_MANAGER) { - if (!strategies[_strategy].active) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + if (!$.strategies[_strategy].active) { revert InactiveStrategy(); } - strategies[_strategy].cap = _cap.toUint120(); + $.strategies[_strategy].cap = _cap.toUint120(); emit SetStrategyCap(_strategy, _cap); } @@ -329,7 +396,9 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @param _strategy Address of the strategy /// @param _allocationPoints Strategy's allocation points function addStrategy(address _strategy, uint256 _allocationPoints) external nonReentrant onlyRole(STRATEGY_ADDER) { - if (strategies[_strategy].active) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + if ($.strategies[_strategy].active) { revert StrategyAlreadyExist(); } @@ -339,11 +408,11 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 _callHooksTarget(ADD_STRATEGY, _msgSender()); - strategies[_strategy] = + $.strategies[_strategy] = Strategy({allocated: 0, allocationPoints: _allocationPoints.toUint120(), active: true, cap: 0}); - totalAllocationPoints += _allocationPoints; - IWithdrawalQueue(withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); + $.totalAllocationPoints += _allocationPoints; + IWithdrawalQueue($.withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); emit AddStrategy(_strategy, _allocationPoints); } @@ -355,7 +424,9 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 function removeStrategy(address _strategy) external nonReentrant onlyRole(STRATEGY_REMOVER) { if (_strategy == address(0)) revert CanNotRemoveCashReserve(); - Strategy storage strategyStorage = strategies[_strategy]; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + Strategy storage strategyStorage = $.strategies[_strategy]; if (!strategyStorage.active) { revert AlreadyRemoved(); @@ -363,19 +434,18 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 _callHooksTarget(REMOVE_STRATEGY, _msgSender()); - totalAllocationPoints -= strategyStorage.allocationPoints; + $.totalAllocationPoints -= strategyStorage.allocationPoints; strategyStorage.active = false; strategyStorage.allocationPoints = 0; // remove from withdrawalQueue - IWithdrawalQueue(withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); + IWithdrawalQueue($.withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); emit RemoveStrategy(_strategy); } /// @notice update accrued interest - /// @return struct ESR struct - function updateInterestAccrued() external returns (ESR memory) { + function updateInterestAccrued() external returns (AggregationVaultSavingRateStorage memory) { return _updateInterestAccrued(); } @@ -388,19 +458,55 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @param _strategy strategy's address /// @return Strategy struct function getStrategy(address _strategy) external view returns (Strategy memory) { - return strategies[_strategy]; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return $.strategies[_strategy]; } /// @notice Return the ESR struct /// @return ESR struct - function getESRSlot() external view returns (ESR memory) { - return esrSlot; + function getAggregationVaultSavingRate() external view returns (AggregationVaultSavingRateStorage memory) { + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + + return avsrCache; } /// @notice Return the accrued interest /// @return uint256 accrued interest function interestAccrued() external view returns (uint256) { - return _interestAccruedFromCache(esrSlot); + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + + return _interestAccruedFromCache(avsrCache); + } + + function totalAllocated() external view returns (uint256) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return $.totalAllocated; + } + + function totalAllocationPoints() external view returns (uint256) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return $.totalAllocationPoints; + } + + function totalAssetsDeposited() external view returns (uint256) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return $.totalAssetsDeposited; + } + + function withdrawalQueue() external view returns (address) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return $.withdrawalQueue; + } + + function performanceFeeConfig() external view returns (address, uint256) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return ($.feeRecipient, $.performanceFee); } /// @dev See {IERC4626-deposit}. @@ -449,31 +555,49 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @notice update accrued interest. /// @return struct ESR struct. - function _updateInterestAccrued() internal returns (ESR memory) { - ESR memory esrSlotCache = esrSlot; - uint256 accruedInterest = _interestAccruedFromCache(esrSlotCache); + function _updateInterestAccrued() internal returns (AggregationVaultSavingRateStorage memory) { + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + + uint256 accruedInterest = _interestAccruedFromCache(avsrCache); + // 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); + avsrCache.interestLeft -= uint168(accruedInterest); + avsrCache.lastInterestUpdate = uint40(block.timestamp); + // write esrSlotCache back to storage in a single SSTORE - esrSlot = esrSlotCache; + _setAggregationVaultSavingRateStorage(avsrCache); + // Move interest accrued to totalAssetsDeposited - totalAssetsDeposited += accruedInterest; + _increaseTotalAssetsDepositedByInterest(accruedInterest); - return esrSlotCache; + return avsrCache; + } + + function _setAggregationVaultSavingRateStorage(AggregationVaultSavingRateStorage memory _avsrCache) private { + AggregationVaultSavingRateStorage storage $ = _getAggregationVaultSavingRateStorage(); + + $.lastInterestUpdate = _avsrCache.lastInterestUpdate; + $.interestSmearEnd = _avsrCache.interestSmearEnd; + $.interestLeft = _avsrCache.interestLeft; + $.locked = _avsrCache.locked; } /// @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 + _interestAccruedFromCache(esrSlot); + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + + return $.totalAssetsDeposited + _interestAccruedFromCache(avsrCache); } /// @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; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + return IERC20(asset()).balanceOf(address(this)) + $.totalAllocated; } /// @dev Increate the total assets deposited, and call IERC4626._deposit() @@ -481,7 +605,9 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override { _callHooksTarget(DEPOSIT, caller); - totalAssetsDeposited += assets; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + $.totalAssetsDeposited += assets; super._deposit(caller, receiver, assets, shares); } @@ -496,11 +622,13 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 { _callHooksTarget(WITHDRAW, caller); - totalAssetsDeposited -= assets; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + $.totalAssetsDeposited -= assets; uint256 assetsRetrieved = IERC20(asset()).balanceOf(address(this)); if (assetsRetrieved < assets) { - IWithdrawalQueue(withdrawalQueue).executeWithdrawFromQueue( + IWithdrawalQueue($.withdrawalQueue).executeWithdrawFromQueue( caller, receiver, owner, assets, shares, assetsRetrieved ); } else { @@ -510,9 +638,11 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 // TODO: add access control function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + // Update allocated assets - strategies[_strategy].allocated -= uint120(_withdrawAmount); - totalAllocated -= _withdrawAmount; + $.strategies[_strategy].allocated -= uint120(_withdrawAmount); + $.totalAllocated -= _withdrawAmount; // Do actual withdraw from strategy IERC4626(_strategy).withdraw(_withdrawAmount, address(this), address(this)); @@ -540,27 +670,31 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @dev gulp positive yield and increment the left interest function _gulp() internal { - ESR memory esrSlotCache = _updateInterestAccrued(); + AggregationVaultSavingRateStorage memory avsrCache = _updateInterestAccrued(); + + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - if (totalAssetsDeposited == 0) return; - uint256 toGulp = totalAssetsAllocatable() - totalAssetsDeposited - esrSlotCache.interestLeft; + if ($.totalAssetsDeposited == 0) return; + uint256 toGulp = totalAssetsAllocatable() - $.totalAssetsDeposited - avsrCache.interestLeft; if (toGulp == 0) return; - uint256 maxGulp = type(uint168).max - esrSlotCache.interestLeft; + uint256 maxGulp = type(uint168).max - avsrCache.interestLeft; if (toGulp > maxGulp) toGulp = maxGulp; // cap interest, allowing the vault to function - esrSlotCache.interestSmearEnd = uint40(block.timestamp + INTEREST_SMEAR); - esrSlotCache.interestLeft += uint168(toGulp); // toGulp <= maxGulp <= max uint168 + avsrCache.interestSmearEnd = uint40(block.timestamp + INTEREST_SMEAR); + avsrCache.interestLeft += uint168(toGulp); // toGulp <= maxGulp <= max uint168 - // write esrSlotCache back to storage in a single SSTORE - esrSlot = esrSlotCache; + // write avsrCache back to storage in a single SSTORE + _setAggregationVaultSavingRateStorage(avsrCache); - emit Gulp(esrSlotCache.interestLeft, esrSlotCache.interestSmearEnd); + emit Gulp(avsrCache.interestLeft, avsrCache.interestSmearEnd); } function _harvest(address _strategy) internal { - uint120 strategyAllocatedAmount = strategies[_strategy].allocated; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + uint120 strategyAllocatedAmount = $.strategies[_strategy].allocated; if (strategyAllocatedAmount == 0) return; @@ -578,35 +712,37 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 yield -= accruedPerformanceFee; } - strategies[_strategy].allocated = uint120(underlyingBalance); - totalAllocated += yield; + $.strategies[_strategy].allocated = uint120(underlyingBalance); + $.totalAllocated += yield; } else { uint256 loss = strategyAllocatedAmount - underlyingBalance; - strategies[_strategy].allocated = uint120(underlyingBalance); - totalAllocated -= loss; + $.strategies[_strategy].allocated = uint120(underlyingBalance); + $.totalAllocated -= loss; - ESR memory esrSlotCache = esrSlot; - if (esrSlotCache.interestLeft >= loss) { - esrSlotCache.interestLeft -= uint168(loss); + AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + if (avsrCache.interestLeft >= loss) { + avsrCache.interestLeft -= uint168(loss); } else { - totalAssetsDeposited -= loss - esrSlotCache.interestLeft; - esrSlotCache.interestLeft = 0; + $.totalAssetsDeposited -= loss - avsrCache.interestLeft; + avsrCache.interestLeft = 0; } - esrSlot = esrSlotCache; + _setAggregationVaultSavingRateStorage(avsrCache); } emit Harvest(_strategy, underlyingBalance, strategyAllocatedAmount); } function _accruePerformanceFee(address _strategy, uint256 _yield) internal returns (uint120) { - address cachedFeeRecipient = feeRecipient; - uint256 cachedPerformanceFee = performanceFee; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + address cachedFeeRecipient = $.feeRecipient; + uint256 cachedPerformanceFee = $.performanceFee; if (cachedFeeRecipient == address(0) || cachedPerformanceFee == 0) return 0; // `feeAssets` will be rounded down to 0 if `yield * performanceFee < 1e18`. - uint256 feeAssets = Math.mulDiv(_yield, cachedPerformanceFee, 1e18, Math.Rounding.Down); + uint256 feeAssets = Math.mulDiv(_yield, cachedPerformanceFee, 1e18, Math.Rounding.Floor); if (feeAssets > 0) { IERC4626(_strategy).withdraw(feeAssets, cachedFeeRecipient, address(this)); @@ -621,14 +757,18 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @dev Calling .balanceTrackerHook() passing the address total balance /// @param from Address sending the amount /// @param to Address receiving the amount - function _afterTokenTransfer(address from, address to, uint256 /*amount*/ ) internal override { + function _update(address from, address to, uint256 value) internal override { + super._update(from, to, value); + if (from == to) return; - if ((from != address(0)) && (isBalanceForwarderEnabled[from])) { + IBalanceTracker balanceTracker = _balanceTracker(); + + if ((from != address(0)) && (_balanceForwarderEnabled(from))) { balanceTracker.balanceTrackerHook(from, super.balanceOf(from), false); } - if ((to != address(0)) && (isBalanceForwarderEnabled[to])) { + if ((to != address(0)) && (_balanceForwarderEnabled(to))) { balanceTracker.balanceTrackerHook(to, super.balanceOf(to), false); } } @@ -636,7 +776,11 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @dev Get accrued interest without updating it. /// @param esrSlotCache Cached esrSlot /// @return uint256 accrued interest - function _interestAccruedFromCache(ESR memory esrSlotCache) internal view returns (uint256) { + function _interestAccruedFromCache(AggregationVaultSavingRateStorage memory esrSlotCache) + internal + view + returns (uint256) + { // If distribution ended, full amount is accrued if (block.timestamp >= esrSlotCache.interestSmearEnd) { return esrSlotCache.interestLeft; @@ -658,7 +802,20 @@ contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC46 /// @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. /// @return The address of the message sender. - function _msgSender() internal view override (Context, EVCUtil) returns (address) { - return EVCUtil._msgSender(); + function _msgSender() internal view override (ContextUpgradeable) returns (address) { + address sender = msg.sender; + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + if (sender == address($.evc)) { + (sender,) = $.evc.getCurrentOnBehalfOfAccount(address(0)); + } + + return sender; + } + + function _increaseTotalAssetsDepositedByInterest(uint256 _accruedInterest) private { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + + $.totalAssetsDeposited += _accruedInterest; } } diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index 5b169b7e..ea27f454 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -25,22 +25,22 @@ contract FourSixTwoSixAggFactory { uint256[] memory _initialStrategiesAllocationPoints ) external returns (address) { address withdrawalQueueAddr = address(new WithdrawalQueue()); - address fourSixTwoSixAggAddr = address( - new FourSixTwoSixAgg( - evc, - balanceTracker, - withdrawalQueueAddr, - rebalancer, - msg.sender, - _asset, - _name, - _symbol, - _initialCashAllocationPoints, - _initialStrategies, - _initialStrategiesAllocationPoints - ) + FourSixTwoSixAgg fourSixTwoSixAggAddr = new FourSixTwoSixAgg(); + + fourSixTwoSixAggAddr.init( + evc, + balanceTracker, + withdrawalQueueAddr, + rebalancer, + msg.sender, + _asset, + _name, + _symbol, + _initialCashAllocationPoints, + _initialStrategies, + _initialStrategiesAllocationPoints ); - return fourSixTwoSixAggAddr; + return address(fourSixTwoSixAggAddr); } } diff --git a/src/Hooks.sol b/src/Hooks.sol index 8127a6fc..033916cf 100644 --- a/src/Hooks.sol +++ b/src/Hooks.sol @@ -16,12 +16,23 @@ abstract contract Hooks { uint32 public constant WITHDRAW = 1 << 1; uint32 public constant ADD_STRATEGY = 1 << 2; uint32 public constant REMOVE_STRATEGY = 1 << 3; - uint32 constant ACTIONS_COUNTER = 1 << 4; - uint256 public constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; - /// @dev storing the hooks target and kooked functions. - uint256 hooksConfig; + uint256 constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; + + struct HooksStorage { + /// @dev storing the hooks target and kooked functions. + uint256 hooksConfig; + } + + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.Hooks")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant HooksStorageLocation = 0x7daefa3ee1567d8892b825e51ce683a058a5785193bcc2ca50940db02ccbf700; + + function _getHooksStorage() private pure returns (HooksStorage storage $) { + assembly { + $.slot := HooksStorageLocation + } + } event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); @@ -29,7 +40,9 @@ abstract contract Hooks { /// @return address Hooks contract. /// @return uint32 Hooked functions. function getHooksConfig() external view returns (address, uint32) { - return _getHooksConfig(hooksConfig); + HooksStorage storage $ = _getHooksStorage(); + + return _getHooksConfig($.hooksConfig); } /// @notice Set hooks contract and hooked functions. @@ -52,7 +65,8 @@ abstract contract Hooks { } if (_hookedFns >= ACTIONS_COUNTER) revert InvalidHookedFns(); - hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); + HooksStorage storage $ = _getHooksStorage(); + $.hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); emit SetHooksConfig(_hooksTarget, _hookedFns); } @@ -61,7 +75,9 @@ abstract contract Hooks { /// @param _fn Function to call the hook for. /// @param _caller Caller's address. function _callHooksTarget(uint32 _fn, address _caller) internal { - (address target, uint32 hookedFns) = _getHooksConfig(hooksConfig); + HooksStorage storage $ = _getHooksStorage(); + + (address target, uint32 hookedFns) = _getHooksConfig($.hooksConfig); if (hookedFns.isNotSet(_fn)) return; diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index d2f5ac7e..caf7dae1 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -2,7 +2,8 @@ pragma solidity ^0.8.0; // external dep -import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from + "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; // internal dep import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; @@ -15,49 +16,66 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { bytes32 public constant WITHDRAW_QUEUE_MANAGER = keccak256("WITHDRAW_QUEUE_MANAGER"); bytes32 public constant WITHDRAW_QUEUE_MANAGER_ADMIN = keccak256("WITHDRAW_QUEUE_MANAGER_ADMIN"); - address public eulerAggregationVault; + struct WithdrawalQueueStorage { + address eulerAggregationVault; + /// @dev An array of strategy addresses to withdraw from + address[] withdrawalQueue; + } - /// @dev An array of strategy addresses to withdraw from - address[] public withdrawalQueue; + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.WithdrawalQueue")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant WithdrawalQueueStorageLocation = + 0x8522ce6e5838588854909d348b0c9f7932eae519636e8e48e91e9b2639174600; - bool private isInitialized_; + function _getWithdrawalQueueStorage() private pure returns (WithdrawalQueueStorage storage $) { + assembly { + $.slot := WithdrawalQueueStorageLocation + } + } event ReorderWithdrawalQueue(uint8 index1, uint8 index2); - function initWithdrawalQueue(address _owner, address[] calldata _initialStrategies) external { - if (isInitialized_) revert(); + function init(address _owner, address[] calldata _initialStrategies) external initializer { + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); - isInitialized_ = true; + $.eulerAggregationVault = msg.sender; for (uint256 i; i < _initialStrategies.length; ++i) { - withdrawalQueue.push(_initialStrategies[i]); + $.withdrawalQueue.push(_initialStrategies[i]); } - eulerAggregationVault = msg.sender; - // Setup DEFAULT_ADMIN _grantRole(DEFAULT_ADMIN_ROLE, _owner); _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); } + function getWithdrawalQueueAtIndex(uint256 _index) external view returns (address) { + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); + + return $.withdrawalQueue[_index]; + } + // TODO: add access control function addStrategyToWithdrawalQueue(address _strategy) external { - withdrawalQueue.push(_strategy); + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); + + $.withdrawalQueue.push(_strategy); } // TODO: add access control function removeStrategyFromWithdrawalQueue(address _strategy) external { - uint256 lastStrategyIndex = withdrawalQueue.length - 1; + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); + + uint256 lastStrategyIndex = $.withdrawalQueue.length - 1; for (uint256 i = 0; i < lastStrategyIndex; ++i) { - if (withdrawalQueue[i] == _strategy) { - withdrawalQueue[i] = withdrawalQueue[lastStrategyIndex]; + if ($.withdrawalQueue[i] == _strategy) { + $.withdrawalQueue[i] = $.withdrawalQueue[lastStrategyIndex]; break; } } - withdrawalQueue.pop(); + $.withdrawalQueue.pop(); } // TODO: add access control @@ -69,17 +87,20 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { uint256 shares, uint256 availableAssets ) external { - uint256 numStrategies = withdrawalQueue.length; + WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); + address eulerAggregationVaultCached = $.eulerAggregationVault; + + uint256 numStrategies = $.withdrawalQueue.length; for (uint256 i; i < numStrategies; ++i) { - IERC4626 strategy = IERC4626(withdrawalQueue[i]); + IERC4626 strategy = IERC4626($.withdrawalQueue[i]); - IFourSixTwoSixAgg(eulerAggregationVault).harvest(address(strategy)); + IFourSixTwoSixAgg(eulerAggregationVaultCached).harvest(address(strategy)); - uint256 underlyingBalance = strategy.maxWithdraw(eulerAggregationVault); + uint256 underlyingBalance = strategy.maxWithdraw(eulerAggregationVaultCached); uint256 desiredAssets = assets - availableAssets; uint256 withdrawAmount = (underlyingBalance > desiredAssets) ? desiredAssets : underlyingBalance; - IFourSixTwoSixAgg(eulerAggregationVault).withdrawFromStrategy(address(strategy), withdrawAmount); + IFourSixTwoSixAgg(eulerAggregationVaultCached).withdrawFromStrategy(address(strategy), withdrawAmount); // update assetsRetrieved availableAssets += withdrawAmount; @@ -93,7 +114,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { revert NotEnoughAssets(); } - IFourSixTwoSixAgg(eulerAggregationVault).executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + IFourSixTwoSixAgg(eulerAggregationVaultCached).executeWithdrawFromReserve( + caller, receiver, owner, assets, shares + ); } /// @notice Swap two strategies indexes in the withdrawal queue. @@ -101,7 +124,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { /// @param _index1 index of first strategy /// @param _index2 index of second strategy function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { - uint256 length = withdrawalQueue.length; + WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); + + uint256 length = $.withdrawalQueue.length; if (_index1 >= length || _index2 >= length) { revert OutOfBounds(); } @@ -110,7 +135,8 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { revert SameIndexes(); } - (withdrawalQueue[_index1], withdrawalQueue[_index2]) = (withdrawalQueue[_index2], withdrawalQueue[_index1]); + ($.withdrawalQueue[_index1], $.withdrawalQueue[_index2]) = + ($.withdrawalQueue[_index2], $.withdrawalQueue[_index1]); emit ReorderWithdrawalQueue(_index1, _index2); } @@ -118,6 +144,8 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { /// @notice Return the withdrawal queue length. /// @return uint256 length function withdrawalQueueLength() external view returns (uint256) { - return withdrawalQueue.length; + WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); + + return $.withdrawalQueue.length; } } diff --git a/src/interface/IWithdrawalQueue.sol b/src/interface/IWithdrawalQueue.sol index d51c0550..fca03729 100644 --- a/src/interface/IWithdrawalQueue.sol +++ b/src/interface/IWithdrawalQueue.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; interface IWithdrawalQueue { - function initWithdrawalQueue(address _owner, address[] calldata _initialStrategies) external; + function init(address _owner, address[] calldata _initialStrategies) external; function addStrategyToWithdrawalQueue(address _strategy) external; function removeStrategyFromWithdrawalQueue(address _strategy) external; function executeWithdrawFromQueue( diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index a6160abb..0ecbd396 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -118,7 +118,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address[] memory queue = new address[](length); for (uint256 i = 0; i < length; ++i) { - queue[i] = withdrawalQueue.withdrawalQueue(i); + queue[i] = withdrawalQueue.getWithdrawalQueueAtIndex(i); } return queue; } diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 3f5ce3b1..1c619b88 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -24,16 +24,6 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { vm.startPrank(deployer); trackingReward = address(new TrackingRewardStreams(address(evc), 2 weeks)); - // fourSixTwoSixAgg = new FourSixTwoSixAgg( - // address(evc), - // trackingReward, - // address(assetTST), - // "assetTST_Agg", - // "assetTST_Agg", - // CASH_RESERVE_ALLOCATION_POINTS, - // new address[](0), - // new uint256[](0) - // ); fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), trackingReward, address(rebalancer)); fourSixTwoSixAgg = FourSixTwoSixAgg( fourSixTwoSixAggFactory.deployEulerAggregationLayer( @@ -86,7 +76,7 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { } function testBalanceForwarderrAddress_Integrity() public view { - assertEq(address(fourSixTwoSixAgg.balanceTracker()), trackingReward); + assertEq(fourSixTwoSixAgg.balanceTrackerAddress(), trackingReward); } function testEnableBalanceForwarder() public { diff --git a/test/e2e/PerformanceFeeE2ETest.t.sol b/test/e2e/PerformanceFeeE2ETest.t.sol index f72e6eb8..0ecda9a0 100644 --- a/test/e2e/PerformanceFeeE2ETest.t.sol +++ b/test/e2e/PerformanceFeeE2ETest.t.sol @@ -28,7 +28,10 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { } function testSetPerformanceFee() public { - assertEq(fourSixTwoSixAgg.performanceFee(), 0); + { + (, uint256 fee) = fourSixTwoSixAgg.performanceFeeConfig(); + assertEq(fee, 0); + } uint256 newPerformanceFee = 3e17; @@ -37,8 +40,9 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { fourSixTwoSixAgg.setPerformanceFee(newPerformanceFee); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.performanceFee(), newPerformanceFee); - assertEq(fourSixTwoSixAgg.feeRecipient(), feeRecipient); + (address feeRecipientAddr, uint256 fee) = fourSixTwoSixAgg.performanceFeeConfig(); + assertEq(fee, newPerformanceFee); + assertEq(feeRecipientAddr, feeRecipient); } function testHarvestWithFeeEnabled() public { @@ -101,7 +105,8 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); } - uint256 expectedPerformanceFee = yield * fourSixTwoSixAgg.performanceFee() / 1e18; + (, uint256 performanceFee) = fourSixTwoSixAgg.performanceFeeConfig(); + uint256 expectedPerformanceFee = yield * performanceFee / 1e18; FourSixTwoSixAgg.Strategy memory strategyBeforeHarvest = fourSixTwoSixAgg.getStrategy(address(eTST)); uint256 totalAllocatedBefore = fourSixTwoSixAgg.totalAllocated(); diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index 41c20510..bcf2c201 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -56,7 +56,7 @@ contract GulpTest is FourSixTwoSixAggBase { function testGulpAfterNegativeYieldEqualToInterestLeft() public { fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.ESR memory ers = fourSixTwoSixAgg.getESRSlot(); + FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); assertEq(fourSixTwoSixAgg.interestAccrued(), 0); assertEq(ers.interestLeft, 0); @@ -84,13 +84,13 @@ contract GulpTest is FourSixTwoSixAggBase { // interest per day 23.809523809523 assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getESRSlot(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); assertEq(ers.interestLeft, yield - 23809523809523809523); // move close to end of smearing vm.warp(block.timestamp + 11 days); fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getESRSlot(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); // mock a decrease of strategy balance by ers.interestLeft uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); @@ -106,7 +106,7 @@ contract GulpTest is FourSixTwoSixAggBase { function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.ESR memory ers = fourSixTwoSixAgg.getESRSlot(); + FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); assertEq(fourSixTwoSixAgg.interestAccrued(), 0); assertEq(ers.interestLeft, 0); @@ -134,13 +134,13 @@ contract GulpTest is FourSixTwoSixAggBase { // interest per day 23.809523809523 assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getESRSlot(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); assertEq(ers.interestLeft, yield - 23809523809523809523); // move close to end of smearing vm.warp(block.timestamp + 11 days); fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getESRSlot(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); // mock a decrease of strategy balance by ers.interestLeft uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); From 2ed5a5f2b4552de978d8fc80f0ecc527501c73aa Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:52:59 +0300 Subject: [PATCH 07/19] working version --- src/BalanceForwarder.sol | 43 ++-- src/FourSixTwoSixAgg.sol | 208 +++++------------ src/FourSixTwoSixAggFactory.sol | 23 +- src/Hooks.sol | 12 +- src/WithdrawalQueue.sol | 23 +- src/interface/IWithdrawalQueue.sol | 2 +- test/common/FourSixTwoSixAggBase.t.sol | 7 +- test/e2e/BalanceForwarderE2ETest.t.sol | 7 +- test/unit/GulpTest.t.sol | 312 ++++++++++++------------- 9 files changed, 270 insertions(+), 367 deletions(-) diff --git a/src/BalanceForwarder.sol b/src/BalanceForwarder.sol index 8e082fba..6418d7d4 100644 --- a/src/BalanceForwarder.sol +++ b/src/BalanceForwarder.sol @@ -18,24 +18,15 @@ abstract contract BalanceForwarder is IBalanceForwarder { IBalanceTracker balanceTracker; mapping(address => bool) isBalanceForwarderEnabled; } - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant BalanceForwarderStorageLocation = 0xf0af7cc3986d6cac468ac54c07f5f08359b3a00d65f8e2a86f2676ec79073f00; - function _getBalanceForwarderStorage() private pure returns (BalanceForwarderStorage storage $) { - assembly { - $.slot := BalanceForwarderStorageLocation - } - } event EnableBalanceForwarder(address indexed _user); event DisableBalanceForwarder(address indexed _user); - // constructor(address _balanceTracker) { - // balanceTracker = IBalanceTracker(_balanceTracker); - // } - /// @notice Enables balance forwarding for the authenticated account /// @dev Only the authenticated account can enable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the current account's balance @@ -63,12 +54,13 @@ abstract contract BalanceForwarder is IBalanceForwarder { function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + IBalanceTracker balanceTrackerCached = $.balanceTracker; - if (address($.balanceTracker) == address(0)) revert NotSupported(); + if (address(balanceTrackerCached) == address(0)) revert NotSupported(); if ($.isBalanceForwarderEnabled[_sender]) revert AlreadyEnabled(); $.isBalanceForwarderEnabled[_sender] = true; - $.balanceTracker.balanceTrackerHook(_sender, _senderBalance, false); + balanceTrackerCached.balanceTrackerHook(_sender, _senderBalance, false); emit EnableBalanceForwarder(_sender); } @@ -78,16 +70,23 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @dev Should call the IBalanceTracker hook with the account's balance of 0 function _disableBalanceForwarder(address _sender) internal { BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); - - if (address($.balanceTracker) == address(0)) revert NotSupported(); + IBalanceTracker balanceTrackerCached = $.balanceTracker; + + if (address(balanceTrackerCached) == address(0)) revert NotSupported(); if (!$.isBalanceForwarderEnabled[_sender]) revert AlreadyDisabled(); $.isBalanceForwarderEnabled[_sender] = false; - $.balanceTracker.balanceTrackerHook(_sender, 0, false); + balanceTrackerCached.balanceTrackerHook(_sender, 0, false); emit DisableBalanceForwarder(_sender); } + function _setBalanceTracker(address _balancerTracker) internal { + BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + + $.balanceTracker = IBalanceTracker(_balancerTracker); + } + /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract /// @param _account Address to query /// @return True if balance forwarder is enabled @@ -97,11 +96,17 @@ abstract contract BalanceForwarder is IBalanceForwarder { return $.isBalanceForwarderEnabled[_account]; } - /// @notice Retrieve the instance of rewards contract, tracking changes in account's balances - /// @return The balance tracker contract - function _balanceTracker() internal view returns (IBalanceTracker) { + /// @notice Retrieve the address of rewards contract, tracking changes in account's balances + /// @return The balance tracker address + function _balanceTrackerAddress() internal view returns (address) { BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); - return $.balanceTracker; + return address($.balanceTracker); + } + + function _getBalanceForwarderStorage() private pure returns (BalanceForwarderStorage storage $) { + assembly { + $.slot := BalanceForwarderStorageLocation + } } } diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index f8f30447..b2ce132c 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -87,44 +87,21 @@ contract FourSixTwoSixAgg is address withdrawalQueue; /// @dev Mapping between strategy address and it's allocation config mapping(address => Strategy) strategies; - } - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant AggregationVaultStorageLocation = - 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; - function _getAggregationVaultStorage() private pure returns (AggregationVaultStorage storage $) { - assembly { - $.slot := AggregationVaultStorageLocation - } - } - - /// @dev Euler saving rate struct - /// lastInterestUpdate: last timestamo where interest was updated. - /// interestSmearEnd: timestamp when the smearing of interest end. - /// interestLeft: amount of interest left to smear. - /// locked: if locked or not for update. - /// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVaultSavingRate - struct AggregationVaultSavingRateStorage { + /// lastInterestUpdate: last timestamo where interest was updated. uint40 lastInterestUpdate; + /// interestSmearEnd: timestamp when the smearing of interest end. uint40 interestSmearEnd; + /// interestLeft: amount of interest left to smear. uint168 interestLeft; + /// locked: if locked or not for update. uint8 locked; } - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVaultSavingRate")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant AggregationVaultSavingRateStorageLocation = - 0xdb2394914f0f6fb18662eb905f211539008437f354f711cd6b8106daf3f40500; - - function _getAggregationVaultSavingRateStorage() - private - pure - returns (AggregationVaultSavingRateStorage storage $) - { - assembly { - $.slot := AggregationVaultSavingRateStorageLocation - } - } + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant AggregationVaultStorageLocation = + 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); event SetPerformanceFee(uint256 oldFee, uint256 newFee); @@ -141,16 +118,15 @@ contract FourSixTwoSixAgg is /// @dev Non reentrancy modifier for interest rate updates modifier nonReentrant() { - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - if (avsrCache.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); + if ($.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); - avsrCache.locked = REENTRANCYLOCK__LOCKED; + $.locked = REENTRANCYLOCK__LOCKED; _; - avsrCache.locked = REENTRANCYLOCK__UNLOCKED; + $.locked = REENTRANCYLOCK__UNLOCKED; } - // /// @dev Constructor // /// @param _evc EVC address // /// @param _asset Aggregator's asset address // /// @param _name Aggregator's name @@ -158,19 +134,6 @@ contract FourSixTwoSixAgg is // /// @param _initialCashAllocationPoints Initial points to be allocated to the cash reserve // /// @param _initialStrategies An array of initial strategies addresses // /// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points - - function _lock() private onlyInitializing { - AggregationVaultSavingRateStorage storage $ = _getAggregationVaultSavingRateStorage(); - - $.locked = REENTRANCYLOCK__UNLOCKED; - } - - function _setWithdrawalQueue(address _withdrawalQueue) private { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - - $.withdrawalQueue = _withdrawalQueue; - } - function init( address _evc, address _balanceTracker, @@ -180,45 +143,23 @@ contract FourSixTwoSixAgg is address _asset, string memory _name, string memory _symbol, - uint256 _initialCashAllocationPoints, - address[] memory _initialStrategies, - uint256[] memory _initialStrategiesAllocationPoints + uint256 _initialCashAllocationPoints ) external initializer { __ERC4626_init_unchained(IERC20(_asset)); __ERC20_init_unchained(_name, _symbol); _lock(); - if (_initialStrategies.length != _initialStrategiesAllocationPoints.length) revert ArrayLengthMismatch(); if (_initialCashAllocationPoints == 0) revert InitialAllocationPointsZero(); AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - $.withdrawalQueue = _withdrawalQueue; - $.strategies[address(0)] = Strategy({allocated: 0, allocationPoints: _initialCashAllocationPoints.toUint120(), active: true, cap: 0}); + $.totalAllocationPoints = _initialCashAllocationPoints; + $.evc = IEVC(_evc); - uint256 cachedTotalAllocationPoints = _initialCashAllocationPoints; - - for (uint256 i; i < _initialStrategies.length; ++i) { - if (IERC4626(_initialStrategies[i]).asset() != asset()) { - revert InvalidStrategyAsset(); - } - - if ($.strategies[_initialStrategies[i]].active) revert DuplicateInitialStrategy(); - - $.strategies[_initialStrategies[i]] = Strategy({ - allocated: 0, - allocationPoints: _initialStrategiesAllocationPoints[i].toUint120(), - active: true, - cap: 0 - }); - - cachedTotalAllocationPoints += _initialStrategiesAllocationPoints[i]; - } - $.totalAllocationPoints = cachedTotalAllocationPoints; - IWithdrawalQueue($.withdrawalQueue).init(_owner, _initialStrategies); + _setBalanceTracker(_balanceTracker); // Setup DEFAULT_ADMIN _grantRole(DEFAULT_ADMIN_ROLE, _owner); @@ -227,7 +168,6 @@ contract FourSixTwoSixAgg is // Setup role admins _setRoleAdmin(STRATEGY_MANAGER, STRATEGY_MANAGER_ADMIN); - // _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); _setRoleAdmin(STRATEGY_ADDER, STRATEGY_ADDER_ADMIN); _setRoleAdmin(STRATEGY_REMOVER, STRATEGY_REMOVER_ADMIN); _setRoleAdmin(MANAGER, MANAGER_ADMIN); @@ -238,10 +178,11 @@ contract FourSixTwoSixAgg is /// @notice @param _newFeeRecipient Recipient address function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) { AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + address feeRecipientCached = $.feeRecipient; - if (_newFeeRecipient == $.feeRecipient) revert FeeRecipientAlreadySet(); + if (_newFeeRecipient == feeRecipientCached) revert FeeRecipientAlreadySet(); - emit SetFeeRecipient($.feeRecipient, _newFeeRecipient); + emit SetFeeRecipient(feeRecipientCached, _newFeeRecipient); $.feeRecipient = _newFeeRecipient; } @@ -251,11 +192,13 @@ contract FourSixTwoSixAgg is function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) { AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + uint256 performanceFeeCached = $.performanceFee; + if (_newFee > MAX_PERFORMANCE_FEE) revert MaxPerformanceFeeExceeded(); if ($.feeRecipient == address(0)) revert FeeRecipientNotSet(); - if (_newFee == $.performanceFee) revert PerformanceFeeAlreadySet(); + if (_newFee == performanceFeeCached) revert PerformanceFeeAlreadySet(); - emit SetPerformanceFee($.performanceFee, _newFee); + emit SetPerformanceFee(performanceFeeCached, _newFee); $.performanceFee = _newFee; } @@ -445,9 +388,9 @@ contract FourSixTwoSixAgg is } /// @notice update accrued interest - function updateInterestAccrued() external returns (AggregationVaultSavingRateStorage memory) { - return _updateInterestAccrued(); - } + // function updateInterestAccrued() external returns (AggregationVaultSavingRateStorage memory) { + // return _updateInterestAccrued(); + // } /// @notice gulp positive harvest yield function gulp() external nonReentrant { @@ -463,20 +406,10 @@ contract FourSixTwoSixAgg is return $.strategies[_strategy]; } - /// @notice Return the ESR struct - /// @return ESR struct - function getAggregationVaultSavingRate() external view returns (AggregationVaultSavingRateStorage memory) { - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - - return avsrCache; - } - /// @notice Return the accrued interest /// @return uint256 accrued interest function interestAccrued() external view returns (uint256) { - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - - return _interestAccruedFromCache(avsrCache); + return _interestAccruedFromCache(); } function totalAllocated() external view returns (uint256) { @@ -554,41 +487,24 @@ contract FourSixTwoSixAgg is } /// @notice update accrued interest. - /// @return struct ESR struct. - function _updateInterestAccrued() internal returns (AggregationVaultSavingRateStorage memory) { - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - - uint256 accruedInterest = _interestAccruedFromCache(avsrCache); + function _updateInterestAccrued() internal { + uint256 accruedInterest = _interestAccruedFromCache(); + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); // it's safe to down-cast because the accrued interest is a fraction of interest left - avsrCache.interestLeft -= uint168(accruedInterest); - avsrCache.lastInterestUpdate = uint40(block.timestamp); - - // write esrSlotCache back to storage in a single SSTORE - _setAggregationVaultSavingRateStorage(avsrCache); + $.interestLeft -= uint168(accruedInterest); + $.lastInterestUpdate = uint40(block.timestamp); // Move interest accrued to totalAssetsDeposited - _increaseTotalAssetsDepositedByInterest(accruedInterest); - - return avsrCache; - } - - function _setAggregationVaultSavingRateStorage(AggregationVaultSavingRateStorage memory _avsrCache) private { - AggregationVaultSavingRateStorage storage $ = _getAggregationVaultSavingRateStorage(); - - $.lastInterestUpdate = _avsrCache.lastInterestUpdate; - $.interestSmearEnd = _avsrCache.interestSmearEnd; - $.interestLeft = _avsrCache.interestLeft; - $.locked = _avsrCache.locked; + $.totalAssetsDeposited += accruedInterest; } /// @notice Return the total amount of assets deposited, plus the accrued interest. /// @return uint256 total amount function totalAssets() public view override returns (uint256) { AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - return $.totalAssetsDeposited + _interestAccruedFromCache(avsrCache); + return $.totalAssetsDeposited + _interestAccruedFromCache(); } /// @notice get the total assets allocatable @@ -670,25 +586,22 @@ contract FourSixTwoSixAgg is /// @dev gulp positive yield and increment the left interest function _gulp() internal { - AggregationVaultSavingRateStorage memory avsrCache = _updateInterestAccrued(); + _updateInterestAccrued(); AggregationVaultStorage storage $ = _getAggregationVaultStorage(); if ($.totalAssetsDeposited == 0) return; - uint256 toGulp = totalAssetsAllocatable() - $.totalAssetsDeposited - avsrCache.interestLeft; + uint256 toGulp = totalAssetsAllocatable() - $.totalAssetsDeposited - $.interestLeft; if (toGulp == 0) return; - uint256 maxGulp = type(uint168).max - avsrCache.interestLeft; + uint256 maxGulp = type(uint168).max - $.interestLeft; if (toGulp > maxGulp) toGulp = maxGulp; // cap interest, allowing the vault to function - avsrCache.interestSmearEnd = uint40(block.timestamp + INTEREST_SMEAR); - avsrCache.interestLeft += uint168(toGulp); // toGulp <= maxGulp <= max uint168 - - // write avsrCache back to storage in a single SSTORE - _setAggregationVaultSavingRateStorage(avsrCache); + $.interestSmearEnd = uint40(block.timestamp + INTEREST_SMEAR); + $.interestLeft += uint168(toGulp); // toGulp <= maxGulp <= max uint168 - emit Gulp(avsrCache.interestLeft, avsrCache.interestSmearEnd); + emit Gulp($.interestLeft, $.interestSmearEnd); } function _harvest(address _strategy) internal { @@ -720,14 +633,12 @@ contract FourSixTwoSixAgg is $.strategies[_strategy].allocated = uint120(underlyingBalance); $.totalAllocated -= loss; - AggregationVaultSavingRateStorage memory avsrCache = _getAggregationVaultSavingRateStorage(); - if (avsrCache.interestLeft >= loss) { - avsrCache.interestLeft -= uint168(loss); + if ($.interestLeft >= loss) { + $.interestLeft -= uint168(loss); } else { - $.totalAssetsDeposited -= loss - avsrCache.interestLeft; - avsrCache.interestLeft = 0; + $.totalAssetsDeposited -= loss - $.interestLeft; + $.interestLeft = 0; } - _setAggregationVaultSavingRateStorage(avsrCache); } emit Harvest(_strategy, underlyingBalance, strategyAllocatedAmount); @@ -762,7 +673,7 @@ contract FourSixTwoSixAgg is if (from == to) return; - IBalanceTracker balanceTracker = _balanceTracker(); + IBalanceTracker balanceTracker = IBalanceTracker(_balanceTrackerAddress()); if ((from != address(0)) && (_balanceForwarderEnabled(from))) { balanceTracker.balanceTrackerHook(from, super.balanceOf(from), false); @@ -774,28 +685,25 @@ contract FourSixTwoSixAgg is } /// @dev Get accrued interest without updating it. - /// @param esrSlotCache Cached esrSlot /// @return uint256 accrued interest - function _interestAccruedFromCache(AggregationVaultSavingRateStorage memory esrSlotCache) - internal - view - returns (uint256) - { + function _interestAccruedFromCache() internal view returns (uint256) { + AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + // If distribution ended, full amount is accrued - if (block.timestamp >= esrSlotCache.interestSmearEnd) { - return esrSlotCache.interestLeft; + if (block.timestamp >= $.interestSmearEnd) { + return $.interestLeft; } // If just updated return 0 - if (esrSlotCache.lastInterestUpdate == block.timestamp) { + if ($.lastInterestUpdate == block.timestamp) { return 0; } // Else return what has accrued - uint256 totalDuration = esrSlotCache.interestSmearEnd - esrSlotCache.lastInterestUpdate; - uint256 timePassed = block.timestamp - esrSlotCache.lastInterestUpdate; + uint256 totalDuration = $.interestSmearEnd - $.lastInterestUpdate; + uint256 timePassed = block.timestamp - $.lastInterestUpdate; - return esrSlotCache.interestLeft * timePassed / totalDuration; + return $.interestLeft * timePassed / totalDuration; } /// @notice Retrieves the message sender in the context of the EVC. @@ -813,9 +721,15 @@ contract FourSixTwoSixAgg is return sender; } - function _increaseTotalAssetsDepositedByInterest(uint256 _accruedInterest) private { + function _lock() private onlyInitializing { AggregationVaultStorage storage $ = _getAggregationVaultStorage(); - $.totalAssetsDeposited += _accruedInterest; + $.locked = REENTRANCYLOCK__UNLOCKED; + } + + function _getAggregationVaultStorage() private pure returns (AggregationVaultStorage storage $) { + assembly { + $.slot := AggregationVaultStorageLocation + } } } diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index ea27f454..e06a99c3 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -20,27 +20,26 @@ contract FourSixTwoSixAggFactory { address _asset, string memory _name, string memory _symbol, - uint256 _initialCashAllocationPoints, - address[] memory _initialStrategies, - uint256[] memory _initialStrategiesAllocationPoints + uint256 _initialCashAllocationPoints ) external returns (address) { - address withdrawalQueueAddr = address(new WithdrawalQueue()); - FourSixTwoSixAgg fourSixTwoSixAggAddr = new FourSixTwoSixAgg(); + WithdrawalQueue withdrawalQueue = new WithdrawalQueue(); + FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(); - fourSixTwoSixAggAddr.init( + address owner = msg.sender; + + withdrawalQueue.init(owner, address(fourSixTwoSixAgg)); + fourSixTwoSixAgg.init( evc, balanceTracker, - withdrawalQueueAddr, + address(withdrawalQueue), rebalancer, - msg.sender, + owner, _asset, _name, _symbol, - _initialCashAllocationPoints, - _initialStrategies, - _initialStrategiesAllocationPoints + _initialCashAllocationPoints ); - return address(fourSixTwoSixAggAddr); + return address(fourSixTwoSixAgg); } } diff --git a/src/Hooks.sol b/src/Hooks.sol index 033916cf..c73f3b28 100644 --- a/src/Hooks.sol +++ b/src/Hooks.sol @@ -28,12 +28,6 @@ abstract contract Hooks { // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.Hooks")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant HooksStorageLocation = 0x7daefa3ee1567d8892b825e51ce683a058a5785193bcc2ca50940db02ccbf700; - function _getHooksStorage() private pure returns (HooksStorage storage $) { - assembly { - $.slot := HooksStorageLocation - } - } - event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); /// @notice Get the hooks contract and the hooked functions. @@ -104,4 +98,10 @@ abstract contract Hooks { revert EmptyError(); } + + function _getHooksStorage() private pure returns (HooksStorage storage $) { + assembly { + $.slot := HooksStorageLocation + } + } } diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index caf7dae1..56b7f873 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -26,22 +26,11 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { bytes32 private constant WithdrawalQueueStorageLocation = 0x8522ce6e5838588854909d348b0c9f7932eae519636e8e48e91e9b2639174600; - function _getWithdrawalQueueStorage() private pure returns (WithdrawalQueueStorage storage $) { - assembly { - $.slot := WithdrawalQueueStorageLocation - } - } - event ReorderWithdrawalQueue(uint8 index1, uint8 index2); - function init(address _owner, address[] calldata _initialStrategies) external initializer { + function init(address _owner, address _eulerAggregationVault) external initializer { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); - - $.eulerAggregationVault = msg.sender; - - for (uint256 i; i < _initialStrategies.length; ++i) { - $.withdrawalQueue.push(_initialStrategies[i]); - } + $.eulerAggregationVault = _eulerAggregationVault; // Setup DEFAULT_ADMIN _grantRole(DEFAULT_ADMIN_ROLE, _owner); @@ -143,9 +132,15 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { /// @notice Return the withdrawal queue length. /// @return uint256 length - function withdrawalQueueLength() external view returns (uint256) { + function withdrawalQueueLength() external pure returns (uint256) { WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); return $.withdrawalQueue.length; } + + function _getWithdrawalQueueStorage() private pure returns (WithdrawalQueueStorage storage $) { + assembly { + $.slot := WithdrawalQueueStorageLocation + } + } } diff --git a/src/interface/IWithdrawalQueue.sol b/src/interface/IWithdrawalQueue.sol index fca03729..1bf65969 100644 --- a/src/interface/IWithdrawalQueue.sol +++ b/src/interface/IWithdrawalQueue.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; interface IWithdrawalQueue { - function init(address _owner, address[] calldata _initialStrategies) external; + function init(address _owner, address _eulerAggregationVault) external; function addStrategyToWithdrawalQueue(address _strategy) external; function removeStrategyFromWithdrawalQueue(address _strategy) external; function executeWithdrawFromQueue( diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 0ecbd396..47f7e600 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -37,12 +37,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { fourSixTwoSixAgg = FourSixTwoSixAgg( fourSixTwoSixAggFactory.deployEulerAggregationLayer( - address(assetTST), - "assetTST_Agg", - "assetTST_Agg", - CASH_RESERVE_ALLOCATION_POINTS, - new address[](0), - new uint256[](0) + address(assetTST), "assetTST_Agg", "assetTST_Agg", CASH_RESERVE_ALLOCATION_POINTS ) ); withdrawalQueue = WithdrawalQueue(fourSixTwoSixAgg.withdrawalQueue()); diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 1c619b88..024ff17d 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -27,12 +27,7 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), trackingReward, address(rebalancer)); fourSixTwoSixAgg = FourSixTwoSixAgg( fourSixTwoSixAggFactory.deployEulerAggregationLayer( - address(assetTST), - "assetTST_Agg", - "assetTST_Agg", - CASH_RESERVE_ALLOCATION_POINTS, - new address[](0), - new uint256[](0) + address(assetTST), "assetTST_Agg", "assetTST_Agg", CASH_RESERVE_ALLOCATION_POINTS ) ); diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index bcf2c201..a866267a 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -1,156 +1,156 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract GulpTest is FourSixTwoSixAggBase { - uint256 user1InitialBalance = 100000e18; - uint256 amountToDeposit = 10000e18; - - function setUp() public virtual override { - super.setUp(); - - uint256 initialStrategyAllocationPoints = 500e18; - _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); - - assetTST.mint(user1, user1InitialBalance); - - // 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); - address[] memory strategiesToRebalance = new address[](1); - strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); - - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); - } - } - - function testGulpAfterNegativeYieldEqualToInterestLeft() public { - fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - assertEq(ers.interestLeft, 0); - - vm.warp(block.timestamp + 2 days); - fourSixTwoSixAgg.gulp(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - - vm.warp(block.timestamp + 1 days); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - uint256 yield; - { - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); - uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; - yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; - assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); - } - vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); - - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - - vm.warp(block.timestamp + 1 days); - // interest per day 23.809523809523 - assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(ers.interestLeft, yield - 23809523809523809523); - - // move close to end of smearing - vm.warp(block.timestamp + 11 days); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - - // mock a decrease of strategy balance by ers.interestLeft - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - ers.interestLeft; - vm.mockCall( - address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), - abi.encode(aggrCurrentStrategyBalanceAfterNegYield) - ); - vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); - } - - function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { - fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - assertEq(ers.interestLeft, 0); - - vm.warp(block.timestamp + 2 days); - fourSixTwoSixAgg.gulp(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - - vm.warp(block.timestamp + 1 days); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - uint256 yield; - { - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); - uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; - yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; - assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); - } - vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); - - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - - vm.warp(block.timestamp + 1 days); - // interest per day 23.809523809523 - assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(ers.interestLeft, yield - 23809523809523809523); - - // move close to end of smearing - vm.warp(block.timestamp + 11 days); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - - // mock a decrease of strategy balance by ers.interestLeft - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - (ers.interestLeft * 2); - vm.mockCall( - address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), - abi.encode(aggrCurrentStrategyBalanceAfterNegYield) - ); - vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); - } -} +// // SPDX-License-Identifier: GPL-2.0-or-later +// pragma solidity ^0.8.0; + +// import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault} from "../common/FourSixTwoSixAggBase.t.sol"; + +// contract GulpTest is FourSixTwoSixAggBase { +// uint256 user1InitialBalance = 100000e18; +// uint256 amountToDeposit = 10000e18; + +// function setUp() public virtual override { +// super.setUp(); + +// uint256 initialStrategyAllocationPoints = 500e18; +// _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); + +// assetTST.mint(user1, user1InitialBalance); + +// // 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); +// address[] memory strategiesToRebalance = new address[](1); +// strategiesToRebalance[0] = address(eTST); +// rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + +// assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); +// assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); +// assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); +// } +// } + +// function testGulpAfterNegativeYieldEqualToInterestLeft() public { +// fourSixTwoSixAgg.gulp(); +// FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); +// assertEq(ers.interestLeft, 0); + +// vm.warp(block.timestamp + 2 days); +// fourSixTwoSixAgg.gulp(); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + +// vm.warp(block.timestamp + 1 days); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); +// uint256 yield; +// { +// uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); +// uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); +// uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; +// yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; +// assetTST.mint(address(eTST), yield); +// eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); +// } +// vm.prank(user1); +// fourSixTwoSixAgg.harvest(address(eTST)); + +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + +// vm.warp(block.timestamp + 1 days); +// // interest per day 23.809523809523 +// assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); +// fourSixTwoSixAgg.gulp(); +// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); +// assertEq(ers.interestLeft, yield - 23809523809523809523); + +// // move close to end of smearing +// vm.warp(block.timestamp + 11 days); +// fourSixTwoSixAgg.gulp(); +// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + +// // mock a decrease of strategy balance by ers.interestLeft +// uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); +// uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - ers.interestLeft; +// vm.mockCall( +// address(eTST), +// abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), +// abi.encode(aggrCurrentStrategyBalanceAfterNegYield) +// ); +// vm.prank(user1); +// fourSixTwoSixAgg.harvest(address(eTST)); +// } + +// function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { +// fourSixTwoSixAgg.gulp(); +// FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); +// assertEq(ers.interestLeft, 0); + +// vm.warp(block.timestamp + 2 days); +// fourSixTwoSixAgg.gulp(); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + +// vm.warp(block.timestamp + 1 days); +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); +// uint256 yield; +// { +// uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); +// uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); +// uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; +// yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; +// assetTST.mint(address(eTST), yield); +// eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); +// } +// vm.prank(user1); +// fourSixTwoSixAgg.harvest(address(eTST)); + +// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + +// vm.warp(block.timestamp + 1 days); +// // interest per day 23.809523809523 +// assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); +// fourSixTwoSixAgg.gulp(); +// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); +// assertEq(ers.interestLeft, yield - 23809523809523809523); + +// // move close to end of smearing +// vm.warp(block.timestamp + 11 days); +// fourSixTwoSixAgg.gulp(); +// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + +// // mock a decrease of strategy balance by ers.interestLeft +// uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); +// uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - (ers.interestLeft * 2); +// vm.mockCall( +// address(eTST), +// abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), +// abi.encode(aggrCurrentStrategyBalanceAfterNegYield) +// ); +// vm.prank(user1); +// fourSixTwoSixAgg.harvest(address(eTST)); +// } +// } From 8785df043aebde3f6fd0c7ffc7e0a0ee80fbf2aa Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:45:20 +0300 Subject: [PATCH 08/19] working version --- src/BalanceForwarder.sol | 57 +++++----- src/FourSixTwoSixAgg.sol | 100 ++++++------------ src/Hooks.sol | 21 +--- src/Rebalancer.sol | 4 +- src/interface/IFourSixTwoSixAgg.sol | 14 +-- src/lib/StorageLib.sol | 82 ++++++++++++++ test/common/FourSixTwoSixAggBase.t.sol | 4 +- ...positRebalanceHarvestWithdrawE2ETest.t.sol | 26 +++-- test/e2e/PerformanceFeeE2ETest.t.sol | 7 +- test/e2e/StrategyCapE2ETest.t.sol | 9 +- .../fuzz/AdjustAllocationPointsFuzzTest.t.sol | 4 +- test/unit/AdjustAllocationPointsTest.t.sol | 4 +- test/unit/GulpTest.t.sol | 4 +- test/unit/HarvestTest.t.sol | 14 +-- test/unit/RebalanceTest.t.sol | 15 +-- test/unit/RemoveStrategy.t.sol | 6 +- 16 files changed, 200 insertions(+), 171 deletions(-) create mode 100644 src/lib/StorageLib.sol diff --git a/src/BalanceForwarder.sol b/src/BalanceForwarder.sol index 6418d7d4..687302c8 100644 --- a/src/BalanceForwarder.sol +++ b/src/BalanceForwarder.sol @@ -1,36 +1,34 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "./lib/StorageLib.sol"; import {IBalanceForwarder} from "./interface/IBalanceForwarder.sol"; import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; +import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; /// @title BalanceForwarder contract /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams -abstract contract BalanceForwarder is IBalanceForwarder { +abstract contract BalanceForwarderModule is IBalanceForwarder { + using StorageLib for *; + error NotSupported(); error AlreadyEnabled(); error AlreadyDisabled(); - /// @custom:storage-location erc7201:euler_aggregation_vault.storage.BalanceForwarder - struct BalanceForwarderStorage { - IBalanceTracker balanceTracker; - mapping(address => bool) isBalanceForwarderEnabled; - } - - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant BalanceForwarderStorageLocation = - 0xf0af7cc3986d6cac468ac54c07f5f08359b3a00d65f8e2a86f2676ec79073f00; - - event EnableBalanceForwarder(address indexed _user); event DisableBalanceForwarder(address indexed _user); /// @notice Enables balance forwarding for the authenticated account /// @dev Only the authenticated account can enable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the current account's balance - function enableBalanceForwarder() external virtual; + function enableBalanceForwarder() external override { + address user = _msgSender(); + uint256 userBalance = this.balanceOf(user); + + _enableBalanceForwarder(user, userBalance); + } /// @notice Disables balance forwarding for the authenticated account /// @dev Only the authenticated account can disable balance forwarding for itself @@ -40,7 +38,7 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address function balanceTrackerAddress() external view returns (address) { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); return address($.balanceTracker); } @@ -53,8 +51,8 @@ abstract contract BalanceForwarder is IBalanceForwarder { } function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); - IBalanceTracker balanceTrackerCached = $.balanceTracker; + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); if (address(balanceTrackerCached) == address(0)) revert NotSupported(); if ($.isBalanceForwarderEnabled[_sender]) revert AlreadyEnabled(); @@ -69,9 +67,9 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @dev Only the authenticated account can disable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the account's balance of 0 function _disableBalanceForwarder(address _sender) internal { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); - IBalanceTracker balanceTrackerCached = $.balanceTracker; - + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); + if (address(balanceTrackerCached) == address(0)) revert NotSupported(); if (!$.isBalanceForwarderEnabled[_sender]) revert AlreadyDisabled(); @@ -82,16 +80,16 @@ abstract contract BalanceForwarder is IBalanceForwarder { } function _setBalanceTracker(address _balancerTracker) internal { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - $.balanceTracker = IBalanceTracker(_balancerTracker); + $.balanceTracker = _balancerTracker; } /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract /// @param _account Address to query /// @return True if balance forwarder is enabled function _balanceForwarderEnabled(address _account) internal view returns (bool) { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); return $.isBalanceForwarderEnabled[_account]; } @@ -99,14 +97,21 @@ abstract contract BalanceForwarder is IBalanceForwarder { /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address function _balanceTrackerAddress() internal view returns (address) { - BalanceForwarderStorage storage $ = _getBalanceForwarderStorage(); + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); return address($.balanceTracker); } - function _getBalanceForwarderStorage() private pure returns (BalanceForwarderStorage storage $) { - assembly { - $.slot := BalanceForwarderStorageLocation + function _msgSender() internal view override returns (address) { + address sender = msg.sender; + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (sender == address($.evc)) { + (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); } + + return sender; } } + +contract BalanceForwarder is BalanceForwarderModule {} \ No newline at end of file diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index b2ce132c..3e20cd25 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -17,6 +17,7 @@ import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; // internal dep +import {StorageLib, AggregationVaultStorage, Strategy} from "./lib/StorageLib.sol"; import {Hooks} from "./Hooks.sol"; import {BalanceForwarder, IBalanceForwarder, IBalanceTracker} from "./BalanceForwarder.sol"; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; @@ -70,39 +71,6 @@ contract FourSixTwoSixAgg is uint256 internal constant MAX_PERFORMANCE_FEE = 0.5e18; uint256 public constant INTEREST_SMEAR = 2 weeks; - /// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVault - struct AggregationVaultStorage { - IEVC evc; - /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract - uint256 totalAssetsDeposited; - /// @dev Total amount of _asset deposited across all strategies. - uint256 totalAllocated; - /// @dev Total amount of allocation points across all strategies including the cash reserve. - uint256 totalAllocationPoints; - /// @dev fee rate - uint256 performanceFee; - /// @dev fee recipient address - address feeRecipient; - /// @dev Withdrawal queue contract's address - address withdrawalQueue; - /// @dev Mapping between strategy address and it's allocation config - mapping(address => Strategy) strategies; - - - /// lastInterestUpdate: last timestamo where interest was updated. - uint40 lastInterestUpdate; - /// interestSmearEnd: timestamp when the smearing of interest end. - uint40 interestSmearEnd; - /// interestLeft: amount of interest left to smear. - uint168 interestLeft; - /// locked: if locked or not for update. - uint8 locked; - } - - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant AggregationVaultStorageLocation = - 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; - event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); event SetPerformanceFee(uint256 oldFee, uint256 newFee); event OptInStrategyRewards(address indexed strategy); @@ -118,7 +86,7 @@ contract FourSixTwoSixAgg is /// @dev Non reentrancy modifier for interest rate updates modifier nonReentrant() { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if ($.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); @@ -152,12 +120,12 @@ contract FourSixTwoSixAgg is if (_initialCashAllocationPoints == 0) revert InitialAllocationPointsZero(); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.withdrawalQueue = _withdrawalQueue; $.strategies[address(0)] = Strategy({allocated: 0, allocationPoints: _initialCashAllocationPoints.toUint120(), active: true, cap: 0}); $.totalAllocationPoints = _initialCashAllocationPoints; - $.evc = IEVC(_evc); + $.evc = _evc; _setBalanceTracker(_balanceTracker); @@ -177,7 +145,7 @@ contract FourSixTwoSixAgg is /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); address feeRecipientCached = $.feeRecipient; if (_newFeeRecipient == feeRecipientCached) revert FeeRecipientAlreadySet(); @@ -190,7 +158,7 @@ contract FourSixTwoSixAgg is /// @notice Set performance fee (1e18 == 100%) /// @notice @param _newFee Fee rate function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); uint256 performanceFeeCached = $.performanceFee; @@ -206,7 +174,7 @@ contract FourSixTwoSixAgg is /// @notice Opt in to strategy rewards /// @param _strategy Strategy address function optInStrategyRewards(address _strategy) external onlyRole(MANAGER) nonReentrant { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if (!$.strategies[_strategy].active) revert InactiveStrategy(); @@ -276,7 +244,7 @@ contract FourSixTwoSixAgg is nonReentrant onlyRole(REBALANCER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); Strategy memory strategyData = $.strategies[_strategy]; @@ -304,7 +272,7 @@ contract FourSixTwoSixAgg is nonReentrant onlyRole(STRATEGY_MANAGER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); Strategy memory strategyDataCache = $.strategies[_strategy]; @@ -323,7 +291,7 @@ contract FourSixTwoSixAgg is /// @param _strategy Strategy address. /// @param _cap Cap amount function setStrategyCap(address _strategy, uint256 _cap) external nonReentrant onlyRole(STRATEGY_MANAGER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if (!$.strategies[_strategy].active) { revert InactiveStrategy(); @@ -339,7 +307,7 @@ contract FourSixTwoSixAgg is /// @param _strategy Address of the strategy /// @param _allocationPoints Strategy's allocation points function addStrategy(address _strategy, uint256 _allocationPoints) external nonReentrant onlyRole(STRATEGY_ADDER) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if ($.strategies[_strategy].active) { revert StrategyAlreadyExist(); @@ -367,7 +335,7 @@ contract FourSixTwoSixAgg is function removeStrategy(address _strategy) external nonReentrant onlyRole(STRATEGY_REMOVER) { if (_strategy == address(0)) revert CanNotRemoveCashReserve(); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); Strategy storage strategyStorage = $.strategies[_strategy]; @@ -401,7 +369,7 @@ contract FourSixTwoSixAgg is /// @param _strategy strategy's address /// @return Strategy struct function getStrategy(address _strategy) external view returns (Strategy memory) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.strategies[_strategy]; } @@ -413,31 +381,31 @@ contract FourSixTwoSixAgg is } function totalAllocated() external view returns (uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.totalAllocated; } function totalAllocationPoints() external view returns (uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.totalAllocationPoints; } function totalAssetsDeposited() external view returns (uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.totalAssetsDeposited; } function withdrawalQueue() external view returns (address) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.withdrawalQueue; } function performanceFeeConfig() external view returns (address, uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return ($.feeRecipient, $.performanceFee); } @@ -490,7 +458,7 @@ contract FourSixTwoSixAgg is function _updateInterestAccrued() internal { uint256 accruedInterest = _interestAccruedFromCache(); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); // it's safe to down-cast because the accrued interest is a fraction of interest left $.interestLeft -= uint168(accruedInterest); $.lastInterestUpdate = uint40(block.timestamp); @@ -502,7 +470,7 @@ contract FourSixTwoSixAgg is /// @notice Return the total amount of assets deposited, plus the accrued interest. /// @return uint256 total amount function totalAssets() public view override returns (uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.totalAssetsDeposited + _interestAccruedFromCache(); } @@ -511,7 +479,7 @@ contract FourSixTwoSixAgg is /// @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) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return IERC20(asset()).balanceOf(address(this)) + $.totalAllocated; } @@ -521,7 +489,7 @@ contract FourSixTwoSixAgg is function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override { _callHooksTarget(DEPOSIT, caller); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.totalAssetsDeposited += assets; @@ -538,7 +506,7 @@ contract FourSixTwoSixAgg is { _callHooksTarget(WITHDRAW, caller); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.totalAssetsDeposited -= assets; uint256 assetsRetrieved = IERC20(asset()).balanceOf(address(this)); @@ -554,7 +522,7 @@ contract FourSixTwoSixAgg is // TODO: add access control function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); // Update allocated assets $.strategies[_strategy].allocated -= uint120(_withdrawAmount); @@ -588,7 +556,7 @@ contract FourSixTwoSixAgg is function _gulp() internal { _updateInterestAccrued(); - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if ($.totalAssetsDeposited == 0) return; uint256 toGulp = totalAssetsAllocatable() - $.totalAssetsDeposited - $.interestLeft; @@ -605,7 +573,7 @@ contract FourSixTwoSixAgg is } function _harvest(address _strategy) internal { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); uint120 strategyAllocatedAmount = $.strategies[_strategy].allocated; @@ -645,7 +613,7 @@ contract FourSixTwoSixAgg is } function _accruePerformanceFee(address _strategy, uint256 _yield) internal returns (uint120) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); address cachedFeeRecipient = $.feeRecipient; uint256 cachedPerformanceFee = $.performanceFee; @@ -687,7 +655,7 @@ contract FourSixTwoSixAgg is /// @dev Get accrued interest without updating it. /// @return uint256 accrued interest function _interestAccruedFromCache() internal view returns (uint256) { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); // If distribution ended, full amount is accrued if (block.timestamp >= $.interestSmearEnd) { @@ -712,24 +680,18 @@ contract FourSixTwoSixAgg is /// @return The address of the message sender. function _msgSender() internal view override (ContextUpgradeable) returns (address) { address sender = msg.sender; - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if (sender == address($.evc)) { - (sender,) = $.evc.getCurrentOnBehalfOfAccount(address(0)); + (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); } return sender; } function _lock() private onlyInitializing { - AggregationVaultStorage storage $ = _getAggregationVaultStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.locked = REENTRANCYLOCK__UNLOCKED; } - - function _getAggregationVaultStorage() private pure returns (AggregationVaultStorage storage $) { - assembly { - $.slot := AggregationVaultStorageLocation - } - } } diff --git a/src/Hooks.sol b/src/Hooks.sol index c73f3b28..a205262e 100644 --- a/src/Hooks.sol +++ b/src/Hooks.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {StorageLib, HooksStorage} from "./lib/StorageLib.sol"; import {HooksLib} from "./lib/HooksLib.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; @@ -20,21 +21,13 @@ abstract contract Hooks { uint256 constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; - struct HooksStorage { - /// @dev storing the hooks target and kooked functions. - uint256 hooksConfig; - } - - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.Hooks")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant HooksStorageLocation = 0x7daefa3ee1567d8892b825e51ce683a058a5785193bcc2ca50940db02ccbf700; - event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); /// @notice Get the hooks contract and the hooked functions. /// @return address Hooks contract. /// @return uint32 Hooked functions. function getHooksConfig() external view returns (address, uint32) { - HooksStorage storage $ = _getHooksStorage(); + HooksStorage storage $ = StorageLib._getHooksStorage(); return _getHooksConfig($.hooksConfig); } @@ -59,7 +52,7 @@ abstract contract Hooks { } if (_hookedFns >= ACTIONS_COUNTER) revert InvalidHookedFns(); - HooksStorage storage $ = _getHooksStorage(); + HooksStorage storage $ = StorageLib._getHooksStorage(); $.hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); emit SetHooksConfig(_hooksTarget, _hookedFns); @@ -69,7 +62,7 @@ abstract contract Hooks { /// @param _fn Function to call the hook for. /// @param _caller Caller's address. function _callHooksTarget(uint32 _fn, address _caller) internal { - HooksStorage storage $ = _getHooksStorage(); + HooksStorage storage $ = StorageLib._getHooksStorage(); (address target, uint32 hookedFns) = _getHooksConfig($.hooksConfig); @@ -98,10 +91,4 @@ abstract contract Hooks { revert EmptyError(); } - - function _getHooksStorage() private pure returns (HooksStorage storage $) { - assembly { - $.slot := HooksStorageLocation - } - } } diff --git a/src/Rebalancer.sol b/src/Rebalancer.sol index 6c5f554a..e783d700 100644 --- a/src/Rebalancer.sol +++ b/src/Rebalancer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {IFourSixTwoSixAgg, Strategy} from "./interface/IFourSixTwoSixAgg.sol"; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; contract Rebalancer { @@ -36,7 +36,7 @@ contract Rebalancer { IFourSixTwoSixAgg(_curatedVault).harvest(_strategy); - IFourSixTwoSixAgg.Strategy memory strategyData = IFourSixTwoSixAgg(_curatedVault).getStrategy(_strategy); + Strategy memory strategyData = IFourSixTwoSixAgg(_curatedVault).getStrategy(_strategy); uint256 totalAllocationPointsCache = IFourSixTwoSixAgg(_curatedVault).totalAllocationPoints(); uint256 totalAssetsAllocatableCache = IFourSixTwoSixAgg(_curatedVault).totalAssetsAllocatable(); diff --git a/src/interface/IFourSixTwoSixAgg.sol b/src/interface/IFourSixTwoSixAgg.sol index c8fdbc84..773b3d23 100644 --- a/src/interface/IFourSixTwoSixAgg.sol +++ b/src/interface/IFourSixTwoSixAgg.sol @@ -1,19 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -interface IFourSixTwoSixAgg { - /// @dev A struct that hold a strategy allocation's config - /// allocated: amount of asset deposited into strategy - /// allocationPoints: number of points allocated to this strategy - /// active: a boolean to indice if this strategy is active or not - /// cap: an optional cap in terms of deposited underlying asset. By default, it is set to 0(not activated) - struct Strategy { - uint120 allocated; - uint120 allocationPoints; - bool active; - uint120 cap; - } +import {Strategy} from "../lib/StorageLib.sol"; +interface IFourSixTwoSixAgg { function rebalance(address _strategy, uint256 _amountToRebalance, bool _isDeposit) external; function gulp() external; function harvest(address strategy) external; diff --git a/src/lib/StorageLib.sol b/src/lib/StorageLib.sol new file mode 100644 index 00000000..e3f00b68 --- /dev/null +++ b/src/lib/StorageLib.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +/// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVault +struct AggregationVaultStorage { + address evc; + /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract + uint256 totalAssetsDeposited; + /// @dev Total amount of _asset deposited across all strategies. + uint256 totalAllocated; + /// @dev Total amount of allocation points across all strategies including the cash reserve. + uint256 totalAllocationPoints; + /// @dev fee rate + uint256 performanceFee; + /// @dev fee recipient address + address feeRecipient; + /// @dev Withdrawal queue contract's address + address withdrawalQueue; + /// @dev Mapping between strategy address and it's allocation config + mapping(address => Strategy) strategies; + /// lastInterestUpdate: last timestamo where interest was updated. + uint40 lastInterestUpdate; + /// interestSmearEnd: timestamp when the smearing of interest end. + uint40 interestSmearEnd; + /// interestLeft: amount of interest left to smear. + uint168 interestLeft; + /// locked: if locked or not for update. + uint8 locked; +} +/// @custom:storage-location erc7201:euler_aggregation_vault.storage.BalanceForwarder + +struct BalanceForwarderStorage { + address balanceTracker; + mapping(address => bool) isBalanceForwarderEnabled; +} +/// @custom:storage-location erc7201:euler_aggregation_vault.storage.Hooks + +struct HooksStorage { + /// @dev storing the hooks target and kooked functions. + uint256 hooksConfig; +} +/// @dev A struct that hold a strategy allocation's config +/// allocated: amount of asset deposited into strategy +/// allocationPoints: number of points allocated to this strategy +/// active: a boolean to indice if this strategy is active or not +/// cap: an optional cap in terms of deposited underlying asset. By default, it is set to 0(not activated) + +struct Strategy { + uint120 allocated; + uint120 allocationPoints; + bool active; + uint120 cap; +} + +library StorageLib { + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant AggregationVaultStorageLocation = + 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant BalanceForwarderStorageLocation = + 0xf0af7cc3986d6cac468ac54c07f5f08359b3a00d65f8e2a86f2676ec79073f00; + // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.Hooks")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant HooksStorageLocation = 0x7daefa3ee1567d8892b825e51ce683a058a5785193bcc2ca50940db02ccbf700; + + function _getAggregationVaultStorage() internal pure returns (AggregationVaultStorage storage $) { + assembly { + $.slot := AggregationVaultStorageLocation + } + } + + function _getBalanceForwarderStorage() internal pure returns (BalanceForwarderStorage storage $) { + assembly { + $.slot := BalanceForwarderStorageLocation + } + } + + function _getHooksStorage() internal pure returns (HooksStorage storage $) { + assembly { + $.slot := HooksStorageLocation + } + } +} diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 47f7e600..641c9122 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import "evk/test/unit/evault/EVaultTestBase.t.sol"; -import {FourSixTwoSixAgg} from "../../src/FourSixTwoSixAgg.sol"; +import {FourSixTwoSixAgg, Strategy} from "../../src/FourSixTwoSixAgg.sol"; import {Rebalancer} from "../../src/Rebalancer.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; import {Hooks} from "../../src/Hooks.sol"; @@ -61,7 +61,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { } function testInitialParams() public { - FourSixTwoSixAgg.Strategy memory cashReserve = fourSixTwoSixAgg.getStrategy(address(0)); + Strategy memory cashReserve = fourSixTwoSixAgg.getStrategy(address(0)); assertEq(cashReserve.allocated, 0); assertEq(cashReserve.allocationPoints, CASH_RESERVE_ALLOCATION_POINTS); diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index 281e441f..4038be6b 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -9,7 +9,8 @@ import { IEVault, IRMTestDefault, TestERC20, - WithdrawalQueue + WithdrawalQueue, + Strategy } from "../common/FourSixTwoSixAggBase.t.sol"; contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { @@ -48,7 +49,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -71,7 +72,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // partial withdraw, no need to withdraw from strategy as cash reserve is enough uint256 amountToWithdraw = 6000e18; { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); uint256 strategyShareBalanceBefore = eTST.balanceOf(address(fourSixTwoSixAgg)); uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); @@ -129,7 +130,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -216,9 +217,8 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - FourSixTwoSixAgg.Strategy memory eTSTsecondarystrategyBefore = - fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); assertEq( @@ -316,7 +316,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -406,9 +406,8 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - FourSixTwoSixAgg.Strategy memory eTSTsecondarystrategyBefore = - fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); assertEq( @@ -537,9 +536,8 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - FourSixTwoSixAgg.Strategy memory eTSTsecondarystrategyBefore = - fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); assertEq( diff --git a/test/e2e/PerformanceFeeE2ETest.t.sol b/test/e2e/PerformanceFeeE2ETest.t.sol index 0ecda9a0..5b3a811b 100644 --- a/test/e2e/PerformanceFeeE2ETest.t.sol +++ b/test/e2e/PerformanceFeeE2ETest.t.sol @@ -8,7 +8,8 @@ import { EVault, IEVault, IRMTestDefault, - TestERC20 + TestERC20, + Strategy } from "../common/FourSixTwoSixAggBase.t.sol"; contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { @@ -76,7 +77,7 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -108,7 +109,7 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { (, uint256 performanceFee) = fourSixTwoSixAgg.performanceFeeConfig(); uint256 expectedPerformanceFee = yield * performanceFee / 1e18; - FourSixTwoSixAgg.Strategy memory strategyBeforeHarvest = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBeforeHarvest = fourSixTwoSixAgg.getStrategy(address(eTST)); uint256 totalAllocatedBefore = fourSixTwoSixAgg.totalAllocated(); // harvest diff --git a/test/e2e/StrategyCapE2ETest.t.sol b/test/e2e/StrategyCapE2ETest.t.sol index e0235ecd..4da58520 100644 --- a/test/e2e/StrategyCapE2ETest.t.sol +++ b/test/e2e/StrategyCapE2ETest.t.sol @@ -8,7 +8,8 @@ import { EVault, IEVault, IRMTestDefault, - TestERC20 + TestERC20, + Strategy } from "../common/FourSixTwoSixAggBase.t.sol"; contract StrategyCapE2ETest is FourSixTwoSixAggBase { @@ -31,7 +32,7 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { vm.prank(manager); fourSixTwoSixAgg.setStrategyCap(address(eTST), cap); - FourSixTwoSixAgg.Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(strategy.cap, cap); } @@ -72,7 +73,7 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -132,7 +133,7 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); diff --git a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol index 11cfc703..07c38942 100644 --- a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol +++ b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { function setUp() public virtual override { @@ -21,7 +21,7 @@ contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { vm.prank(manager); fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), _newAllocationPoints); - FourSixTwoSixAgg.Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); if (_newAllocationPoints < strategyAllocationPoints) { assertEq( diff --git a/test/unit/AdjustAllocationPointsTest.t.sol b/test/unit/AdjustAllocationPointsTest.t.sol index 6e7f66fd..0dd6db38 100644 --- a/test/unit/AdjustAllocationPointsTest.t.sol +++ b/test/unit/AdjustAllocationPointsTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { uint256 initialStrategyAllocationPoints = 500e18; @@ -20,7 +20,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { vm.prank(manager); fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); - FourSixTwoSixAgg.Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq( fourSixTwoSixAgg.totalAllocationPoints(), diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index a866267a..d6c383f1 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -1,7 +1,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later // pragma solidity ^0.8.0; -// import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault} from "../common/FourSixTwoSixAggBase.t.sol"; +// import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; // contract GulpTest is FourSixTwoSixAggBase { // uint256 user1InitialBalance = 100000e18; @@ -36,7 +36,7 @@ // // rebalance into strategy // vm.warp(block.timestamp + 86400); // { -// FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); +// Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); // assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); diff --git a/test/unit/HarvestTest.t.sol b/test/unit/HarvestTest.t.sol index 7ee54b8d..6b6efddb 100644 --- a/test/unit/HarvestTest.t.sol +++ b/test/unit/HarvestTest.t.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault} from "../common/FourSixTwoSixAggBase.t.sol"; +import { + FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy +} from "../common/FourSixTwoSixAggBase.t.sol"; contract HarvestTest is FourSixTwoSixAggBase { uint256 user1InitialBalance = 100000e18; @@ -36,7 +38,7 @@ contract HarvestTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -56,7 +58,7 @@ contract HarvestTest is FourSixTwoSixAggBase { function testHarvest() public { // no yield increase - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); uint256 totalAllocatedBefore = fourSixTwoSixAgg.totalAllocated(); assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) == strategyBefore.allocated); @@ -101,7 +103,7 @@ contract HarvestTest is FourSixTwoSixAggBase { abi.encode(aggrCurrentStrategyBalance * 9e17 / 1e18) ); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); @@ -121,7 +123,7 @@ contract HarvestTest is FourSixTwoSixAggBase { abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(fourSixTwoSixAgg)); @@ -164,7 +166,7 @@ contract HarvestTest is FourSixTwoSixAggBase { abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(fourSixTwoSixAgg)); uint256 user1SharesBefore = fourSixTwoSixAgg.balanceOf(user1); diff --git a/test/unit/RebalanceTest.t.sol b/test/unit/RebalanceTest.t.sol index 5342e9b0..23378e97 100644 --- a/test/unit/RebalanceTest.t.sol +++ b/test/unit/RebalanceTest.t.sol @@ -8,7 +8,8 @@ import { EVault, IEVault, IRMTestDefault, - TestERC20 + TestERC20, + Strategy } from "../common/FourSixTwoSixAggBase.t.sol"; contract RebalanceTest is FourSixTwoSixAggBase { @@ -46,7 +47,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -88,7 +89,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -154,7 +155,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into eTSTsecondary vm.warp(block.timestamp + 86400); { - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); assertEq( eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), @@ -205,7 +206,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -260,7 +261,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { vm.warp(block.timestamp + 86400); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -315,7 +316,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { vm.warp(block.timestamp + 86400); - FourSixTwoSixAgg.Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); diff --git a/test/unit/RemoveStrategy.t.sol b/test/unit/RemoveStrategy.t.sol index d90ee41b..ea12437d 100644 --- a/test/unit/RemoveStrategy.t.sol +++ b/test/unit/RemoveStrategy.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; contract RemoveStrategyTest is FourSixTwoSixAggBase { uint256 strategyAllocationPoints; @@ -22,7 +22,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { vm.prank(manager); fourSixTwoSixAgg.removeStrategy(address(eTST)); - FourSixTwoSixAgg.Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); @@ -42,7 +42,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { vm.prank(manager); fourSixTwoSixAgg.removeStrategy(address(eTST)); - FourSixTwoSixAgg.Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); From 9dd308021018a99cdee4838a49b90a770b8ea424 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:47:26 +0300 Subject: [PATCH 09/19] working version --- src/Base.sol | 32 ++++++ src/Dispatch.sol | 59 ++++++++++ src/FourSixTwoSixAgg.sol | 128 ++++++--------------- src/FourSixTwoSixAggFactory.sol | 41 +++++-- src/Hooks.sol | 32 +++--- src/{BalanceForwarder.sol => Rewards.sol} | 78 ++++++++----- src/lib/ErrorsLib.sol | 22 ++++ test/common/FourSixTwoSixAggBase.t.sol | 24 +++- test/e2e/BalanceForwarderE2ETest.t.sol | 12 +- test/e2e/HooksE2ETest.t.sol | 8 +- test/e2e/StrategyCapE2ETest.t.sol | 5 +- test/unit/AdjustAllocationPointsTest.t.sol | 4 +- 12 files changed, 286 insertions(+), 159 deletions(-) create mode 100644 src/Base.sol create mode 100644 src/Dispatch.sol rename src/{BalanceForwarder.sol => Rewards.sol} (66%) create mode 100644 src/lib/ErrorsLib.sol diff --git a/src/Base.sol b/src/Base.sol new file mode 100644 index 00000000..9d86c15d --- /dev/null +++ b/src/Base.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {StorageLib, AggregationVaultStorage} from "./lib/StorageLib.sol"; +import {ErrorsLib} from "./lib/ErrorsLib.sol"; +import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; + +contract Base { + uint8 internal constant REENTRANCYLOCK__UNLOCKED = 1; + uint8 internal constant REENTRANCYLOCK__LOCKED = 2; + + modifier nonReentrant() { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if ($.locked == REENTRANCYLOCK__LOCKED) revert ErrorsLib.Reentrancy(); + + $.locked = REENTRANCYLOCK__LOCKED; + _; + $.locked = REENTRANCYLOCK__UNLOCKED; + } + + function _msgSender() internal view virtual returns (address) { + address sender = msg.sender; + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (sender == address($.evc)) { + (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); + } + + return sender; + } +} diff --git a/src/Dispatch.sol b/src/Dispatch.sol new file mode 100644 index 00000000..a61931ef --- /dev/null +++ b/src/Dispatch.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.8.0; + +import {Base} from "./Base.sol"; +import {HooksModule} from "./Hooks.sol"; +import {RewardsModule} from "./Rewards.sol"; + +abstract contract Dispatch is RewardsModule, HooksModule { + error E_Unauthorized(); + + address public immutable MODULE_REWARDS; + address public immutable MODULE_HOOKS; + + // /// @title DeployedModules + // /// @notice This struct is used to pass in the addresses of EVault modules during deployment + // struct DeployedModules { + // address initialize; + // address token; + // address vault; + // address borrowing; + // address liquidation; + // address riskManager; + // address balanceForwarder; + // address governance; + // } + + // constructor(Integrations memory integrations, DeployedModules memory modules) Base(integrations) { + // MODULE_INITIALIZE = AddressUtils.checkContract(modules.initialize); + // MODULE_TOKEN = AddressUtils.checkContract(modules.token); + // MODULE_VAULT = AddressUtils.checkContract(modules.vault); + // MODULE_BORROWING = AddressUtils.checkContract(modules.borrowing); + // MODULE_LIQUIDATION = AddressUtils.checkContract(modules.liquidation); + // MODULE_RISKMANAGER = AddressUtils.checkContract(modules.riskManager); + // MODULE_BALANCE_FORWARDER = AddressUtils.checkContract(modules.balanceForwarder); + // MODULE_GOVERNANCE = AddressUtils.checkContract(modules.governance); + // } + constructor(address _rewardsModule, address _hooksModule) Base() { + MODULE_REWARDS = _rewardsModule; + MODULE_HOOKS = _hooksModule; + } + + // Modifier proxies the function call to a module and low-level returns the result + modifier use(address module) { + _; // when using the modifier, it is assumed the function body is empty. + delegateToModule(module); + } + + function delegateToModule(address module) private { + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), module, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + } +} diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 3e20cd25..b456bdef 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {Base} from "./Base.sol"; // external dep -import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { ERC4626Upgradeable, @@ -13,48 +13,24 @@ import { import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; // internal dep import {StorageLib, AggregationVaultStorage, Strategy} from "./lib/StorageLib.sol"; -import {Hooks} from "./Hooks.sol"; -import {BalanceForwarder, IBalanceForwarder, IBalanceTracker} from "./BalanceForwarder.sol"; +import {ErrorsLib} from "./lib/ErrorsLib.sol"; +import {IBalanceTracker} from "./Rewards.sol"; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; +import {Dispatch} from "./Dispatch.sol"; +import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; /// @dev Do NOT use with fee on transfer tokens /// @dev Do NOT use with rebasing tokens /// @dev Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol /// @dev inspired by Yearn v3 ❤️ -contract FourSixTwoSixAgg is - ERC4626Upgradeable, - AccessControlEnumerableUpgradeable, - BalanceForwarder, - Hooks, - IFourSixTwoSixAgg -{ +contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradeable, Dispatch, IFourSixTwoSixAgg { using SafeERC20 for IERC20; using SafeCast for uint256; - error Reentrancy(); - error ArrayLengthMismatch(); - error InitialAllocationPointsZero(); - error NegativeYield(); - error InactiveStrategy(); - error InvalidStrategyAsset(); - error StrategyAlreadyExist(); - error AlreadyRemoved(); - error PerformanceFeeAlreadySet(); - error MaxPerformanceFeeExceeded(); - error FeeRecipientNotSet(); - error FeeRecipientAlreadySet(); - error CanNotRemoveCashReserve(); - error DuplicateInitialStrategy(); - - uint8 internal constant REENTRANCYLOCK__UNLOCKED = 1; - uint8 internal constant REENTRANCYLOCK__LOCKED = 2; - // Roles bytes32 public constant STRATEGY_MANAGER = keccak256("STRATEGY_MANAGER"); bytes32 public constant STRATEGY_MANAGER_ADMIN = keccak256("STRATEGY_MANAGER_ADMIN"); @@ -73,8 +49,6 @@ contract FourSixTwoSixAgg is event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); event SetPerformanceFee(uint256 oldFee, uint256 newFee); - event OptInStrategyRewards(address indexed strategy); - event OptOutStrategyRewards(address indexed strategy); event Gulp(uint256 interestLeft, uint256 interestSmearEnd); event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); @@ -84,16 +58,7 @@ contract FourSixTwoSixAgg is event SetStrategyCap(address indexed strategy, uint256 cap); event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); - /// @dev Non reentrancy modifier for interest rate updates - modifier nonReentrant() { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if ($.locked == REENTRANCYLOCK__LOCKED) revert Reentrancy(); - - $.locked = REENTRANCYLOCK__LOCKED; - _; - $.locked = REENTRANCYLOCK__UNLOCKED; - } + constructor(address _rewardsModule, address _hooksModule) Dispatch(_rewardsModule, _hooksModule) {} // /// @param _evc EVC address // /// @param _asset Aggregator's asset address @@ -118,7 +83,7 @@ contract FourSixTwoSixAgg is _lock(); - if (_initialCashAllocationPoints == 0) revert InitialAllocationPointsZero(); + if (_initialCashAllocationPoints == 0) revert ErrorsLib.InitialAllocationPointsZero(); AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.withdrawalQueue = _withdrawalQueue; @@ -148,7 +113,7 @@ contract FourSixTwoSixAgg is AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); address feeRecipientCached = $.feeRecipient; - if (_newFeeRecipient == feeRecipientCached) revert FeeRecipientAlreadySet(); + if (_newFeeRecipient == feeRecipientCached) revert ErrorsLib.FeeRecipientAlreadySet(); emit SetFeeRecipient(feeRecipientCached, _newFeeRecipient); @@ -162,9 +127,9 @@ contract FourSixTwoSixAgg is uint256 performanceFeeCached = $.performanceFee; - if (_newFee > MAX_PERFORMANCE_FEE) revert MaxPerformanceFeeExceeded(); - if ($.feeRecipient == address(0)) revert FeeRecipientNotSet(); - if (_newFee == performanceFeeCached) revert PerformanceFeeAlreadySet(); + if (_newFee > MAX_PERFORMANCE_FEE) revert ErrorsLib.MaxPerformanceFeeExceeded(); + if ($.feeRecipient == address(0)) revert ErrorsLib.FeeRecipientNotSet(); + if (_newFee == performanceFeeCached) revert ErrorsLib.PerformanceFeeAlreadySet(); emit SetPerformanceFee(performanceFeeCached, _newFee); @@ -173,23 +138,11 @@ contract FourSixTwoSixAgg is /// @notice Opt in to strategy rewards /// @param _strategy Strategy address - function optInStrategyRewards(address _strategy) external onlyRole(MANAGER) nonReentrant { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (!$.strategies[_strategy].active) revert InactiveStrategy(); - - IBalanceForwarder(_strategy).enableBalanceForwarder(); - - emit OptInStrategyRewards(_strategy); - } + function optInStrategyRewards(address _strategy) external override onlyRole(MANAGER) use(MODULE_REWARDS) {} /// @notice Opt out of strategy rewards /// @param _strategy Strategy address - function optOutStrategyRewards(address _strategy) external onlyRole(MANAGER) nonReentrant { - IBalanceForwarder(_strategy).disableBalanceForwarder(); - - emit OptOutStrategyRewards(_strategy); - } + function optOutStrategyRewards(address _strategy) external override onlyRole(MANAGER) use(MODULE_REWARDS) {} /// @notice Claim a specific strategy rewards /// @param _strategy Strategy address. @@ -198,13 +151,10 @@ contract FourSixTwoSixAgg is /// @param _forfeitRecentReward Whether to forfeit the recent rewards and not update the accumulator. function claimStrategyReward(address _strategy, address _reward, address _recipient, bool _forfeitRecentReward) external + override onlyRole(MANAGER) - nonReentrant - { - address rewardStreams = IBalanceForwarder(_strategy).balanceTrackerAddress(); - - IRewardStreams(rewardStreams).claimReward(_strategy, _reward, _recipient, _forfeitRecentReward); - } + use(MODULE_REWARDS) + {} /// @notice Enables balance forwarding for sender /// @dev Should call the IBalanceTracker hook with the current user's balance @@ -217,9 +167,7 @@ contract FourSixTwoSixAgg is /// @notice Disables balance forwarding for the sender /// @dev Should call the IBalanceTracker hook with the account's balance of 0 - function disableBalanceForwarder() external override nonReentrant { - _disableBalanceForwarder(_msgSender()); - } + function disableBalanceForwarder() external override use(MODULE_REWARDS) {} /// @notice Harvest strategy. /// @param strategy address of strategy @@ -277,7 +225,7 @@ contract FourSixTwoSixAgg is Strategy memory strategyDataCache = $.strategies[_strategy]; if (!strategyDataCache.active) { - revert InactiveStrategy(); + revert ErrorsLib.InactiveStrategy(); } $.strategies[_strategy].allocationPoints = _newPoints.toUint120(); @@ -294,7 +242,7 @@ contract FourSixTwoSixAgg is AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if (!$.strategies[_strategy].active) { - revert InactiveStrategy(); + revert ErrorsLib.InactiveStrategy(); } $.strategies[_strategy].cap = _cap.toUint120(); @@ -310,11 +258,11 @@ contract FourSixTwoSixAgg is AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); if ($.strategies[_strategy].active) { - revert StrategyAlreadyExist(); + revert ErrorsLib.StrategyAlreadyExist(); } if (IERC4626(_strategy).asset() != asset()) { - revert InvalidStrategyAsset(); + revert ErrorsLib.InvalidStrategyAsset(); } _callHooksTarget(ADD_STRATEGY, _msgSender()); @@ -333,14 +281,14 @@ contract FourSixTwoSixAgg is /// @dev Can only be called by an address that have the STRATEGY_REMOVER /// @param _strategy Address of the strategy function removeStrategy(address _strategy) external nonReentrant onlyRole(STRATEGY_REMOVER) { - if (_strategy == address(0)) revert CanNotRemoveCashReserve(); + if (_strategy == address(0)) revert ErrorsLib.CanNotRemoveCashReserve(); AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); Strategy storage strategyStorage = $.strategies[_strategy]; if (!strategyStorage.active) { - revert AlreadyRemoved(); + revert ErrorsLib.AlreadyRemoved(); } _callHooksTarget(REMOVE_STRATEGY, _msgSender()); @@ -450,9 +398,12 @@ contract FourSixTwoSixAgg is /// @dev This funtion should be overriden to implement access control. /// @param _hooksTarget Hooks contract. /// @param _hookedFns Hooked functions. - function setHooksConfig(address _hooksTarget, uint32 _hookedFns) public override onlyRole(MANAGER) nonReentrant { - _setHooksConfig(_hooksTarget, _hookedFns); - } + function setHooksConfig(address _hooksTarget, uint32 _hookedFns) + external + override + onlyRole(MANAGER) + use(MODULE_HOOKS) + {} /// @notice update accrued interest. function _updateInterestAccrued() internal { @@ -674,24 +625,13 @@ contract FourSixTwoSixAgg is return $.interestLeft * timePassed / totalDuration; } - /// @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. - /// @return The address of the message sender. - function _msgSender() internal view override (ContextUpgradeable) returns (address) { - address sender = msg.sender; - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (sender == address($.evc)) { - (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); - } - - return sender; - } - function _lock() private onlyInitializing { AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.locked = REENTRANCYLOCK__UNLOCKED; } + + function _msgSender() internal view override (ContextUpgradeable, Base) returns (address) { + return Base._msgSender(); + } } diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index e06a99c3..34e67bee 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -1,18 +1,40 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAgg} from "./FourSixTwoSixAgg.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {Rewards} from "./Rewards.sol"; +import {Hooks} from "./Hooks.sol"; import {WithdrawalQueue} from "./WithdrawalQueue.sol"; +import {FourSixTwoSixAgg} from "./FourSixTwoSixAgg.sol"; contract FourSixTwoSixAggFactory { + // core dependencies address public immutable evc; address public immutable balanceTracker; + // core modules + address public immutable rewardsImpl; + address public immutable hooksImpl; + // peripheries + /// @dev Rebalancer periphery, one instance can serve different aggregation vaults address public immutable rebalancer; + /// @dev Withdrawal queue perihperhy, need to be deployed per aggregation vault + address public immutable withdrawalQueueImpl; - constructor(address _evc, address _balanceTracker, address _rebalancer) { + constructor( + address _evc, + address _balanceTracker, + address _rewardsImpl, + address _hooksImpl, + address _rebalancer, + address _withdrawalQueueImpl + ) { evc = _evc; balanceTracker = _balanceTracker; + rewardsImpl = _rewardsImpl; + hooksImpl = _hooksImpl; + rebalancer = _rebalancer; + withdrawalQueueImpl = _withdrawalQueueImpl; } // TODO: decrease bytecode size, use clones or something @@ -22,18 +44,23 @@ contract FourSixTwoSixAggFactory { string memory _symbol, uint256 _initialCashAllocationPoints ) external returns (address) { - WithdrawalQueue withdrawalQueue = new WithdrawalQueue(); - FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(); + // cloning core modules + address rewardsImplAddr = Clones.clone(rewardsImpl); + Hooks hooks = Hooks(Clones.clone(hooksImpl)); + + // cloning peripheries + WithdrawalQueue withdrawalQueue = WithdrawalQueue(Clones.clone(withdrawalQueueImpl)); - address owner = msg.sender; + // deploy new aggregation vault + FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(rewardsImplAddr, address(hooks)); - withdrawalQueue.init(owner, address(fourSixTwoSixAgg)); + withdrawalQueue.init(msg.sender, address(fourSixTwoSixAgg)); fourSixTwoSixAgg.init( evc, balanceTracker, address(withdrawalQueue), rebalancer, - owner, + msg.sender, _asset, _name, _symbol, diff --git a/src/Hooks.sol b/src/Hooks.sol index a205262e..bd03fc55 100644 --- a/src/Hooks.sol +++ b/src/Hooks.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {Base} from "./Base.sol"; import {StorageLib, HooksStorage} from "./lib/StorageLib.sol"; import {HooksLib} from "./lib/HooksLib.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; -abstract contract Hooks { +abstract contract HooksModule is Base { using HooksLib for uint32; error InvalidHooksTarget(); @@ -23,26 +24,10 @@ abstract contract Hooks { event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); - /// @notice Get the hooks contract and the hooked functions. - /// @return address Hooks contract. - /// @return uint32 Hooked functions. - function getHooksConfig() external view returns (address, uint32) { - HooksStorage storage $ = StorageLib._getHooksStorage(); - - return _getHooksConfig($.hooksConfig); - } - /// @notice Set hooks contract and hooked functions. - /// @dev This funtion should be overriden to implement access control and call _setHooksConfig(). /// @param _hooksTarget Hooks contract. /// @param _hookedFns Hooked functions. - function setHooksConfig(address _hooksTarget, uint32 _hookedFns) public virtual; - - /// @notice Set hooks contract and hooked functions. - /// @dev This funtion should be called when implementing setHooksConfig(). - /// @param _hooksTarget Hooks contract. - /// @param _hookedFns Hooked functions. - function _setHooksConfig(address _hooksTarget, uint32 _hookedFns) internal { + function setHooksConfig(address _hooksTarget, uint32 _hookedFns) external virtual nonReentrant { if (_hooksTarget != address(0) && IHookTarget(_hooksTarget).isHookTarget() != IHookTarget.isHookTarget.selector) { revert NotHooksContract(); @@ -58,6 +43,15 @@ abstract contract Hooks { emit SetHooksConfig(_hooksTarget, _hookedFns); } + /// @notice Get the hooks contract and the hooked functions. + /// @return address Hooks contract. + /// @return uint32 Hooked functions. + function getHooksConfig() external view returns (address, uint32) { + HooksStorage storage $ = StorageLib._getHooksStorage(); + + return _getHooksConfig($.hooksConfig); + } + /// @notice Checks whether a hook has been installed for the function and if so, invokes the hook target. /// @param _fn Function to call the hook for. /// @param _caller Caller's address. @@ -92,3 +86,5 @@ abstract contract Hooks { revert EmptyError(); } } + +contract Hooks is HooksModule {} diff --git a/src/BalanceForwarder.sol b/src/Rewards.sol similarity index 66% rename from src/BalanceForwarder.sol rename to src/Rewards.sol index 687302c8..f6a029c1 100644 --- a/src/BalanceForwarder.sol +++ b/src/Rewards.sol @@ -1,39 +1,74 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {Base} from "./Base.sol"; import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "./lib/StorageLib.sol"; import {IBalanceForwarder} from "./interface/IBalanceForwarder.sol"; import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; +import {ErrorsLib} from "./lib/ErrorsLib.sol"; +import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; /// @title BalanceForwarder contract /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams -abstract contract BalanceForwarderModule is IBalanceForwarder { +abstract contract RewardsModule is IBalanceForwarder, Base { using StorageLib for *; - error NotSupported(); - error AlreadyEnabled(); - error AlreadyDisabled(); - + event OptInStrategyRewards(address indexed strategy); + event OptOutStrategyRewards(address indexed strategy); event EnableBalanceForwarder(address indexed _user); event DisableBalanceForwarder(address indexed _user); + /// @notice Opt in to strategy rewards + /// @param _strategy Strategy address + function optInStrategyRewards(address _strategy) external virtual nonReentrant { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (!$.strategies[_strategy].active) revert ErrorsLib.InactiveStrategy(); + + IBalanceForwarder(_strategy).enableBalanceForwarder(); + + emit OptInStrategyRewards(_strategy); + } + + /// @notice Opt out of strategy rewards + /// @param _strategy Strategy address + function optOutStrategyRewards(address _strategy) external virtual nonReentrant { + IBalanceForwarder(_strategy).disableBalanceForwarder(); + + emit OptOutStrategyRewards(_strategy); + } + + /// @notice Claim a specific strategy rewards + /// @param _strategy Strategy address. + /// @param _reward The address of the reward token. + /// @param _recipient The address to receive the claimed reward tokens. + /// @param _forfeitRecentReward Whether to forfeit the recent rewards and not update the accumulator. + function claimStrategyReward(address _strategy, address _reward, address _recipient, bool _forfeitRecentReward) + external + virtual + nonReentrant + { + address rewardStreams = IBalanceForwarder(_strategy).balanceTrackerAddress(); + + IRewardStreams(rewardStreams).claimReward(_strategy, _reward, _recipient, _forfeitRecentReward); + } + /// @notice Enables balance forwarding for the authenticated account /// @dev Only the authenticated account can enable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the current account's balance - function enableBalanceForwarder() external override { - address user = _msgSender(); - uint256 userBalance = this.balanceOf(user); - - _enableBalanceForwarder(user, userBalance); + function enableBalanceForwarder() external virtual { + return; } /// @notice Disables balance forwarding for the authenticated account /// @dev Only the authenticated account can disable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the account's balance of 0 - function disableBalanceForwarder() external virtual; + function disableBalanceForwarder() external virtual nonReentrant { + _disableBalanceForwarder(_msgSender()); + } /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address @@ -54,8 +89,8 @@ abstract contract BalanceForwarderModule is IBalanceForwarder { BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); - if (address(balanceTrackerCached) == address(0)) revert NotSupported(); - if ($.isBalanceForwarderEnabled[_sender]) revert AlreadyEnabled(); + if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); + if ($.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyEnabled(); $.isBalanceForwarderEnabled[_sender] = true; balanceTrackerCached.balanceTrackerHook(_sender, _senderBalance, false); @@ -70,8 +105,8 @@ abstract contract BalanceForwarderModule is IBalanceForwarder { BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); - if (address(balanceTrackerCached) == address(0)) revert NotSupported(); - if (!$.isBalanceForwarderEnabled[_sender]) revert AlreadyDisabled(); + if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); + if (!$.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyDisabled(); $.isBalanceForwarderEnabled[_sender] = false; balanceTrackerCached.balanceTrackerHook(_sender, 0, false); @@ -101,17 +136,6 @@ abstract contract BalanceForwarderModule is IBalanceForwarder { return address($.balanceTracker); } - - function _msgSender() internal view override returns (address) { - address sender = msg.sender; - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (sender == address($.evc)) { - (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); - } - - return sender; - } } -contract BalanceForwarder is BalanceForwarderModule {} \ No newline at end of file +contract Rewards is RewardsModule {} diff --git a/src/lib/ErrorsLib.sol b/src/lib/ErrorsLib.sol new file mode 100644 index 00000000..3b57e3b8 --- /dev/null +++ b/src/lib/ErrorsLib.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +library ErrorsLib { + error Reentrancy(); + error ArrayLengthMismatch(); + error InitialAllocationPointsZero(); + error NegativeYield(); + error InactiveStrategy(); + error InvalidStrategyAsset(); + error StrategyAlreadyExist(); + error AlreadyRemoved(); + error PerformanceFeeAlreadySet(); + error MaxPerformanceFeeExceeded(); + error FeeRecipientNotSet(); + error FeeRecipientAlreadySet(); + error CanNotRemoveCashReserve(); + error DuplicateInitialStrategy(); + error NotSupported(); + error AlreadyEnabled(); + error AlreadyDisabled(); +} diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 641c9122..6ea444b8 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -5,10 +5,12 @@ import "evk/test/unit/evault/EVaultTestBase.t.sol"; import {FourSixTwoSixAgg, Strategy} from "../../src/FourSixTwoSixAgg.sol"; import {Rebalancer} from "../../src/Rebalancer.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; -import {Hooks} from "../../src/Hooks.sol"; +import {Hooks, HooksModule} from "../../src/Hooks.sol"; +import {Rewards} from "../../src/Rewards.sol"; import {FourSixTwoSixAggFactory} from "../../src/FourSixTwoSixAggFactory.sol"; import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; +import {ErrorsLib} from "../../src/lib/ErrorsLib.sol"; contract FourSixTwoSixAggBase is EVaultTestBase { uint256 public constant CASH_RESERVE_ALLOCATION_POINTS = 1000e18; @@ -18,8 +20,14 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address user2; address manager; - FourSixTwoSixAggFactory fourSixTwoSixAggFactory; + // core modules + Rewards rewardsImpl; + Hooks hooksImpl; + // peripheries Rebalancer rebalancer; + WithdrawalQueue withdrawalQueueImpl; + + FourSixTwoSixAggFactory fourSixTwoSixAggFactory; FourSixTwoSixAgg fourSixTwoSixAgg; WithdrawalQueue withdrawalQueue; @@ -32,8 +40,18 @@ contract FourSixTwoSixAggBase is EVaultTestBase { user2 = makeAddr("User_2"); vm.startPrank(deployer); + rewardsImpl = new Rewards(); + hooksImpl = new Hooks(); rebalancer = new Rebalancer(); - fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), address(0), address(rebalancer)); + withdrawalQueueImpl = new WithdrawalQueue(); + fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory( + address(evc), + address(0), + address(rewardsImpl), + address(hooksImpl), + address(rebalancer), + address(withdrawalQueueImpl) + ); fourSixTwoSixAgg = FourSixTwoSixAgg( fourSixTwoSixAggFactory.deployEulerAggregationLayer( diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 024ff17d..2120fb80 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -9,7 +9,8 @@ import { IEVault, IRMTestDefault, TestERC20, - FourSixTwoSixAggFactory + FourSixTwoSixAggFactory, + Rewards } from "../common/FourSixTwoSixAggBase.t.sol"; import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; @@ -24,7 +25,14 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { vm.startPrank(deployer); trackingReward = address(new TrackingRewardStreams(address(evc), 2 weeks)); - fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory(address(evc), trackingReward, address(rebalancer)); + fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory( + address(evc), + trackingReward, + address(rewardsImpl), + address(hooksImpl), + address(rebalancer), + address(withdrawalQueueImpl) + ); fourSixTwoSixAgg = FourSixTwoSixAgg( fourSixTwoSixAggFactory.deployEulerAggregationLayer( address(assetTST), "assetTST_Agg", "assetTST_Agg", CASH_RESERVE_ALLOCATION_POINTS diff --git a/test/e2e/HooksE2ETest.t.sol b/test/e2e/HooksE2ETest.t.sol index efff9207..a0e966c0 100644 --- a/test/e2e/HooksE2ETest.t.sol +++ b/test/e2e/HooksE2ETest.t.sol @@ -10,7 +10,7 @@ import { IRMTestDefault, TestERC20, IHookTarget, - Hooks + HooksModule } from "../common/FourSixTwoSixAggBase.t.sol"; contract HooksE2ETest is FourSixTwoSixAggBase { @@ -45,7 +45,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); vm.startPrank(manager); - vm.expectRevert(Hooks.InvalidHooksTarget.selector); + vm.expectRevert(HooksModule.InvalidHooksTarget.selector); fourSixTwoSixAgg.setHooksConfig(address(0), expectedHookedFns); vm.stopPrank(); } @@ -56,7 +56,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { vm.startPrank(manager); address hooksContract = address(new NotHooksContract()); - vm.expectRevert(Hooks.NotHooksContract.selector); + vm.expectRevert(HooksModule.NotHooksContract.selector); fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } @@ -65,7 +65,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { uint32 expectedHookedFns = 1 << 5; vm.startPrank(manager); address hooksContract = address(new HooksContract()); - vm.expectRevert(Hooks.InvalidHookedFns.selector); + vm.expectRevert(HooksModule.InvalidHookedFns.selector); fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } diff --git a/test/e2e/StrategyCapE2ETest.t.sol b/test/e2e/StrategyCapE2ETest.t.sol index 4da58520..d7373ad4 100644 --- a/test/e2e/StrategyCapE2ETest.t.sol +++ b/test/e2e/StrategyCapE2ETest.t.sol @@ -9,7 +9,8 @@ import { IEVault, IRMTestDefault, TestERC20, - Strategy + Strategy, + ErrorsLib } from "../common/FourSixTwoSixAggBase.t.sol"; contract StrategyCapE2ETest is FourSixTwoSixAggBase { @@ -41,7 +42,7 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { uint256 cap = 1000000e18; vm.prank(manager); - vm.expectRevert(FourSixTwoSixAgg.InactiveStrategy.selector); + vm.expectRevert(ErrorsLib.InactiveStrategy.selector); fourSixTwoSixAgg.setStrategyCap(address(0x2), cap); } diff --git a/test/unit/AdjustAllocationPointsTest.t.sol b/test/unit/AdjustAllocationPointsTest.t.sol index 0dd6db38..12cf042b 100644 --- a/test/unit/AdjustAllocationPointsTest.t.sol +++ b/test/unit/AdjustAllocationPointsTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; +import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy, ErrorsLib} from "../common/FourSixTwoSixAggBase.t.sol"; contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { uint256 initialStrategyAllocationPoints = 500e18; @@ -43,7 +43,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { uint256 newAllocationPoints = 859e18; vm.startPrank(manager); - vm.expectRevert(FourSixTwoSixAgg.InactiveStrategy.selector); + vm.expectRevert(ErrorsLib.InactiveStrategy.selector); fourSixTwoSixAgg.adjustAllocationPoints(address(eTST2), newAllocationPoints); vm.stopPrank(); } From 529aad6f3ef99da3da1428f590abaa7d75bae896 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:01:59 +0300 Subject: [PATCH 10/19] working version --- src/WithdrawalQueue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index 56b7f873..1dd7730b 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -113,7 +113,7 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { /// @param _index1 index of first strategy /// @param _index2 index of second strategy function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { - WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); uint256 length = $.withdrawalQueue.length; if (_index1 >= length || _index2 >= length) { From d6cbb5754ead98494b7ea84c21fb2598ce9fe3ef Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:05:22 +0300 Subject: [PATCH 11/19] working version --- src/Base.sol | 32 ------ src/Dispatch.sol | 33 ++---- src/FourSixTwoSixAgg.sol | 65 +++++------- src/FourSixTwoSixAggFactory.sol | 21 ++-- src/Hooks.sol | 90 ---------------- src/Rewards.sol | 141 ------------------------- test/common/FourSixTwoSixAggBase.t.sol | 9 +- test/e2e/BalanceForwarderE2ETest.t.sol | 1 + 8 files changed, 52 insertions(+), 340 deletions(-) delete mode 100644 src/Base.sol delete mode 100644 src/Hooks.sol delete mode 100644 src/Rewards.sol diff --git a/src/Base.sol b/src/Base.sol deleted file mode 100644 index 9d86c15d..00000000 --- a/src/Base.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -import {StorageLib, AggregationVaultStorage} from "./lib/StorageLib.sol"; -import {ErrorsLib} from "./lib/ErrorsLib.sol"; -import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; - -contract Base { - uint8 internal constant REENTRANCYLOCK__UNLOCKED = 1; - uint8 internal constant REENTRANCYLOCK__LOCKED = 2; - - modifier nonReentrant() { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if ($.locked == REENTRANCYLOCK__LOCKED) revert ErrorsLib.Reentrancy(); - - $.locked = REENTRANCYLOCK__LOCKED; - _; - $.locked = REENTRANCYLOCK__UNLOCKED; - } - - function _msgSender() internal view virtual returns (address) { - address sender = msg.sender; - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (sender == address($.evc)) { - (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); - } - - return sender; - } -} diff --git a/src/Dispatch.sol b/src/Dispatch.sol index a61931ef..60e96a49 100644 --- a/src/Dispatch.sol +++ b/src/Dispatch.sol @@ -2,42 +2,21 @@ pragma solidity ^0.8.0; -import {Base} from "./Base.sol"; -import {HooksModule} from "./Hooks.sol"; -import {RewardsModule} from "./Rewards.sol"; +import {Shared} from "./Shared.sol"; +import {HooksModule} from "./modules/Hooks.sol"; +import {RewardsModule} from "./modules/Rewards.sol"; abstract contract Dispatch is RewardsModule, HooksModule { error E_Unauthorized(); address public immutable MODULE_REWARDS; address public immutable MODULE_HOOKS; + address public immutable MODULE_FEE; - // /// @title DeployedModules - // /// @notice This struct is used to pass in the addresses of EVault modules during deployment - // struct DeployedModules { - // address initialize; - // address token; - // address vault; - // address borrowing; - // address liquidation; - // address riskManager; - // address balanceForwarder; - // address governance; - // } - - // constructor(Integrations memory integrations, DeployedModules memory modules) Base(integrations) { - // MODULE_INITIALIZE = AddressUtils.checkContract(modules.initialize); - // MODULE_TOKEN = AddressUtils.checkContract(modules.token); - // MODULE_VAULT = AddressUtils.checkContract(modules.vault); - // MODULE_BORROWING = AddressUtils.checkContract(modules.borrowing); - // MODULE_LIQUIDATION = AddressUtils.checkContract(modules.liquidation); - // MODULE_RISKMANAGER = AddressUtils.checkContract(modules.riskManager); - // MODULE_BALANCE_FORWARDER = AddressUtils.checkContract(modules.balanceForwarder); - // MODULE_GOVERNANCE = AddressUtils.checkContract(modules.governance); - // } - constructor(address _rewardsModule, address _hooksModule) Base() { + constructor(address _rewardsModule, address _hooksModule, address _feeModule) Shared() { MODULE_REWARDS = _rewardsModule; MODULE_HOOKS = _hooksModule; + MODULE_FEE = _feeModule; } // Modifier proxies the function call to a module and low-level returns the result diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index b456bdef..f109edf8 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {Base} from "./Base.sol"; +import {Shared} from "./Shared.sol"; // external dep import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { @@ -17,7 +17,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; // internal dep import {StorageLib, AggregationVaultStorage, Strategy} from "./lib/StorageLib.sol"; import {ErrorsLib} from "./lib/ErrorsLib.sol"; -import {IBalanceTracker} from "./Rewards.sol"; +import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; import {Dispatch} from "./Dispatch.sol"; @@ -43,12 +43,8 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea bytes32 public constant REBALANCER = keccak256("REBALANCER"); bytes32 public constant REBALANCER_ADMIN = keccak256("REBALANCER_ADMIN"); - /// @dev The maximum performanceFee the vault can have is 50% - uint256 internal constant MAX_PERFORMANCE_FEE = 0.5e18; uint256 public constant INTEREST_SMEAR = 2 weeks; - event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); - event SetPerformanceFee(uint256 oldFee, uint256 newFee); event Gulp(uint256 interestLeft, uint256 interestSmearEnd); event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); @@ -58,8 +54,21 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea event SetStrategyCap(address indexed strategy, uint256 cap); event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); - constructor(address _rewardsModule, address _hooksModule) Dispatch(_rewardsModule, _hooksModule) {} + constructor(address _rewardsModule, address _hooksModule, address _feeModule) + Dispatch(_rewardsModule, _hooksModule, _feeModule) + {} + struct InitParams { + address evc; + address balanceTracker; + address withdrawalQueuePeriphery; + address rebalancerPerihpery; + address aggregationVaultOwner; + address asset; + string name; + string symbol; + uint256 initialCashAllocationPoints; + } // /// @param _evc EVC address // /// @param _asset Aggregator's asset address // /// @param _name Aggregator's name @@ -109,32 +118,11 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address - function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - address feeRecipientCached = $.feeRecipient; - - if (_newFeeRecipient == feeRecipientCached) revert ErrorsLib.FeeRecipientAlreadySet(); - - emit SetFeeRecipient(feeRecipientCached, _newFeeRecipient); - - $.feeRecipient = _newFeeRecipient; - } + function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) use(MODULE_FEE) {} /// @notice Set performance fee (1e18 == 100%) /// @notice @param _newFee Fee rate - function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - uint256 performanceFeeCached = $.performanceFee; - - if (_newFee > MAX_PERFORMANCE_FEE) revert ErrorsLib.MaxPerformanceFeeExceeded(); - if ($.feeRecipient == address(0)) revert ErrorsLib.FeeRecipientNotSet(); - if (_newFee == performanceFeeCached) revert ErrorsLib.PerformanceFeeAlreadySet(); - - emit SetPerformanceFee(performanceFeeCached, _newFee); - - $.performanceFee = _newFee; - } + function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) use(MODULE_FEE) {} /// @notice Opt in to strategy rewards /// @param _strategy Strategy address @@ -158,12 +146,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @notice Enables balance forwarding for sender /// @dev Should call the IBalanceTracker hook with the current user's balance - function enableBalanceForwarder() external override nonReentrant { - address user = _msgSender(); - uint256 userBalance = this.balanceOf(user); - - _enableBalanceForwarder(user, userBalance); - } + function enableBalanceForwarder() external override use(MODULE_REWARDS) {} /// @notice Disables balance forwarding for the sender /// @dev Should call the IBalanceTracker hook with the account's balance of 0 @@ -304,9 +287,9 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea } /// @notice update accrued interest - // function updateInterestAccrued() external returns (AggregationVaultSavingRateStorage memory) { - // return _updateInterestAccrued(); - // } + function updateInterestAccrued() external { + return _updateInterestAccrued(); + } /// @notice gulp positive harvest yield function gulp() external nonReentrant { @@ -631,7 +614,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea $.locked = REENTRANCYLOCK__UNLOCKED; } - function _msgSender() internal view override (ContextUpgradeable, Base) returns (address) { - return Base._msgSender(); + function _msgSender() internal view override (ContextUpgradeable, Shared) returns (address) { + return Shared._msgSender(); } } diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index 34e67bee..ef8f8f45 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -2,19 +2,23 @@ pragma solidity ^0.8.0; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; -import {Rewards} from "./Rewards.sol"; -import {Hooks} from "./Hooks.sol"; +// core modules +import {Rewards} from "./modules/Rewards.sol"; +import {Hooks} from "./modules/Hooks.sol"; +import {Fee} from "./modules/Fee.sol"; +// core peripheries import {WithdrawalQueue} from "./WithdrawalQueue.sol"; import {FourSixTwoSixAgg} from "./FourSixTwoSixAgg.sol"; contract FourSixTwoSixAggFactory { - // core dependencies + /// core dependencies address public immutable evc; address public immutable balanceTracker; - // core modules + /// core modules address public immutable rewardsImpl; address public immutable hooksImpl; - // peripheries + address public immutable feeModuleImpl; + /// peripheries /// @dev Rebalancer periphery, one instance can serve different aggregation vaults address public immutable rebalancer; /// @dev Withdrawal queue perihperhy, need to be deployed per aggregation vault @@ -25,6 +29,7 @@ contract FourSixTwoSixAggFactory { address _balanceTracker, address _rewardsImpl, address _hooksImpl, + address _feeModuleImpl, address _rebalancer, address _withdrawalQueueImpl ) { @@ -32,6 +37,7 @@ contract FourSixTwoSixAggFactory { balanceTracker = _balanceTracker; rewardsImpl = _rewardsImpl; hooksImpl = _hooksImpl; + feeModuleImpl = _feeModuleImpl; rebalancer = _rebalancer; withdrawalQueueImpl = _withdrawalQueueImpl; @@ -46,13 +52,14 @@ contract FourSixTwoSixAggFactory { ) external returns (address) { // cloning core modules address rewardsImplAddr = Clones.clone(rewardsImpl); - Hooks hooks = Hooks(Clones.clone(hooksImpl)); + address hooks = Clones.clone(hooksImpl); + address feeModule = Clones.clone(feeModuleImpl); // cloning peripheries WithdrawalQueue withdrawalQueue = WithdrawalQueue(Clones.clone(withdrawalQueueImpl)); // deploy new aggregation vault - FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(rewardsImplAddr, address(hooks)); + FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(rewardsImplAddr, hooks, feeModule); withdrawalQueue.init(msg.sender, address(fourSixTwoSixAgg)); fourSixTwoSixAgg.init( diff --git a/src/Hooks.sol b/src/Hooks.sol deleted file mode 100644 index bd03fc55..00000000 --- a/src/Hooks.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -import {Base} from "./Base.sol"; -import {StorageLib, HooksStorage} from "./lib/StorageLib.sol"; -import {HooksLib} from "./lib/HooksLib.sol"; -import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; - -abstract contract HooksModule is Base { - using HooksLib for uint32; - - error InvalidHooksTarget(); - error NotHooksContract(); - error InvalidHookedFns(); - error EmptyError(); - - uint32 public constant DEPOSIT = 1 << 0; - uint32 public constant WITHDRAW = 1 << 1; - uint32 public constant ADD_STRATEGY = 1 << 2; - uint32 public constant REMOVE_STRATEGY = 1 << 3; - uint32 constant ACTIONS_COUNTER = 1 << 4; - - uint256 constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; - - event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); - - /// @notice Set hooks contract and hooked functions. - /// @param _hooksTarget Hooks contract. - /// @param _hookedFns Hooked functions. - function setHooksConfig(address _hooksTarget, uint32 _hookedFns) external virtual nonReentrant { - if (_hooksTarget != address(0) && IHookTarget(_hooksTarget).isHookTarget() != IHookTarget.isHookTarget.selector) - { - revert NotHooksContract(); - } - if (_hookedFns != 0 && _hooksTarget == address(0)) { - revert InvalidHooksTarget(); - } - if (_hookedFns >= ACTIONS_COUNTER) revert InvalidHookedFns(); - - HooksStorage storage $ = StorageLib._getHooksStorage(); - $.hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); - - emit SetHooksConfig(_hooksTarget, _hookedFns); - } - - /// @notice Get the hooks contract and the hooked functions. - /// @return address Hooks contract. - /// @return uint32 Hooked functions. - function getHooksConfig() external view returns (address, uint32) { - HooksStorage storage $ = StorageLib._getHooksStorage(); - - return _getHooksConfig($.hooksConfig); - } - - /// @notice Checks whether a hook has been installed for the function and if so, invokes the hook target. - /// @param _fn Function to call the hook for. - /// @param _caller Caller's address. - function _callHooksTarget(uint32 _fn, address _caller) internal { - HooksStorage storage $ = StorageLib._getHooksStorage(); - - (address target, uint32 hookedFns) = _getHooksConfig($.hooksConfig); - - if (hookedFns.isNotSet(_fn)) return; - - (bool success, bytes memory data) = target.call(abi.encodePacked(msg.data, _caller)); - - if (!success) _revertBytes(data); - } - - /// @notice Get the hooks contract and the hooked functions. - /// @return address Hooks contract. - /// @return uint32 Hooked functions. - function _getHooksConfig(uint256 _hooksConfig) internal pure returns (address, uint32) { - return (address(uint160(_hooksConfig >> 32)), uint32(_hooksConfig & HOOKS_MASK)); - } - - /// @dev Revert with call error or EmptyError - /// @param _errorMsg call revert message - function _revertBytes(bytes memory _errorMsg) private pure { - if (_errorMsg.length > 0) { - assembly { - revert(add(32, _errorMsg), mload(_errorMsg)) - } - } - - revert EmptyError(); - } -} - -contract Hooks is HooksModule {} diff --git a/src/Rewards.sol b/src/Rewards.sol deleted file mode 100644 index f6a029c1..00000000 --- a/src/Rewards.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; - -import {Base} from "./Base.sol"; -import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "./lib/StorageLib.sol"; -import {IBalanceForwarder} from "./interface/IBalanceForwarder.sol"; -import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; -import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; -import {ErrorsLib} from "./lib/ErrorsLib.sol"; -import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; - -/// @title BalanceForwarder contract -/// @custom:security-contact security@euler.xyz -/// @author Euler Labs (https://www.eulerlabs.com/) -/// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams -abstract contract RewardsModule is IBalanceForwarder, Base { - using StorageLib for *; - - event OptInStrategyRewards(address indexed strategy); - event OptOutStrategyRewards(address indexed strategy); - event EnableBalanceForwarder(address indexed _user); - event DisableBalanceForwarder(address indexed _user); - - /// @notice Opt in to strategy rewards - /// @param _strategy Strategy address - function optInStrategyRewards(address _strategy) external virtual nonReentrant { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (!$.strategies[_strategy].active) revert ErrorsLib.InactiveStrategy(); - - IBalanceForwarder(_strategy).enableBalanceForwarder(); - - emit OptInStrategyRewards(_strategy); - } - - /// @notice Opt out of strategy rewards - /// @param _strategy Strategy address - function optOutStrategyRewards(address _strategy) external virtual nonReentrant { - IBalanceForwarder(_strategy).disableBalanceForwarder(); - - emit OptOutStrategyRewards(_strategy); - } - - /// @notice Claim a specific strategy rewards - /// @param _strategy Strategy address. - /// @param _reward The address of the reward token. - /// @param _recipient The address to receive the claimed reward tokens. - /// @param _forfeitRecentReward Whether to forfeit the recent rewards and not update the accumulator. - function claimStrategyReward(address _strategy, address _reward, address _recipient, bool _forfeitRecentReward) - external - virtual - nonReentrant - { - address rewardStreams = IBalanceForwarder(_strategy).balanceTrackerAddress(); - - IRewardStreams(rewardStreams).claimReward(_strategy, _reward, _recipient, _forfeitRecentReward); - } - - /// @notice Enables balance forwarding for the authenticated account - /// @dev Only the authenticated account can enable balance forwarding for itself - /// @dev Should call the IBalanceTracker hook with the current account's balance - function enableBalanceForwarder() external virtual { - return; - } - - /// @notice Disables balance forwarding for the authenticated account - /// @dev Only the authenticated account can disable balance forwarding for itself - /// @dev Should call the IBalanceTracker hook with the account's balance of 0 - function disableBalanceForwarder() external virtual nonReentrant { - _disableBalanceForwarder(_msgSender()); - } - - /// @notice Retrieve the address of rewards contract, tracking changes in account's balances - /// @return The balance tracker address - function balanceTrackerAddress() external view returns (address) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - - return address($.balanceTracker); - } - - /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract - /// @param _account Address to query - /// @return True if balance forwarder is enabled - function balanceForwarderEnabled(address _account) external view returns (bool) { - return _balanceForwarderEnabled(_account); - } - - function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); - - if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); - if ($.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyEnabled(); - - $.isBalanceForwarderEnabled[_sender] = true; - balanceTrackerCached.balanceTrackerHook(_sender, _senderBalance, false); - - emit EnableBalanceForwarder(_sender); - } - - /// @notice Disables balance forwarding for the authenticated account - /// @dev Only the authenticated account can disable balance forwarding for itself - /// @dev Should call the IBalanceTracker hook with the account's balance of 0 - function _disableBalanceForwarder(address _sender) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); - - if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); - if (!$.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyDisabled(); - - $.isBalanceForwarderEnabled[_sender] = false; - balanceTrackerCached.balanceTrackerHook(_sender, 0, false); - - emit DisableBalanceForwarder(_sender); - } - - function _setBalanceTracker(address _balancerTracker) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - - $.balanceTracker = _balancerTracker; - } - - /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract - /// @param _account Address to query - /// @return True if balance forwarder is enabled - function _balanceForwarderEnabled(address _account) internal view returns (bool) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - - return $.isBalanceForwarderEnabled[_account]; - } - - /// @notice Retrieve the address of rewards contract, tracking changes in account's balances - /// @return The balance tracker address - function _balanceTrackerAddress() internal view returns (address) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); - - return address($.balanceTracker); - } -} - -contract Rewards is RewardsModule {} diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 6ea444b8..6de1dd67 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -5,8 +5,9 @@ import "evk/test/unit/evault/EVaultTestBase.t.sol"; import {FourSixTwoSixAgg, Strategy} from "../../src/FourSixTwoSixAgg.sol"; import {Rebalancer} from "../../src/Rebalancer.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; -import {Hooks, HooksModule} from "../../src/Hooks.sol"; -import {Rewards} from "../../src/Rewards.sol"; +import {Hooks, HooksModule} from "../../src/modules/Hooks.sol"; +import {Rewards} from "../../src/modules/Rewards.sol"; +import {Fee} from "../../src/modules/Fee.sol"; import {FourSixTwoSixAggFactory} from "../../src/FourSixTwoSixAggFactory.sol"; import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; @@ -23,6 +24,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { // core modules Rewards rewardsImpl; Hooks hooksImpl; + Fee feeModuleImpl; // peripheries Rebalancer rebalancer; WithdrawalQueue withdrawalQueueImpl; @@ -42,6 +44,8 @@ contract FourSixTwoSixAggBase is EVaultTestBase { vm.startPrank(deployer); rewardsImpl = new Rewards(); hooksImpl = new Hooks(); + feeModuleImpl = new Fee(); + rebalancer = new Rebalancer(); withdrawalQueueImpl = new WithdrawalQueue(); fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory( @@ -49,6 +53,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address(0), address(rewardsImpl), address(hooksImpl), + address(feeModuleImpl), address(rebalancer), address(withdrawalQueueImpl) ); diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 2120fb80..b02fca97 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -30,6 +30,7 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { trackingReward, address(rewardsImpl), address(hooksImpl), + address(feeModuleImpl), address(rebalancer), address(withdrawalQueueImpl) ); From 18756aa4d14218744756a129a61afcfff0ed6272 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:09:53 +0300 Subject: [PATCH 12/19] working version --- src/FourSixTwoSixAgg.sol | 41 +++++++++++++++------------------ src/FourSixTwoSixAggFactory.sol | 24 ++++++++++--------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index f109edf8..ee506d51 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -76,37 +76,32 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea // /// @param _initialCashAllocationPoints Initial points to be allocated to the cash reserve // /// @param _initialStrategies An array of initial strategies addresses // /// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points - function init( - address _evc, - address _balanceTracker, - address _withdrawalQueue, - address _rebalancer, - address _owner, - address _asset, - string memory _name, - string memory _symbol, - uint256 _initialCashAllocationPoints - ) external initializer { - __ERC4626_init_unchained(IERC20(_asset)); - __ERC20_init_unchained(_name, _symbol); + + function init(InitParams calldata _initParams) external initializer { + __ERC4626_init_unchained(IERC20(_initParams.asset)); + __ERC20_init_unchained(_initParams.name, _initParams.symbol); _lock(); - if (_initialCashAllocationPoints == 0) revert ErrorsLib.InitialAllocationPointsZero(); + if (_initParams.initialCashAllocationPoints == 0) revert ErrorsLib.InitialAllocationPointsZero(); AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - $.withdrawalQueue = _withdrawalQueue; - $.strategies[address(0)] = - Strategy({allocated: 0, allocationPoints: _initialCashAllocationPoints.toUint120(), active: true, cap: 0}); - $.totalAllocationPoints = _initialCashAllocationPoints; - $.evc = _evc; - - _setBalanceTracker(_balanceTracker); + $.withdrawalQueue = _initParams.withdrawalQueuePeriphery; + $.strategies[address(0)] = Strategy({ + allocated: 0, + allocationPoints: _initParams.initialCashAllocationPoints.toUint120(), + active: true, + cap: 0 + }); + $.totalAllocationPoints = _initParams.initialCashAllocationPoints; + $.evc = _initParams.evc; + + _setBalanceTracker(_initParams.balanceTracker); // Setup DEFAULT_ADMIN - _grantRole(DEFAULT_ADMIN_ROLE, _owner); + _grantRole(DEFAULT_ADMIN_ROLE, _initParams.aggregationVaultOwner); // By default, the Rebalancer contract is assigned the REBALANCER role - _grantRole(REBALANCER, _rebalancer); + _grantRole(REBALANCER, _initParams.rebalancerPerihpery); // Setup role admins _setRoleAdmin(STRATEGY_MANAGER, STRATEGY_MANAGER_ADMIN); diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index ef8f8f45..5e9a6ac5 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -61,18 +61,20 @@ contract FourSixTwoSixAggFactory { // deploy new aggregation vault FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(rewardsImplAddr, hooks, feeModule); + FourSixTwoSixAgg.InitParams memory aggregationVaultInitParams = FourSixTwoSixAgg.InitParams({ + evc: evc, + balanceTracker: balanceTracker, + withdrawalQueuePeriphery: address(withdrawalQueue), + rebalancerPerihpery: rebalancer, + aggregationVaultOwner: msg.sender, + asset: _asset, + name: _name, + symbol: _symbol, + initialCashAllocationPoints: _initialCashAllocationPoints + }); + withdrawalQueue.init(msg.sender, address(fourSixTwoSixAgg)); - fourSixTwoSixAgg.init( - evc, - balanceTracker, - address(withdrawalQueue), - rebalancer, - msg.sender, - _asset, - _name, - _symbol, - _initialCashAllocationPoints - ); + fourSixTwoSixAgg.init(aggregationVaultInitParams); return address(fourSixTwoSixAgg); } From 328270908288d70116e692224ba91ba7b3da4924 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:40:42 +0300 Subject: [PATCH 13/19] bytecode under limit --- src/Dispatch.sol | 6 +- src/FourSixTwoSixAgg.sol | 86 +++------------ src/FourSixTwoSixAggFactory.sol | 41 +++---- src/Shared.sol | 96 +++++++++++++++++ src/lib/ErrorsLib.sol | 4 + src/modules/AllocationPoints.sol | 109 +++++++++++++++++++ src/modules/Fee.sol | 51 +++++++++ src/modules/Hooks.sol | 29 +++++ src/modules/Rewards.sol | 144 +++++++++++++++++++++++++ test/common/FourSixTwoSixAggBase.t.sol | 4 + test/e2e/BalanceForwarderE2ETest.t.sol | 1 + test/e2e/HooksE2ETest.t.sol | 8 +- 12 files changed, 485 insertions(+), 94 deletions(-) create mode 100644 src/Shared.sol create mode 100644 src/modules/AllocationPoints.sol create mode 100644 src/modules/Fee.sol create mode 100644 src/modules/Hooks.sol create mode 100644 src/modules/Rewards.sol diff --git a/src/Dispatch.sol b/src/Dispatch.sol index 60e96a49..16a75f16 100644 --- a/src/Dispatch.sol +++ b/src/Dispatch.sol @@ -12,11 +12,15 @@ abstract contract Dispatch is RewardsModule, HooksModule { address public immutable MODULE_REWARDS; address public immutable MODULE_HOOKS; address public immutable MODULE_FEE; + address public immutable MODULE_ALLOCATION_POINTS; - constructor(address _rewardsModule, address _hooksModule, address _feeModule) Shared() { + constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) + Shared() + { MODULE_REWARDS = _rewardsModule; MODULE_HOOKS = _hooksModule; MODULE_FEE = _feeModule; + MODULE_ALLOCATION_POINTS = _allocationPointsModule; } // Modifier proxies the function call to a module and low-level returns the result diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index ee506d51..3d518e81 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -54,8 +54,8 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea event SetStrategyCap(address indexed strategy, uint256 cap); event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); - constructor(address _rewardsModule, address _hooksModule, address _feeModule) - Dispatch(_rewardsModule, _hooksModule, _feeModule) + constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) + Dispatch(_rewardsModule, _hooksModule, _feeModule, _allocationPointsModule) {} struct InitParams { @@ -195,91 +195,35 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @param _newPoints new strategy's points function adjustAllocationPoints(address _strategy, uint256 _newPoints) external - nonReentrant + use(MODULE_ALLOCATION_POINTS) onlyRole(STRATEGY_MANAGER) - { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - Strategy memory strategyDataCache = $.strategies[_strategy]; - - if (!strategyDataCache.active) { - revert ErrorsLib.InactiveStrategy(); - } - - $.strategies[_strategy].allocationPoints = _newPoints.toUint120(); - $.totalAllocationPoints = $.totalAllocationPoints + _newPoints - strategyDataCache.allocationPoints; - - emit AdjustAllocationPoints(_strategy, strategyDataCache.allocationPoints, _newPoints); - } + {} /// @notice Set cap on strategy allocated amount. /// @dev By default, cap is set to 0, not activated. /// @param _strategy Strategy address. /// @param _cap Cap amount - function setStrategyCap(address _strategy, uint256 _cap) external nonReentrant onlyRole(STRATEGY_MANAGER) { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if (!$.strategies[_strategy].active) { - revert ErrorsLib.InactiveStrategy(); - } - - $.strategies[_strategy].cap = _cap.toUint120(); - - emit SetStrategyCap(_strategy, _cap); - } + function setStrategyCap(address _strategy, uint256 _cap) + external + use(MODULE_ALLOCATION_POINTS) + onlyRole(STRATEGY_MANAGER) + {} /// @notice Add new strategy with it's allocation points. /// @dev Can only be called by an address that have STRATEGY_ADDER. /// @param _strategy Address of the strategy /// @param _allocationPoints Strategy's allocation points - function addStrategy(address _strategy, uint256 _allocationPoints) external nonReentrant onlyRole(STRATEGY_ADDER) { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - if ($.strategies[_strategy].active) { - revert ErrorsLib.StrategyAlreadyExist(); - } - - if (IERC4626(_strategy).asset() != asset()) { - revert ErrorsLib.InvalidStrategyAsset(); - } - - _callHooksTarget(ADD_STRATEGY, _msgSender()); - - $.strategies[_strategy] = - Strategy({allocated: 0, allocationPoints: _allocationPoints.toUint120(), active: true, cap: 0}); - - $.totalAllocationPoints += _allocationPoints; - IWithdrawalQueue($.withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); - - emit AddStrategy(_strategy, _allocationPoints); - } + function addStrategy(address _strategy, uint256 _allocationPoints) + external + use(MODULE_ALLOCATION_POINTS) + onlyRole(STRATEGY_ADDER) + {} /// @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 /// @param _strategy Address of the strategy - function removeStrategy(address _strategy) external nonReentrant onlyRole(STRATEGY_REMOVER) { - if (_strategy == address(0)) revert ErrorsLib.CanNotRemoveCashReserve(); - - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - Strategy storage strategyStorage = $.strategies[_strategy]; - - if (!strategyStorage.active) { - revert ErrorsLib.AlreadyRemoved(); - } - - _callHooksTarget(REMOVE_STRATEGY, _msgSender()); - - $.totalAllocationPoints -= strategyStorage.allocationPoints; - strategyStorage.active = false; - strategyStorage.allocationPoints = 0; - - // remove from withdrawalQueue - IWithdrawalQueue($.withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); - - emit RemoveStrategy(_strategy); - } + function removeStrategy(address _strategy) external use(MODULE_ALLOCATION_POINTS) onlyRole(STRATEGY_REMOVER) {} /// @notice update accrued interest function updateInterestAccrued() external { diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index 5e9a6ac5..00465d44 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -15,32 +15,35 @@ contract FourSixTwoSixAggFactory { address public immutable evc; address public immutable balanceTracker; /// core modules - address public immutable rewardsImpl; - address public immutable hooksImpl; + address public immutable rewardsModuleImpl; + address public immutable hooksModuleImpl; address public immutable feeModuleImpl; + address public immutable allocationpointsModuleImpl; /// peripheries /// @dev Rebalancer periphery, one instance can serve different aggregation vaults - address public immutable rebalancer; + address public immutable rebalancerAddr; /// @dev Withdrawal queue perihperhy, need to be deployed per aggregation vault - address public immutable withdrawalQueueImpl; + address public immutable withdrawalQueueAddr; constructor( address _evc, address _balanceTracker, - address _rewardsImpl, - address _hooksImpl, + address _rewardsModuleImpl, + address _hooksModuleImpl, address _feeModuleImpl, - address _rebalancer, - address _withdrawalQueueImpl + address _allocationpointsModuleImpl, + address _rebalancerAddr, + address _withdrawalQueueAddr ) { evc = _evc; balanceTracker = _balanceTracker; - rewardsImpl = _rewardsImpl; - hooksImpl = _hooksImpl; + rewardsModuleImpl = _rewardsModuleImpl; + hooksModuleImpl = _hooksModuleImpl; feeModuleImpl = _feeModuleImpl; + allocationpointsModuleImpl = _allocationpointsModuleImpl; - rebalancer = _rebalancer; - withdrawalQueueImpl = _withdrawalQueueImpl; + rebalancerAddr = _rebalancerAddr; + withdrawalQueueAddr = _withdrawalQueueAddr; } // TODO: decrease bytecode size, use clones or something @@ -51,21 +54,23 @@ contract FourSixTwoSixAggFactory { uint256 _initialCashAllocationPoints ) external returns (address) { // cloning core modules - address rewardsImplAddr = Clones.clone(rewardsImpl); - address hooks = Clones.clone(hooksImpl); - address feeModule = Clones.clone(feeModuleImpl); + address rewardsModuleAddr = Clones.clone(rewardsModuleImpl); + address hooksModuleAddr = Clones.clone(hooksModuleImpl); + address feeModuleAddr = Clones.clone(feeModuleImpl); + address allocationpointsModuleAddr = Clones.clone(allocationpointsModuleImpl); // cloning peripheries - WithdrawalQueue withdrawalQueue = WithdrawalQueue(Clones.clone(withdrawalQueueImpl)); + WithdrawalQueue withdrawalQueue = WithdrawalQueue(Clones.clone(withdrawalQueueAddr)); // deploy new aggregation vault - FourSixTwoSixAgg fourSixTwoSixAgg = new FourSixTwoSixAgg(rewardsImplAddr, hooks, feeModule); + FourSixTwoSixAgg fourSixTwoSixAgg = + new FourSixTwoSixAgg(rewardsModuleAddr, hooksModuleAddr, feeModuleAddr, allocationpointsModuleAddr); FourSixTwoSixAgg.InitParams memory aggregationVaultInitParams = FourSixTwoSixAgg.InitParams({ evc: evc, balanceTracker: balanceTracker, withdrawalQueuePeriphery: address(withdrawalQueue), - rebalancerPerihpery: rebalancer, + rebalancerPerihpery: rebalancerAddr, aggregationVaultOwner: msg.sender, asset: _asset, name: _name, diff --git a/src/Shared.sol b/src/Shared.sol new file mode 100644 index 00000000..d9e29d19 --- /dev/null +++ b/src/Shared.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {StorageLib, AggregationVaultStorage, HooksStorage} from "./lib/StorageLib.sol"; +import {ErrorsLib} from "./lib/ErrorsLib.sol"; +import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; +import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; +import {HooksLib} from "./lib/HooksLib.sol"; + +contract Shared { + using HooksLib for uint32; + + uint8 internal constant REENTRANCYLOCK__UNLOCKED = 1; + uint8 internal constant REENTRANCYLOCK__LOCKED = 2; + + uint32 public constant DEPOSIT = 1 << 0; + uint32 public constant WITHDRAW = 1 << 1; + uint32 public constant ADD_STRATEGY = 1 << 2; + uint32 public constant REMOVE_STRATEGY = 1 << 3; + + uint32 constant ACTIONS_COUNTER = 1 << 4; + uint256 constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; + + event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); + + modifier nonReentrant() { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if ($.locked == REENTRANCYLOCK__LOCKED) revert ErrorsLib.Reentrancy(); + + $.locked = REENTRANCYLOCK__LOCKED; + _; + $.locked = REENTRANCYLOCK__UNLOCKED; + } + + function _msgSender() internal view virtual returns (address) { + address sender = msg.sender; + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (sender == address($.evc)) { + (sender,) = IEVC($.evc).getCurrentOnBehalfOfAccount(address(0)); + } + + return sender; + } + + function _setHooksConfig(address _hooksTarget, uint32 _hookedFns) internal { + if (_hooksTarget != address(0) && IHookTarget(_hooksTarget).isHookTarget() != IHookTarget.isHookTarget.selector) + { + revert ErrorsLib.NotHooksContract(); + } + if (_hookedFns != 0 && _hooksTarget == address(0)) { + revert ErrorsLib.InvalidHooksTarget(); + } + if (_hookedFns >= ACTIONS_COUNTER) revert ErrorsLib.InvalidHookedFns(); + + HooksStorage storage $ = StorageLib._getHooksStorage(); + $.hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); + + emit SetHooksConfig(_hooksTarget, _hookedFns); + } + + /// @notice Checks whether a hook has been installed for the function and if so, invokes the hook target. + /// @param _fn Function to call the hook for. + /// @param _caller Caller's address. + function _callHooksTarget(uint32 _fn, address _caller) internal { + HooksStorage storage $ = StorageLib._getHooksStorage(); + + (address target, uint32 hookedFns) = _getHooksConfig($.hooksConfig); + + if (hookedFns.isNotSet(_fn)) return; + + (bool success, bytes memory data) = target.call(abi.encodePacked(msg.data, _caller)); + + if (!success) _revertBytes(data); + } + + /// @notice Get the hooks contract and the hooked functions. + /// @return address Hooks contract. + /// @return uint32 Hooked functions. + function _getHooksConfig(uint256 _hooksConfig) internal pure returns (address, uint32) { + return (address(uint160(_hooksConfig >> 32)), uint32(_hooksConfig & HOOKS_MASK)); + } + + /// @dev Revert with call error or EmptyError + /// @param _errorMsg call revert message + function _revertBytes(bytes memory _errorMsg) private pure { + if (_errorMsg.length > 0) { + assembly { + revert(add(32, _errorMsg), mload(_errorMsg)) + } + } + + revert ErrorsLib.EmptyError(); + } +} diff --git a/src/lib/ErrorsLib.sol b/src/lib/ErrorsLib.sol index 3b57e3b8..d7599b39 100644 --- a/src/lib/ErrorsLib.sol +++ b/src/lib/ErrorsLib.sol @@ -19,4 +19,8 @@ library ErrorsLib { error NotSupported(); error AlreadyEnabled(); error AlreadyDisabled(); + error InvalidHooksTarget(); + error NotHooksContract(); + error InvalidHookedFns(); + error EmptyError(); } diff --git a/src/modules/AllocationPoints.sol b/src/modules/AllocationPoints.sol new file mode 100644 index 00000000..6029741e --- /dev/null +++ b/src/modules/AllocationPoints.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Shared} from "../Shared.sol"; +// internal dep +import {StorageLib, AggregationVaultStorage, Strategy} from "../lib/StorageLib.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {IERC4626} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; +import {IWithdrawalQueue} from "../interface/IWithdrawalQueue.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +abstract contract AllocationPointsModule is Shared { + using SafeCast for uint256; + + event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); + event AddStrategy(address indexed strategy, uint256 allocationPoints); + event RemoveStrategy(address indexed _strategy); + event SetStrategyCap(address indexed strategy, uint256 cap); + + /// @notice Adjust a certain strategy's allocation points. + /// @dev Can only be called by an address that have the STRATEGY_MANAGER + /// @param _strategy address of strategy + /// @param _newPoints new strategy's points + function adjustAllocationPoints(address _strategy, uint256 _newPoints) external virtual nonReentrant { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + Strategy memory strategyDataCache = $.strategies[_strategy]; + + if (!strategyDataCache.active) { + revert ErrorsLib.InactiveStrategy(); + } + + $.strategies[_strategy].allocationPoints = _newPoints.toUint120(); + $.totalAllocationPoints = $.totalAllocationPoints + _newPoints - strategyDataCache.allocationPoints; + + emit AdjustAllocationPoints(_strategy, strategyDataCache.allocationPoints, _newPoints); + } + + /// @notice Set cap on strategy allocated amount. + /// @dev By default, cap is set to 0, not activated. + /// @param _strategy Strategy address. + /// @param _cap Cap amount + function setStrategyCap(address _strategy, uint256 _cap) external virtual nonReentrant { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (!$.strategies[_strategy].active) { + revert ErrorsLib.InactiveStrategy(); + } + + $.strategies[_strategy].cap = _cap.toUint120(); + + emit SetStrategyCap(_strategy, _cap); + } + + /// @notice Add new strategy with it's allocation points. + /// @dev Can only be called by an address that have STRATEGY_ADDER. + /// @param _strategy Address of the strategy + /// @param _allocationPoints Strategy's allocation points + function addStrategy(address _strategy, uint256 _allocationPoints) external virtual nonReentrant { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if ($.strategies[_strategy].active) { + revert ErrorsLib.StrategyAlreadyExist(); + } + + if (IERC4626(_strategy).asset() != IERC4626(address(this)).asset()) { + revert ErrorsLib.InvalidStrategyAsset(); + } + + _callHooksTarget(ADD_STRATEGY, _msgSender()); + + $.strategies[_strategy] = + Strategy({allocated: 0, allocationPoints: _allocationPoints.toUint120(), active: true, cap: 0}); + + $.totalAllocationPoints += _allocationPoints; + IWithdrawalQueue($.withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); + + emit AddStrategy(_strategy, _allocationPoints); + } + + /// @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 + /// @param _strategy Address of the strategy + function removeStrategy(address _strategy) external virtual nonReentrant { + if (_strategy == address(0)) revert ErrorsLib.CanNotRemoveCashReserve(); + + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + Strategy storage strategyStorage = $.strategies[_strategy]; + + if (!strategyStorage.active) { + revert ErrorsLib.AlreadyRemoved(); + } + + _callHooksTarget(REMOVE_STRATEGY, _msgSender()); + + $.totalAllocationPoints -= strategyStorage.allocationPoints; + strategyStorage.active = false; + strategyStorage.allocationPoints = 0; + + // remove from withdrawalQueue + IWithdrawalQueue($.withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); + + emit RemoveStrategy(_strategy); + } +} + +contract AllocationPoints is AllocationPointsModule {} diff --git a/src/modules/Fee.sol b/src/modules/Fee.sol new file mode 100644 index 00000000..6707efa5 --- /dev/null +++ b/src/modules/Fee.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Shared} from "../Shared.sol"; +import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "../lib/StorageLib.sol"; +import {IBalanceForwarder} from "../interface/IBalanceForwarder.sol"; +import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +abstract contract FeeModule is Shared { + using StorageLib for *; + + /// @dev The maximum performanceFee the vault can have is 50% + uint256 internal constant MAX_PERFORMANCE_FEE = 0.5e18; + + event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); + event SetPerformanceFee(uint256 oldFee, uint256 newFee); + + /// @notice Set performance fee recipient address + /// @notice @param _newFeeRecipient Recipient address + function setFeeRecipient(address _newFeeRecipient) external { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + address feeRecipientCached = $.feeRecipient; + + if (_newFeeRecipient == feeRecipientCached) revert ErrorsLib.FeeRecipientAlreadySet(); + + emit SetFeeRecipient(feeRecipientCached, _newFeeRecipient); + + $.feeRecipient = _newFeeRecipient; + } + + /// @notice Set performance fee (1e18 == 100%) + /// @notice @param _newFee Fee rate + function setPerformanceFee(uint256 _newFee) external { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + uint256 performanceFeeCached = $.performanceFee; + + if (_newFee > MAX_PERFORMANCE_FEE) revert ErrorsLib.MaxPerformanceFeeExceeded(); + if ($.feeRecipient == address(0)) revert ErrorsLib.FeeRecipientNotSet(); + if (_newFee == performanceFeeCached) revert ErrorsLib.PerformanceFeeAlreadySet(); + + emit SetPerformanceFee(performanceFeeCached, _newFee); + + $.performanceFee = _newFee; + } +} + +contract Fee is FeeModule {} diff --git a/src/modules/Hooks.sol b/src/modules/Hooks.sol new file mode 100644 index 00000000..8fee680b --- /dev/null +++ b/src/modules/Hooks.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Shared} from "../Shared.sol"; +import {StorageLib, HooksStorage} from "../lib/StorageLib.sol"; +import {HooksLib} from "../lib/HooksLib.sol"; +import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; + +abstract contract HooksModule is Shared { + using HooksLib for uint32; + + /// @notice Set hooks contract and hooked functions. + /// @param _hooksTarget Hooks contract. + /// @param _hookedFns Hooked functions. + function setHooksConfig(address _hooksTarget, uint32 _hookedFns) external virtual nonReentrant { + _setHooksConfig(_hooksTarget, _hookedFns); + } + + /// @notice Get the hooks contract and the hooked functions. + /// @return address Hooks contract. + /// @return uint32 Hooked functions. + function getHooksConfig() external view returns (address, uint32) { + HooksStorage storage $ = StorageLib._getHooksStorage(); + + return _getHooksConfig($.hooksConfig); + } +} + +contract Hooks is HooksModule {} diff --git a/src/modules/Rewards.sol b/src/modules/Rewards.sol new file mode 100644 index 00000000..5afb4a72 --- /dev/null +++ b/src/modules/Rewards.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {Shared} from "../Shared.sol"; +import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "../lib/StorageLib.sol"; +import {IBalanceForwarder} from "../interface/IBalanceForwarder.sol"; +import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/// @title BalanceForwarder contract +/// @custom:security-contact security@euler.xyz +/// @author Euler Labs (https://www.eulerlabs.com/) +/// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams +abstract contract RewardsModule is IBalanceForwarder, Shared { + using StorageLib for *; + + event OptInStrategyRewards(address indexed strategy); + event OptOutStrategyRewards(address indexed strategy); + event EnableBalanceForwarder(address indexed _user); + event DisableBalanceForwarder(address indexed _user); + + /// @notice Opt in to strategy rewards + /// @param _strategy Strategy address + function optInStrategyRewards(address _strategy) external virtual nonReentrant { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (!$.strategies[_strategy].active) revert ErrorsLib.InactiveStrategy(); + + IBalanceForwarder(_strategy).enableBalanceForwarder(); + + emit OptInStrategyRewards(_strategy); + } + + /// @notice Opt out of strategy rewards + /// @param _strategy Strategy address + function optOutStrategyRewards(address _strategy) external virtual nonReentrant { + IBalanceForwarder(_strategy).disableBalanceForwarder(); + + emit OptOutStrategyRewards(_strategy); + } + + /// @notice Claim a specific strategy rewards + /// @param _strategy Strategy address. + /// @param _reward The address of the reward token. + /// @param _recipient The address to receive the claimed reward tokens. + /// @param _forfeitRecentReward Whether to forfeit the recent rewards and not update the accumulator. + function claimStrategyReward(address _strategy, address _reward, address _recipient, bool _forfeitRecentReward) + external + virtual + nonReentrant + { + address rewardStreams = IBalanceForwarder(_strategy).balanceTrackerAddress(); + + IRewardStreams(rewardStreams).claimReward(_strategy, _reward, _recipient, _forfeitRecentReward); + } + + /// @notice Enables balance forwarding for the authenticated account + /// @dev Only the authenticated account can enable balance forwarding for itself + /// @dev Should call the IBalanceTracker hook with the current account's balance + function enableBalanceForwarder() external virtual nonReentrant { + address user = _msgSender(); + uint256 userBalance = IERC20(address(this)).balanceOf(user); + + _enableBalanceForwarder(user, userBalance); + } + + /// @notice Disables balance forwarding for the authenticated account + /// @dev Only the authenticated account can disable balance forwarding for itself + /// @dev Should call the IBalanceTracker hook with the account's balance of 0 + function disableBalanceForwarder() external virtual nonReentrant { + _disableBalanceForwarder(_msgSender()); + } + + /// @notice Retrieve the address of rewards contract, tracking changes in account's balances + /// @return The balance tracker address + function balanceTrackerAddress() external view returns (address) { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + + return address($.balanceTracker); + } + + /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract + /// @param _account Address to query + /// @return True if balance forwarder is enabled + function balanceForwarderEnabled(address _account) external view returns (bool) { + return _balanceForwarderEnabled(_account); + } + + function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); + + if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); + if ($.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyEnabled(); + + $.isBalanceForwarderEnabled[_sender] = true; + balanceTrackerCached.balanceTrackerHook(_sender, _senderBalance, false); + + emit EnableBalanceForwarder(_sender); + } + + /// @notice Disables balance forwarding for the authenticated account + /// @dev Only the authenticated account can disable balance forwarding for itself + /// @dev Should call the IBalanceTracker hook with the account's balance of 0 + function _disableBalanceForwarder(address _sender) internal { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); + + if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); + if (!$.isBalanceForwarderEnabled[_sender]) revert ErrorsLib.AlreadyDisabled(); + + $.isBalanceForwarderEnabled[_sender] = false; + balanceTrackerCached.balanceTrackerHook(_sender, 0, false); + + emit DisableBalanceForwarder(_sender); + } + + function _setBalanceTracker(address _balancerTracker) internal { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + + $.balanceTracker = _balancerTracker; + } + + /// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract + /// @param _account Address to query + /// @return True if balance forwarder is enabled + function _balanceForwarderEnabled(address _account) internal view returns (bool) { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + + return $.isBalanceForwarderEnabled[_account]; + } + + /// @notice Retrieve the address of rewards contract, tracking changes in account's balances + /// @return The balance tracker address + function _balanceTrackerAddress() internal view returns (address) { + BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + + return address($.balanceTracker); + } +} + +contract Rewards is RewardsModule {} diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 6de1dd67..0268dcd7 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -12,6 +12,7 @@ import {FourSixTwoSixAggFactory} from "../../src/FourSixTwoSixAggFactory.sol"; import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; import {ErrorsLib} from "../../src/lib/ErrorsLib.sol"; +import {AllocationPoints} from "../../src/modules/AllocationPoints.sol"; contract FourSixTwoSixAggBase is EVaultTestBase { uint256 public constant CASH_RESERVE_ALLOCATION_POINTS = 1000e18; @@ -25,6 +26,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { Rewards rewardsImpl; Hooks hooksImpl; Fee feeModuleImpl; + AllocationPoints allocationPointsModuleImpl; // peripheries Rebalancer rebalancer; WithdrawalQueue withdrawalQueueImpl; @@ -45,6 +47,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { rewardsImpl = new Rewards(); hooksImpl = new Hooks(); feeModuleImpl = new Fee(); + allocationPointsModuleImpl = new AllocationPoints(); rebalancer = new Rebalancer(); withdrawalQueueImpl = new WithdrawalQueue(); @@ -54,6 +57,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address(rewardsImpl), address(hooksImpl), address(feeModuleImpl), + address(allocationPointsModuleImpl), address(rebalancer), address(withdrawalQueueImpl) ); diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index b02fca97..9e45b94b 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -31,6 +31,7 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { address(rewardsImpl), address(hooksImpl), address(feeModuleImpl), + address(allocationPointsModuleImpl), address(rebalancer), address(withdrawalQueueImpl) ); diff --git a/test/e2e/HooksE2ETest.t.sol b/test/e2e/HooksE2ETest.t.sol index a0e966c0..a7570b48 100644 --- a/test/e2e/HooksE2ETest.t.sol +++ b/test/e2e/HooksE2ETest.t.sol @@ -10,7 +10,7 @@ import { IRMTestDefault, TestERC20, IHookTarget, - HooksModule + ErrorsLib } from "../common/FourSixTwoSixAggBase.t.sol"; contract HooksE2ETest is FourSixTwoSixAggBase { @@ -45,7 +45,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); vm.startPrank(manager); - vm.expectRevert(HooksModule.InvalidHooksTarget.selector); + vm.expectRevert(ErrorsLib.InvalidHooksTarget.selector); fourSixTwoSixAgg.setHooksConfig(address(0), expectedHookedFns); vm.stopPrank(); } @@ -56,7 +56,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { vm.startPrank(manager); address hooksContract = address(new NotHooksContract()); - vm.expectRevert(HooksModule.NotHooksContract.selector); + vm.expectRevert(ErrorsLib.NotHooksContract.selector); fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } @@ -65,7 +65,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { uint32 expectedHookedFns = 1 << 5; vm.startPrank(manager); address hooksContract = address(new HooksContract()); - vm.expectRevert(HooksModule.InvalidHookedFns.selector); + vm.expectRevert(ErrorsLib.InvalidHookedFns.selector); fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } From c6f29f26358e4c766b63467a122047022c1a4c8f Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:30:24 +0300 Subject: [PATCH 14/19] clean --- foundry.toml | 3 + src/Dispatch.sol | 2 - src/FourSixTwoSixAgg.sol | 205 ++++++++++++++----------------- src/Shared.sol | 10 +- src/lib/EventsLib.sol | 29 +++++ src/lib/StorageLib.sol | 29 +---- src/modules/AllocationPoints.sol | 24 ++-- src/modules/Fee.sol | 19 ++- src/modules/Hooks.sol | 12 +- src/modules/Rewards.sol | 38 +++--- 10 files changed, 183 insertions(+), 188 deletions(-) create mode 100644 src/lib/EventsLib.sol diff --git a/foundry.toml b/foundry.toml index 518361a2..c1df1e59 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,6 +17,9 @@ int_types = "long" quote_style = "double" number_underscore = "preserve" override_spacing = true +ignore = [ + "src/lib/StorageLib.sol" +] [profile.test] no_match_test = "Fuzz" diff --git a/src/Dispatch.sol b/src/Dispatch.sol index 16a75f16..f485d408 100644 --- a/src/Dispatch.sol +++ b/src/Dispatch.sol @@ -7,8 +7,6 @@ import {HooksModule} from "./modules/Hooks.sol"; import {RewardsModule} from "./modules/Rewards.sol"; abstract contract Dispatch is RewardsModule, HooksModule { - error E_Unauthorized(); - address public immutable MODULE_REWARDS; address public immutable MODULE_HOOKS; address public immutable MODULE_FEE; diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 3d518e81..a6fc4c2c 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -1,27 +1,29 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {Shared} from "./Shared.sol"; -// external dep -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +// interfaces +import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; +import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; +import {Dispatch} from "./Dispatch.sol"; +// contracts import { ERC4626Upgradeable, - IERC4626, - ERC20Upgradeable, - Math + ERC20Upgradeable } from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import {Shared} from "./Shared.sol"; +import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; +// libs +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -// internal dep +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {StorageLib, AggregationVaultStorage, Strategy} from "./lib/StorageLib.sol"; import {ErrorsLib} from "./lib/ErrorsLib.sol"; -import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; -import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; -import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; -import {Dispatch} from "./Dispatch.sol"; -import {ContextUpgradeable} from "@openzeppelin-upgradeable/utils/ContextUpgradeable.sol"; +import {EventsLib} from "./lib/EventsLib.sol"; /// @dev Do NOT use with fee on transfer tokens /// @dev Do NOT use with rebasing tokens @@ -45,15 +47,6 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea uint256 public constant INTEREST_SMEAR = 2 weeks; - event Gulp(uint256 interestLeft, uint256 interestSmearEnd); - event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); - event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); - event AddStrategy(address indexed strategy, uint256 allocationPoints); - event RemoveStrategy(address indexed _strategy); - event AccruePerformanceFee(address indexed feeRecipient, uint256 yield, uint256 feeAssets); - event SetStrategyCap(address indexed strategy, uint256 cap); - event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); - constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) Dispatch(_rewardsModule, _hooksModule, _feeModule, _allocationPointsModule) {} @@ -81,11 +74,10 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea __ERC4626_init_unchained(IERC20(_initParams.asset)); __ERC20_init_unchained(_initParams.name, _initParams.symbol); - _lock(); - if (_initParams.initialCashAllocationPoints == 0) revert ErrorsLib.InitialAllocationPointsZero(); AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + $.locked = REENTRANCYLOCK__UNLOCKED; $.withdrawalQueue = _initParams.withdrawalQueuePeriphery; $.strategies[address(0)] = Strategy({ allocated: 0, @@ -147,6 +139,53 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @dev Should call the IBalanceTracker hook with the account's balance of 0 function disableBalanceForwarder() external override use(MODULE_REWARDS) {} + /// @notice Adjust a certain strategy's allocation points. + /// @dev Can only be called by an address that have the STRATEGY_MANAGER + /// @param _strategy address of strategy + /// @param _newPoints new strategy's points + function adjustAllocationPoints(address _strategy, uint256 _newPoints) + external + use(MODULE_ALLOCATION_POINTS) + onlyRole(STRATEGY_MANAGER) + {} + + /// @notice Set cap on strategy allocated amount. + /// @dev By default, cap is set to 0, not activated. + /// @param _strategy Strategy address. + /// @param _cap Cap amount + function setStrategyCap(address _strategy, uint256 _cap) + external + use(MODULE_ALLOCATION_POINTS) + onlyRole(STRATEGY_MANAGER) + {} + + /// @notice Add new strategy with it's allocation points. + /// @dev Can only be called by an address that have STRATEGY_ADDER. + /// @param _strategy Address of the strategy + /// @param _allocationPoints Strategy's allocation points + function addStrategy(address _strategy, uint256 _allocationPoints) + external + use(MODULE_ALLOCATION_POINTS) + onlyRole(STRATEGY_ADDER) + {} + + /// @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 + /// @param _strategy Address of the strategy + function removeStrategy(address _strategy) external use(MODULE_ALLOCATION_POINTS) onlyRole(STRATEGY_REMOVER) {} + + /// @notice Set hooks contract and hooked functions. + /// @dev This funtion should be overriden to implement access control. + /// @param _hooksTarget Hooks contract. + /// @param _hookedFns Hooked functions. + function setHooksConfig(address _hooksTarget, uint32 _hookedFns) + external + override + onlyRole(MANAGER) + use(MODULE_HOOKS) + {} + /// @notice Harvest strategy. /// @param strategy address of strategy //TODO: is this safe without the reentrancy check @@ -186,45 +225,9 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea $.totalAllocated -= _amountToRebalance; } - emit Rebalance(_strategy, _amountToRebalance, _isDeposit); + emit EventsLib.Rebalance(_strategy, _amountToRebalance, _isDeposit); } - /// @notice Adjust a certain strategy's allocation points. - /// @dev Can only be called by an address that have the STRATEGY_MANAGER - /// @param _strategy address of strategy - /// @param _newPoints new strategy's points - function adjustAllocationPoints(address _strategy, uint256 _newPoints) - external - use(MODULE_ALLOCATION_POINTS) - onlyRole(STRATEGY_MANAGER) - {} - - /// @notice Set cap on strategy allocated amount. - /// @dev By default, cap is set to 0, not activated. - /// @param _strategy Strategy address. - /// @param _cap Cap amount - function setStrategyCap(address _strategy, uint256 _cap) - external - use(MODULE_ALLOCATION_POINTS) - onlyRole(STRATEGY_MANAGER) - {} - - /// @notice Add new strategy with it's allocation points. - /// @dev Can only be called by an address that have STRATEGY_ADDER. - /// @param _strategy Address of the strategy - /// @param _allocationPoints Strategy's allocation points - function addStrategy(address _strategy, uint256 _allocationPoints) - external - use(MODULE_ALLOCATION_POINTS) - onlyRole(STRATEGY_ADDER) - {} - - /// @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 - /// @param _strategy Address of the strategy - function removeStrategy(address _strategy) external use(MODULE_ALLOCATION_POINTS) onlyRole(STRATEGY_REMOVER) {} - /// @notice update accrued interest function updateInterestAccrued() external { return _updateInterestAccrued(); @@ -235,6 +238,25 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea _gulp(); } + // TODO: add access control + function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + // Update allocated assets + $.strategies[_strategy].allocated -= uint120(_withdrawAmount); + $.totalAllocated -= _withdrawAmount; + + // Do actual withdraw from strategy + IERC4626(_strategy).withdraw(_withdrawAmount, address(this), address(this)); + } + + // TODO: add access control + function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) + external + { + _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + } + /// @notice Get strategy params. /// @param _strategy strategy's address /// @return Strategy struct @@ -316,30 +338,6 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea return super.redeem(shares, receiver, owner); } - /// @notice Set hooks contract and hooked functions. - /// @dev This funtion should be overriden to implement access control. - /// @param _hooksTarget Hooks contract. - /// @param _hookedFns Hooked functions. - function setHooksConfig(address _hooksTarget, uint32 _hookedFns) - external - override - onlyRole(MANAGER) - use(MODULE_HOOKS) - {} - - /// @notice update accrued interest. - function _updateInterestAccrued() internal { - uint256 accruedInterest = _interestAccruedFromCache(); - - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - // it's safe to down-cast because the accrued interest is a fraction of interest left - $.interestLeft -= uint168(accruedInterest); - $.lastInterestUpdate = uint40(block.timestamp); - - // Move interest accrued to totalAssetsDeposited - $.totalAssetsDeposited += accruedInterest; - } - /// @notice Return the total amount of assets deposited, plus the accrued interest. /// @return uint256 total amount function totalAssets() public view override returns (uint256) { @@ -393,26 +391,19 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea } } - // TODO: add access control - function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - // Update allocated assets - $.strategies[_strategy].allocated -= uint120(_withdrawAmount); - $.totalAllocated -= _withdrawAmount; + /// @notice update accrued interest. + function _updateInterestAccrued() internal { + uint256 accruedInterest = _interestAccruedFromCache(); - // Do actual withdraw from strategy - IERC4626(_strategy).withdraw(_withdrawAmount, address(this), address(this)); - } + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + // it's safe to down-cast because the accrued interest is a fraction of interest left + $.interestLeft -= uint168(accruedInterest); + $.lastInterestUpdate = uint40(block.timestamp); - // TODO: add access control - function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) - external - { - _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + // Move interest accrued to totalAssetsDeposited + $.totalAssetsDeposited += accruedInterest; } - // TODO: add access control function _executeWithdrawFromReserve( address caller, address receiver, @@ -442,7 +433,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea $.interestSmearEnd = uint40(block.timestamp + INTEREST_SMEAR); $.interestLeft += uint168(toGulp); // toGulp <= maxGulp <= max uint168 - emit Gulp($.interestLeft, $.interestSmearEnd); + emit EventsLib.Gulp($.interestLeft, $.interestSmearEnd); } function _harvest(address _strategy) internal { @@ -482,7 +473,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea } } - emit Harvest(_strategy, underlyingBalance, strategyAllocatedAmount); + emit EventsLib.Harvest(_strategy, underlyingBalance, strategyAllocatedAmount); } function _accruePerformanceFee(address _strategy, uint256 _yield) internal returns (uint120) { @@ -500,7 +491,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea IERC4626(_strategy).withdraw(feeAssets, cachedFeeRecipient, address(this)); } - emit AccruePerformanceFee(cachedFeeRecipient, _yield, feeAssets); + emit EventsLib.AccruePerformanceFee(cachedFeeRecipient, _yield, feeAssets); return feeAssets.toUint120(); } @@ -547,12 +538,6 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea return $.interestLeft * timePassed / totalDuration; } - function _lock() private onlyInitializing { - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - - $.locked = REENTRANCYLOCK__UNLOCKED; - } - function _msgSender() internal view override (ContextUpgradeable, Shared) returns (address) { return Shared._msgSender(); } diff --git a/src/Shared.sol b/src/Shared.sol index d9e29d19..8d8d9f2b 100644 --- a/src/Shared.sol +++ b/src/Shared.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {StorageLib, AggregationVaultStorage, HooksStorage} from "./lib/StorageLib.sol"; +import {StorageLib, AggregationVaultStorage} from "./lib/StorageLib.sol"; import {ErrorsLib} from "./lib/ErrorsLib.sol"; import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; @@ -21,8 +21,6 @@ contract Shared { uint32 constant ACTIONS_COUNTER = 1 << 4; uint256 constant HOOKS_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF; - event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); - modifier nonReentrant() { AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); @@ -54,17 +52,15 @@ contract Shared { } if (_hookedFns >= ACTIONS_COUNTER) revert ErrorsLib.InvalidHookedFns(); - HooksStorage storage $ = StorageLib._getHooksStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.hooksConfig = (uint256(uint160(_hooksTarget)) << 32) | uint256(_hookedFns); - - emit SetHooksConfig(_hooksTarget, _hookedFns); } /// @notice Checks whether a hook has been installed for the function and if so, invokes the hook target. /// @param _fn Function to call the hook for. /// @param _caller Caller's address. function _callHooksTarget(uint32 _fn, address _caller) internal { - HooksStorage storage $ = StorageLib._getHooksStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); (address target, uint32 hookedFns) = _getHooksConfig($.hooksConfig); diff --git a/src/lib/EventsLib.sol b/src/lib/EventsLib.sol new file mode 100644 index 00000000..d8c9e8a2 --- /dev/null +++ b/src/lib/EventsLib.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +library EventsLib { + /// @dev FourSixTwoSixAgg events + event Gulp(uint256 interestLeft, uint256 interestSmearEnd); + event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); + event AccruePerformanceFee(address indexed feeRecipient, uint256 yield, uint256 feeAssets); + event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); + + /// @dev Allocationpoints events + event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); + event AddStrategy(address indexed strategy, uint256 allocationPoints); + event RemoveStrategy(address indexed _strategy); + event SetStrategyCap(address indexed strategy, uint256 cap); + + /// @dev Fee events + event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); + event SetPerformanceFee(uint256 oldFee, uint256 newFee); + + /// @dev Hooks events + event SetHooksConfig(address indexed hooksTarget, uint32 hookedFns); + + /// @dev Rewards events + event OptInStrategyRewards(address indexed strategy); + event OptOutStrategyRewards(address indexed strategy); + event EnableBalanceForwarder(address indexed _user); + event DisableBalanceForwarder(address indexed _user); +} diff --git a/src/lib/StorageLib.sol b/src/lib/StorageLib.sol index e3f00b68..33435110 100644 --- a/src/lib/StorageLib.sol +++ b/src/lib/StorageLib.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; /// @custom:storage-location erc7201:euler_aggregation_vault.storage.AggregationVault struct AggregationVaultStorage { + /// @dev EVC address address evc; /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract uint256 totalAssetsDeposited; @@ -18,6 +19,8 @@ struct AggregationVaultStorage { address withdrawalQueue; /// @dev Mapping between strategy address and it's allocation config mapping(address => Strategy) strategies; + + /// lastInterestUpdate: last timestamo where interest was updated. uint40 lastInterestUpdate; /// interestSmearEnd: timestamp when the smearing of interest end. @@ -26,19 +29,16 @@ struct AggregationVaultStorage { uint168 interestLeft; /// locked: if locked or not for update. uint8 locked; -} -/// @custom:storage-location erc7201:euler_aggregation_vault.storage.BalanceForwarder -struct BalanceForwarderStorage { + address balanceTracker; mapping(address => bool) isBalanceForwarderEnabled; -} -/// @custom:storage-location erc7201:euler_aggregation_vault.storage.Hooks -struct HooksStorage { + /// @dev storing the hooks target and kooked functions. uint256 hooksConfig; } + /// @dev A struct that hold a strategy allocation's config /// allocated: amount of asset deposited into strategy /// allocationPoints: number of points allocated to this strategy @@ -56,27 +56,10 @@ library StorageLib { // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.AggregationVault")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant AggregationVaultStorageLocation = 0x7da5ece5aff94f3377324d715703a012d3253da37511270103c646f171c0aa00; - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.BalanceForwarder")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant BalanceForwarderStorageLocation = - 0xf0af7cc3986d6cac468ac54c07f5f08359b3a00d65f8e2a86f2676ec79073f00; - // keccak256(abi.encode(uint256(keccak256("euler_aggregation_vault.storage.Hooks")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant HooksStorageLocation = 0x7daefa3ee1567d8892b825e51ce683a058a5785193bcc2ca50940db02ccbf700; function _getAggregationVaultStorage() internal pure returns (AggregationVaultStorage storage $) { assembly { $.slot := AggregationVaultStorageLocation } } - - function _getBalanceForwarderStorage() internal pure returns (BalanceForwarderStorage storage $) { - assembly { - $.slot := BalanceForwarderStorageLocation - } - } - - function _getHooksStorage() internal pure returns (HooksStorage storage $) { - assembly { - $.slot := HooksStorageLocation - } - } } diff --git a/src/modules/AllocationPoints.sol b/src/modules/AllocationPoints.sol index 6029741e..e4a27947 100644 --- a/src/modules/AllocationPoints.sol +++ b/src/modules/AllocationPoints.sol @@ -1,22 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {Shared} from "../Shared.sol"; -// internal dep -import {StorageLib, AggregationVaultStorage, Strategy} from "../lib/StorageLib.sol"; -import {ErrorsLib} from "../lib/ErrorsLib.sol"; +// interfaces import {IERC4626} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import {IWithdrawalQueue} from "../interface/IWithdrawalQueue.sol"; +// contracts +import {Shared} from "../Shared.sol"; +// libs import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {StorageLib, AggregationVaultStorage, Strategy} from "../lib/StorageLib.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {EventsLib} from "../lib/EventsLib.sol"; abstract contract AllocationPointsModule is Shared { using SafeCast for uint256; - event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); - event AddStrategy(address indexed strategy, uint256 allocationPoints); - event RemoveStrategy(address indexed _strategy); - event SetStrategyCap(address indexed strategy, uint256 cap); - /// @notice Adjust a certain strategy's allocation points. /// @dev Can only be called by an address that have the STRATEGY_MANAGER /// @param _strategy address of strategy @@ -33,7 +31,7 @@ abstract contract AllocationPointsModule is Shared { $.strategies[_strategy].allocationPoints = _newPoints.toUint120(); $.totalAllocationPoints = $.totalAllocationPoints + _newPoints - strategyDataCache.allocationPoints; - emit AdjustAllocationPoints(_strategy, strategyDataCache.allocationPoints, _newPoints); + emit EventsLib.AdjustAllocationPoints(_strategy, strategyDataCache.allocationPoints, _newPoints); } /// @notice Set cap on strategy allocated amount. @@ -49,7 +47,7 @@ abstract contract AllocationPointsModule is Shared { $.strategies[_strategy].cap = _cap.toUint120(); - emit SetStrategyCap(_strategy, _cap); + emit EventsLib.SetStrategyCap(_strategy, _cap); } /// @notice Add new strategy with it's allocation points. @@ -75,7 +73,7 @@ abstract contract AllocationPointsModule is Shared { $.totalAllocationPoints += _allocationPoints; IWithdrawalQueue($.withdrawalQueue).addStrategyToWithdrawalQueue(_strategy); - emit AddStrategy(_strategy, _allocationPoints); + emit EventsLib.AddStrategy(_strategy, _allocationPoints); } /// @notice Remove strategy and set its allocation points to zero. @@ -102,7 +100,7 @@ abstract contract AllocationPointsModule is Shared { // remove from withdrawalQueue IWithdrawalQueue($.withdrawalQueue).removeStrategyFromWithdrawalQueue(_strategy); - emit RemoveStrategy(_strategy); + emit EventsLib.RemoveStrategy(_strategy); } } diff --git a/src/modules/Fee.sol b/src/modules/Fee.sol index 6707efa5..c1436cdf 100644 --- a/src/modules/Fee.sol +++ b/src/modules/Fee.sol @@ -1,23 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {Shared} from "../Shared.sol"; -import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "../lib/StorageLib.sol"; +// interfaces import {IBalanceForwarder} from "../interface/IBalanceForwarder.sol"; import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; -import {ErrorsLib} from "../lib/ErrorsLib.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +// contracts +import {Shared} from "../Shared.sol"; +// libs +import {StorageLib, AggregationVaultStorage} from "../lib/StorageLib.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {EventsLib} from "../lib/EventsLib.sol"; abstract contract FeeModule is Shared { - using StorageLib for *; - /// @dev The maximum performanceFee the vault can have is 50% uint256 internal constant MAX_PERFORMANCE_FEE = 0.5e18; - event SetFeeRecipient(address indexed oldRecipient, address indexed newRecipient); - event SetPerformanceFee(uint256 oldFee, uint256 newFee); - /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address function setFeeRecipient(address _newFeeRecipient) external { @@ -26,7 +25,7 @@ abstract contract FeeModule is Shared { if (_newFeeRecipient == feeRecipientCached) revert ErrorsLib.FeeRecipientAlreadySet(); - emit SetFeeRecipient(feeRecipientCached, _newFeeRecipient); + emit EventsLib.SetFeeRecipient(feeRecipientCached, _newFeeRecipient); $.feeRecipient = _newFeeRecipient; } @@ -42,7 +41,7 @@ abstract contract FeeModule is Shared { if ($.feeRecipient == address(0)) revert ErrorsLib.FeeRecipientNotSet(); if (_newFee == performanceFeeCached) revert ErrorsLib.PerformanceFeeAlreadySet(); - emit SetPerformanceFee(performanceFeeCached, _newFee); + emit EventsLib.SetPerformanceFee(performanceFeeCached, _newFee); $.performanceFee = _newFee; } diff --git a/src/modules/Hooks.sol b/src/modules/Hooks.sol index 8fee680b..3c799d2d 100644 --- a/src/modules/Hooks.sol +++ b/src/modules/Hooks.sol @@ -1,10 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +// interfaces +import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; +// contracts import {Shared} from "../Shared.sol"; -import {StorageLib, HooksStorage} from "../lib/StorageLib.sol"; +// libs +import {StorageLib, AggregationVaultStorage} from "../lib/StorageLib.sol"; import {HooksLib} from "../lib/HooksLib.sol"; -import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; +import {EventsLib} from "../lib/EventsLib.sol"; abstract contract HooksModule is Shared { using HooksLib for uint32; @@ -14,13 +18,15 @@ abstract contract HooksModule is Shared { /// @param _hookedFns Hooked functions. function setHooksConfig(address _hooksTarget, uint32 _hookedFns) external virtual nonReentrant { _setHooksConfig(_hooksTarget, _hookedFns); + + emit EventsLib.SetHooksConfig(_hooksTarget, _hookedFns); } /// @notice Get the hooks contract and the hooked functions. /// @return address Hooks contract. /// @return uint32 Hooked functions. function getHooksConfig() external view returns (address, uint32) { - HooksStorage storage $ = StorageLib._getHooksStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return _getHooksConfig($.hooksConfig); } diff --git a/src/modules/Rewards.sol b/src/modules/Rewards.sol index 5afb4a72..663a680f 100644 --- a/src/modules/Rewards.sol +++ b/src/modules/Rewards.sol @@ -1,26 +1,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {Shared} from "../Shared.sol"; -import {StorageLib, BalanceForwarderStorage, AggregationVaultStorage} from "../lib/StorageLib.sol"; +// interfaces import {IBalanceForwarder} from "../interface/IBalanceForwarder.sol"; import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; -import {ErrorsLib} from "../lib/ErrorsLib.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +// contracts +import {Shared} from "../Shared.sol"; +// libs +import {StorageLib, AggregationVaultStorage} from "../lib/StorageLib.sol"; +import {ErrorsLib} from "../lib/ErrorsLib.sol"; +import {EventsLib} from "../lib/EventsLib.sol"; /// @title BalanceForwarder contract /// @custom:security-contact security@euler.xyz /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams abstract contract RewardsModule is IBalanceForwarder, Shared { - using StorageLib for *; - - event OptInStrategyRewards(address indexed strategy); - event OptOutStrategyRewards(address indexed strategy); - event EnableBalanceForwarder(address indexed _user); - event DisableBalanceForwarder(address indexed _user); - + /// @notice Opt in to strategy rewards /// @param _strategy Strategy address function optInStrategyRewards(address _strategy) external virtual nonReentrant { @@ -30,7 +28,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { IBalanceForwarder(_strategy).enableBalanceForwarder(); - emit OptInStrategyRewards(_strategy); + emit EventsLib.OptInStrategyRewards(_strategy); } /// @notice Opt out of strategy rewards @@ -38,7 +36,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { function optOutStrategyRewards(address _strategy) external virtual nonReentrant { IBalanceForwarder(_strategy).disableBalanceForwarder(); - emit OptOutStrategyRewards(_strategy); + emit EventsLib.OptOutStrategyRewards(_strategy); } /// @notice Claim a specific strategy rewards @@ -76,7 +74,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address function balanceTrackerAddress() external view returns (address) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return address($.balanceTracker); } @@ -89,7 +87,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { } function _enableBalanceForwarder(address _sender, uint256 _senderBalance) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); @@ -98,14 +96,14 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { $.isBalanceForwarderEnabled[_sender] = true; balanceTrackerCached.balanceTrackerHook(_sender, _senderBalance, false); - emit EnableBalanceForwarder(_sender); + emit EventsLib.EnableBalanceForwarder(_sender); } /// @notice Disables balance forwarding for the authenticated account /// @dev Only the authenticated account can disable balance forwarding for itself /// @dev Should call the IBalanceTracker hook with the account's balance of 0 function _disableBalanceForwarder(address _sender) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); IBalanceTracker balanceTrackerCached = IBalanceTracker($.balanceTracker); if (address(balanceTrackerCached) == address(0)) revert ErrorsLib.NotSupported(); @@ -114,11 +112,11 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { $.isBalanceForwarderEnabled[_sender] = false; balanceTrackerCached.balanceTrackerHook(_sender, 0, false); - emit DisableBalanceForwarder(_sender); + emit EventsLib.DisableBalanceForwarder(_sender); } function _setBalanceTracker(address _balancerTracker) internal { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.balanceTracker = _balancerTracker; } @@ -127,7 +125,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { /// @param _account Address to query /// @return True if balance forwarder is enabled function _balanceForwarderEnabled(address _account) internal view returns (bool) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return $.isBalanceForwarderEnabled[_account]; } @@ -135,7 +133,7 @@ abstract contract RewardsModule is IBalanceForwarder, Shared { /// @notice Retrieve the address of rewards contract, tracking changes in account's balances /// @return The balance tracker address function _balanceTrackerAddress() internal view returns (address) { - BalanceForwarderStorage storage $ = StorageLib._getBalanceForwarderStorage(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); return address($.balanceTracker); } From 6e1a8fd167fa131b300583450d78ca195bf30983 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:36:55 +0300 Subject: [PATCH 15/19] chore: rename roleS --- src/FourSixTwoSixAgg.sol | 56 ++++++++++++++------------ src/modules/AllocationPoints.sol | 2 +- src/modules/Rewards.sol | 1 - test/common/FourSixTwoSixAggBase.t.sol | 25 +++++++----- test/e2e/BalanceForwarderE2ETest.t.sol | 8 ++-- 5 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index a6fc4c2c..9ae27333 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -34,23 +34,20 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea using SafeCast for uint256; // Roles - bytes32 public constant STRATEGY_MANAGER = keccak256("STRATEGY_MANAGER"); - bytes32 public constant STRATEGY_MANAGER_ADMIN = keccak256("STRATEGY_MANAGER_ADMIN"); + bytes32 public constant ALLOCATIONS_MANAGER = keccak256("ALLOCATIONS_MANAGER"); + bytes32 public constant ALLOCATIONS_MANAGER_ADMIN = keccak256("ALLOCATIONS_MANAGER_ADMIN"); bytes32 public constant STRATEGY_ADDER = keccak256("STRATEGY_ADDER"); bytes32 public constant STRATEGY_ADDER_ADMIN = keccak256("STRATEGY_ADDER_ADMIN"); bytes32 public constant STRATEGY_REMOVER = keccak256("STRATEGY_REMOVER"); bytes32 public constant STRATEGY_REMOVER_ADMIN = keccak256("STRATEGY_REMOVER_ADMIN"); - bytes32 public constant MANAGER = keccak256("MANAGER"); - bytes32 public constant MANAGER_ADMIN = keccak256("MANAGER_ADMIN"); + bytes32 public constant AGGREGATION_VAULT_MANAGER = keccak256("AGGREGATION_VAULT_MANAGER"); + bytes32 public constant AGGREGATION_VAULT_MANAGER_ADMIN = keccak256("AGGREGATION_VAULT_MANAGER_ADMIN"); bytes32 public constant REBALANCER = keccak256("REBALANCER"); bytes32 public constant REBALANCER_ADMIN = keccak256("REBALANCER_ADMIN"); + /// @dev interest rate smearing period uint256 public constant INTEREST_SMEAR = 2 weeks; - constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) - Dispatch(_rewardsModule, _hooksModule, _feeModule, _allocationPointsModule) - {} - struct InitParams { address evc; address balanceTracker; @@ -62,13 +59,10 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea string symbol; uint256 initialCashAllocationPoints; } - // /// @param _evc EVC address - // /// @param _asset Aggregator's asset address - // /// @param _name Aggregator's name - // /// @param _symbol Aggregator's symbol - // /// @param _initialCashAllocationPoints Initial points to be allocated to the cash reserve - // /// @param _initialStrategies An array of initial strategies addresses - // /// @param _initialStrategiesAllocationPoints An array of initial strategies allocation points + + constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) + Dispatch(_rewardsModule, _hooksModule, _feeModule, _allocationPointsModule) + {} function init(InitParams calldata _initParams) external initializer { __ERC4626_init_unchained(IERC20(_initParams.asset)); @@ -96,28 +90,38 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea _grantRole(REBALANCER, _initParams.rebalancerPerihpery); // Setup role admins - _setRoleAdmin(STRATEGY_MANAGER, STRATEGY_MANAGER_ADMIN); + _setRoleAdmin(ALLOCATIONS_MANAGER, ALLOCATIONS_MANAGER_ADMIN); _setRoleAdmin(STRATEGY_ADDER, STRATEGY_ADDER_ADMIN); _setRoleAdmin(STRATEGY_REMOVER, STRATEGY_REMOVER_ADMIN); - _setRoleAdmin(MANAGER, MANAGER_ADMIN); + _setRoleAdmin(AGGREGATION_VAULT_MANAGER, AGGREGATION_VAULT_MANAGER_ADMIN); _setRoleAdmin(REBALANCER, REBALANCER_ADMIN); } /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address - function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) use(MODULE_FEE) {} + function setFeeRecipient(address _newFeeRecipient) external onlyRole(AGGREGATION_VAULT_MANAGER) use(MODULE_FEE) {} /// @notice Set performance fee (1e18 == 100%) /// @notice @param _newFee Fee rate - function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) use(MODULE_FEE) {} + function setPerformanceFee(uint256 _newFee) external onlyRole(AGGREGATION_VAULT_MANAGER) use(MODULE_FEE) {} /// @notice Opt in to strategy rewards /// @param _strategy Strategy address - function optInStrategyRewards(address _strategy) external override onlyRole(MANAGER) use(MODULE_REWARDS) {} + function optInStrategyRewards(address _strategy) + external + override + onlyRole(AGGREGATION_VAULT_MANAGER) + use(MODULE_REWARDS) + {} /// @notice Opt out of strategy rewards /// @param _strategy Strategy address - function optOutStrategyRewards(address _strategy) external override onlyRole(MANAGER) use(MODULE_REWARDS) {} + function optOutStrategyRewards(address _strategy) + external + override + onlyRole(AGGREGATION_VAULT_MANAGER) + use(MODULE_REWARDS) + {} /// @notice Claim a specific strategy rewards /// @param _strategy Strategy address. @@ -127,7 +131,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea function claimStrategyReward(address _strategy, address _reward, address _recipient, bool _forfeitRecentReward) external override - onlyRole(MANAGER) + onlyRole(AGGREGATION_VAULT_MANAGER) use(MODULE_REWARDS) {} @@ -140,13 +144,13 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea function disableBalanceForwarder() external override use(MODULE_REWARDS) {} /// @notice Adjust a certain strategy's allocation points. - /// @dev Can only be called by an address that have the STRATEGY_MANAGER + /// @dev Can only be called by an address that have the ALLOCATIONS_MANAGER /// @param _strategy address of strategy /// @param _newPoints new strategy's points function adjustAllocationPoints(address _strategy, uint256 _newPoints) external use(MODULE_ALLOCATION_POINTS) - onlyRole(STRATEGY_MANAGER) + onlyRole(ALLOCATIONS_MANAGER) {} /// @notice Set cap on strategy allocated amount. @@ -156,7 +160,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea function setStrategyCap(address _strategy, uint256 _cap) external use(MODULE_ALLOCATION_POINTS) - onlyRole(STRATEGY_MANAGER) + onlyRole(ALLOCATIONS_MANAGER) {} /// @notice Add new strategy with it's allocation points. @@ -182,7 +186,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea function setHooksConfig(address _hooksTarget, uint32 _hookedFns) external override - onlyRole(MANAGER) + onlyRole(AGGREGATION_VAULT_MANAGER) use(MODULE_HOOKS) {} diff --git a/src/modules/AllocationPoints.sol b/src/modules/AllocationPoints.sol index e4a27947..439735b6 100644 --- a/src/modules/AllocationPoints.sol +++ b/src/modules/AllocationPoints.sol @@ -16,7 +16,7 @@ abstract contract AllocationPointsModule is Shared { using SafeCast for uint256; /// @notice Adjust a certain strategy's allocation points. - /// @dev Can only be called by an address that have the STRATEGY_MANAGER + /// @dev Can only be called by an address that have the ALLOCATIONS_MANAGER /// @param _strategy address of strategy /// @param _newPoints new strategy's points function adjustAllocationPoints(address _strategy, uint256 _newPoints) external virtual nonReentrant { diff --git a/src/modules/Rewards.sol b/src/modules/Rewards.sol index 663a680f..24fc79fd 100644 --- a/src/modules/Rewards.sol +++ b/src/modules/Rewards.sol @@ -18,7 +18,6 @@ import {EventsLib} from "../lib/EventsLib.sol"; /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice A generic contract to integrate with https://github.com/euler-xyz/reward-streams abstract contract RewardsModule is IBalanceForwarder, Shared { - /// @notice Opt in to strategy rewards /// @param _strategy Strategy address function optInStrategyRewards(address _strategy) external virtual nonReentrant { diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index 0268dcd7..acb583e7 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -70,18 +70,18 @@ contract FourSixTwoSixAggBase is EVaultTestBase { withdrawalQueue = WithdrawalQueue(fourSixTwoSixAgg.withdrawalQueue()); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.REBALANCER_ADMIN(), deployer); withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER(), manager); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager); withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager); vm.stopPrank(); @@ -95,8 +95,8 @@ contract FourSixTwoSixAggBase is EVaultTestBase { assertEq(cashReserve.active, true); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_MANAGER()), - fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN() + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.ALLOCATIONS_MANAGER()), + fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN() ); assertEq( fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_ADDER()), fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN() @@ -105,22 +105,25 @@ contract FourSixTwoSixAggBase is EVaultTestBase { fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_REMOVER()), fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN() ); - assertEq(fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.MANAGER()), fourSixTwoSixAgg.MANAGER_ADMIN()); + assertEq( + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER()), + fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN() + ); assertEq( withdrawalQueue.getRoleAdmin(withdrawalQueue.WITHDRAW_QUEUE_MANAGER()), withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN() ); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer)); + assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer)); + assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer)); assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager)); + assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager)); assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER(), manager)); + assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager)); assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager)); } diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 9e45b94b..e2c5c8da 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -42,18 +42,18 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { ); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN(), deployer); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer); // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ADMIN(), deployer); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER(), manager); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager); // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER(), manager); + fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager); vm.stopPrank(); uint256 initialStrategyAllocationPoints = 500e18; From 133865d9b3596f567f49f437723da4fda96a48fc Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:46:53 +0300 Subject: [PATCH 16/19] add access control & last touches --- src/FourSixTwoSixAgg.sol | 65 +++++++++++-------- src/FourSixTwoSixAggFactory.sol | 1 - src/WithdrawalQueue.sol | 96 +++++++++++++++++------------ src/interface/IFourSixTwoSixAgg.sol | 11 +++- src/interface/IWithdrawalQueue.sol | 2 +- src/lib/ErrorsLib.sol | 1 + 6 files changed, 103 insertions(+), 73 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 9ae27333..4fc4a673 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -242,8 +242,13 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea _gulp(); } - // TODO: add access control - function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external { + /// @notice Execute a withdraw from a strategy. + /// @dev Can only be called from the WithdrawalQueue contract. + /// @param _strategy Strategy's address. + /// @param _withdrawAmount Amount to withdraw. + function executeStrategyWithdraw(address _strategy, uint256 _withdrawAmount) external { + _isCallerWithdrawalQueue(); + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); // Update allocated assets @@ -254,11 +259,28 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea IERC4626(_strategy).withdraw(_withdrawAmount, address(this), address(this)); } - // TODO: add access control - function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) - external - { - _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); + /// @notice Execute a withdraw from the AggregationVault + /// @dev This function should be called and can only be called by the WithdrawalQueue. + /// @param caller Withdraw call initiator. + /// @param receiver Receiver of the withdrawn asset. + /// @param owner Owner of shares to withdraw against. + /// @param assets Amount of asset to withdraw. + /// @param shares Amount of shares to withdraw against. + function executeAggregationVaultWithdraw( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares + ) external { + _isCallerWithdrawalQueue(); + + _gulp(); + + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + $.totalAssetsDeposited -= assets; + + super._withdraw(caller, receiver, owner, assets, shares); } /// @notice Get strategy params. @@ -383,16 +405,11 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - $.totalAssetsDeposited -= assets; uint256 assetsRetrieved = IERC20(asset()).balanceOf(address(this)); - if (assetsRetrieved < assets) { - IWithdrawalQueue($.withdrawalQueue).executeWithdrawFromQueue( - caller, receiver, owner, assets, shares, assetsRetrieved - ); - } else { - _executeWithdrawFromReserve(caller, receiver, owner, assets, shares); - } + IWithdrawalQueue($.withdrawalQueue).callWithdrawalQueue( + caller, receiver, owner, assets, shares, assetsRetrieved + ); } /// @notice update accrued interest. @@ -408,18 +425,6 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea $.totalAssetsDeposited += accruedInterest; } - function _executeWithdrawFromReserve( - address caller, - address receiver, - address owner, - uint256 assets, - uint256 shares - ) internal { - _gulp(); - - super._withdraw(caller, receiver, owner, assets, shares); - } - /// @dev gulp positive yield and increment the left interest function _gulp() internal { _updateInterestAccrued(); @@ -545,4 +550,10 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea function _msgSender() internal view override (ContextUpgradeable, Shared) returns (address) { return Shared._msgSender(); } + + function _isCallerWithdrawalQueue() internal view { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + + if (_msgSender() != $.withdrawalQueue) revert ErrorsLib.NotWithdrawaQueue(); + } } diff --git a/src/FourSixTwoSixAggFactory.sol b/src/FourSixTwoSixAggFactory.sol index 00465d44..713d5188 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/FourSixTwoSixAggFactory.sol @@ -46,7 +46,6 @@ contract FourSixTwoSixAggFactory { withdrawalQueueAddr = _withdrawalQueueAddr; } - // TODO: decrease bytecode size, use clones or something function deployEulerAggregationLayer( address _asset, string memory _name, diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index 1dd7730b..5c67d253 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -12,6 +12,7 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { error OutOfBounds(); error SameIndexes(); error NotEnoughAssets(); + error NotAuthorized(); bytes32 public constant WITHDRAW_QUEUE_MANAGER = keccak256("WITHDRAW_QUEUE_MANAGER"); bytes32 public constant WITHDRAW_QUEUE_MANAGER_ADMIN = keccak256("WITHDRAW_QUEUE_MANAGER_ADMIN"); @@ -37,21 +38,17 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); } - function getWithdrawalQueueAtIndex(uint256 _index) external view returns (address) { - WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); - - return $.withdrawalQueue[_index]; - } - - // TODO: add access control function addStrategyToWithdrawalQueue(address _strategy) external { + _isCallerAggregationVault(); + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); $.withdrawalQueue.push(_strategy); } - // TODO: add access control function removeStrategyFromWithdrawalQueue(address _strategy) external { + _isCallerAggregationVault(); + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); uint256 lastStrategyIndex = $.withdrawalQueue.length - 1; @@ -67,8 +64,29 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { $.withdrawalQueue.pop(); } - // TODO: add access control - function executeWithdrawFromQueue( + /// @notice Swap two strategies indexes in the withdrawal queue. + /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER. + /// @param _index1 index of first strategy + /// @param _index2 index of second strategy + function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); + + uint256 length = $.withdrawalQueue.length; + if (_index1 >= length || _index2 >= length) { + revert OutOfBounds(); + } + + if (_index1 == _index2) { + revert SameIndexes(); + } + + ($.withdrawalQueue[_index1], $.withdrawalQueue[_index2]) = + ($.withdrawalQueue[_index2], $.withdrawalQueue[_index1]); + + emit ReorderWithdrawalQueue(_index1, _index2); + } + + function callWithdrawalQueue( address caller, address receiver, address owner, @@ -76,26 +94,32 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { uint256 shares, uint256 availableAssets ) external { + _isCallerAggregationVault(); + WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); address eulerAggregationVaultCached = $.eulerAggregationVault; - uint256 numStrategies = $.withdrawalQueue.length; - for (uint256 i; i < numStrategies; ++i) { - IERC4626 strategy = IERC4626($.withdrawalQueue[i]); + if (availableAssets < assets) { + uint256 numStrategies = $.withdrawalQueue.length; + for (uint256 i; i < numStrategies; ++i) { + IERC4626 strategy = IERC4626($.withdrawalQueue[i]); - IFourSixTwoSixAgg(eulerAggregationVaultCached).harvest(address(strategy)); + IFourSixTwoSixAgg(eulerAggregationVaultCached).harvest(address(strategy)); - uint256 underlyingBalance = strategy.maxWithdraw(eulerAggregationVaultCached); - uint256 desiredAssets = assets - availableAssets; - uint256 withdrawAmount = (underlyingBalance > desiredAssets) ? desiredAssets : underlyingBalance; + uint256 underlyingBalance = strategy.maxWithdraw(eulerAggregationVaultCached); + uint256 desiredAssets = assets - availableAssets; + uint256 withdrawAmount = (underlyingBalance > desiredAssets) ? desiredAssets : underlyingBalance; - IFourSixTwoSixAgg(eulerAggregationVaultCached).withdrawFromStrategy(address(strategy), withdrawAmount); + IFourSixTwoSixAgg(eulerAggregationVaultCached).executeStrategyWithdraw( + address(strategy), withdrawAmount + ); - // update assetsRetrieved - availableAssets += withdrawAmount; + // update assetsRetrieved + availableAssets += withdrawAmount; - if (availableAssets >= assets) { - break; + if (availableAssets >= assets) { + break; + } } } @@ -103,31 +127,15 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { revert NotEnoughAssets(); } - IFourSixTwoSixAgg(eulerAggregationVaultCached).executeWithdrawFromReserve( + IFourSixTwoSixAgg(eulerAggregationVaultCached).executeAggregationVaultWithdraw( caller, receiver, owner, assets, shares ); } - /// @notice Swap two strategies indexes in the withdrawal queue. - /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER. - /// @param _index1 index of first strategy - /// @param _index2 index of second strategy - function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { + function getWithdrawalQueueAtIndex(uint256 _index) external view returns (address) { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); - uint256 length = $.withdrawalQueue.length; - if (_index1 >= length || _index2 >= length) { - revert OutOfBounds(); - } - - if (_index1 == _index2) { - revert SameIndexes(); - } - - ($.withdrawalQueue[_index1], $.withdrawalQueue[_index2]) = - ($.withdrawalQueue[_index2], $.withdrawalQueue[_index1]); - - emit ReorderWithdrawalQueue(_index1, _index2); + return $.withdrawalQueue[_index]; } /// @notice Return the withdrawal queue length. @@ -138,6 +146,12 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { return $.withdrawalQueue.length; } + function _isCallerAggregationVault() private view { + WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); + + if (msg.sender != $.eulerAggregationVault) revert NotAuthorized(); + } + function _getWithdrawalQueueStorage() private pure returns (WithdrawalQueueStorage storage $) { assembly { $.slot := WithdrawalQueueStorageLocation diff --git a/src/interface/IFourSixTwoSixAgg.sol b/src/interface/IFourSixTwoSixAgg.sol index 773b3d23..30300a6f 100644 --- a/src/interface/IFourSixTwoSixAgg.sol +++ b/src/interface/IFourSixTwoSixAgg.sol @@ -7,9 +7,14 @@ interface IFourSixTwoSixAgg { function rebalance(address _strategy, uint256 _amountToRebalance, bool _isDeposit) external; function gulp() external; function harvest(address strategy) external; - function withdrawFromStrategy(address _strategy, uint256 _withdrawAmount) external; - function executeWithdrawFromReserve(address caller, address receiver, address owner, uint256 assets, uint256 shares) - external; + function executeStrategyWithdraw(address _strategy, uint256 _withdrawAmount) external; + function executeAggregationVaultWithdraw( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares + ) external; function getStrategy(address _strategy) external view returns (Strategy memory); function totalAllocationPoints() external view returns (uint256); function totalAllocated() external view returns (uint256); diff --git a/src/interface/IWithdrawalQueue.sol b/src/interface/IWithdrawalQueue.sol index 1bf65969..e5e68d0c 100644 --- a/src/interface/IWithdrawalQueue.sol +++ b/src/interface/IWithdrawalQueue.sol @@ -5,7 +5,7 @@ interface IWithdrawalQueue { function init(address _owner, address _eulerAggregationVault) external; function addStrategyToWithdrawalQueue(address _strategy) external; function removeStrategyFromWithdrawalQueue(address _strategy) external; - function executeWithdrawFromQueue( + function callWithdrawalQueue( address caller, address receiver, address owner, diff --git a/src/lib/ErrorsLib.sol b/src/lib/ErrorsLib.sol index d7599b39..b7ed440b 100644 --- a/src/lib/ErrorsLib.sol +++ b/src/lib/ErrorsLib.sol @@ -23,4 +23,5 @@ library ErrorsLib { error NotHooksContract(); error InvalidHookedFns(); error EmptyError(); + error NotWithdrawaQueue(); } From c4d83c2fb978d92efad949c3bccca5d7235087aa Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:58:51 +0300 Subject: [PATCH 17/19] chore: uncomment tests --- src/FourSixTwoSixAgg.sol | 24 +++ test/unit/GulpTest.t.sol | 314 ++++++++++++++++++++------------------- 2 files changed, 182 insertions(+), 156 deletions(-) diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 4fc4a673..e814d251 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -48,6 +48,18 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @dev interest rate smearing period uint256 public constant INTEREST_SMEAR = 2 weeks; + /// @dev Euler saving rate struct + /// lastInterestUpdate: last timestamo where interest was updated. + /// interestSmearEnd: timestamp when the smearing of interest end. + /// interestLeft: amount of interest left to smear. + /// locked: if locked or not for update. + struct AggregationVaultSavingRate { + uint40 lastInterestUpdate; + uint40 interestSmearEnd; + uint168 interestLeft; + uint8 locked; + } + struct InitParams { address evc; address balanceTracker; @@ -298,6 +310,18 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea return _interestAccruedFromCache(); } + function getAggregationVaultSavingRate() external view returns (AggregationVaultSavingRate memory) { + AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); + AggregationVaultSavingRate memory avsr = AggregationVaultSavingRate({ + lastInterestUpdate: $.lastInterestUpdate, + interestSmearEnd: $.interestSmearEnd, + interestLeft: $.interestLeft, + locked: $.locked + }); + + return avsr; + } + function totalAllocated() external view returns (uint256) { AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index d6c383f1..fca77edb 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -1,156 +1,158 @@ -// // SPDX-License-Identifier: GPL-2.0-or-later -// pragma solidity ^0.8.0; - -// import {FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; - -// contract GulpTest is FourSixTwoSixAggBase { -// uint256 user1InitialBalance = 100000e18; -// uint256 amountToDeposit = 10000e18; - -// function setUp() public virtual override { -// super.setUp(); - -// uint256 initialStrategyAllocationPoints = 500e18; -// _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); - -// assetTST.mint(user1, user1InitialBalance); - -// // 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); -// { -// 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); -// address[] memory strategiesToRebalance = new address[](1); -// strategiesToRebalance[0] = address(eTST); -// rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); - -// assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); -// assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); -// assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); -// } -// } - -// function testGulpAfterNegativeYieldEqualToInterestLeft() public { -// fourSixTwoSixAgg.gulp(); -// FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); -// assertEq(ers.interestLeft, 0); - -// vm.warp(block.timestamp + 2 days); -// fourSixTwoSixAgg.gulp(); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - -// vm.warp(block.timestamp + 1 days); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); -// uint256 yield; -// { -// uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); -// uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); -// uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; -// yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; -// assetTST.mint(address(eTST), yield); -// eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); -// } -// vm.prank(user1); -// fourSixTwoSixAgg.harvest(address(eTST)); - -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - -// vm.warp(block.timestamp + 1 days); -// // interest per day 23.809523809523 -// assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); -// fourSixTwoSixAgg.gulp(); -// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); -// assertEq(ers.interestLeft, yield - 23809523809523809523); - -// // move close to end of smearing -// vm.warp(block.timestamp + 11 days); -// fourSixTwoSixAgg.gulp(); -// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - -// // mock a decrease of strategy balance by ers.interestLeft -// uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); -// uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - ers.interestLeft; -// vm.mockCall( -// address(eTST), -// abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), -// abi.encode(aggrCurrentStrategyBalanceAfterNegYield) -// ); -// vm.prank(user1); -// fourSixTwoSixAgg.harvest(address(eTST)); -// } - -// function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { -// fourSixTwoSixAgg.gulp(); -// FourSixTwoSixAgg.AggregationVaultSavingRateStorage memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); -// assertEq(ers.interestLeft, 0); - -// vm.warp(block.timestamp + 2 days); -// fourSixTwoSixAgg.gulp(); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - -// vm.warp(block.timestamp + 1 days); -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); -// uint256 yield; -// { -// uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); -// uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); -// uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; -// yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; -// assetTST.mint(address(eTST), yield); -// eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); -// } -// vm.prank(user1); -// fourSixTwoSixAgg.harvest(address(eTST)); - -// assertEq(fourSixTwoSixAgg.interestAccrued(), 0); - -// vm.warp(block.timestamp + 1 days); -// // interest per day 23.809523809523 -// assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); -// fourSixTwoSixAgg.gulp(); -// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); -// assertEq(ers.interestLeft, yield - 23809523809523809523); - -// // move close to end of smearing -// vm.warp(block.timestamp + 11 days); -// fourSixTwoSixAgg.gulp(); -// ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - -// // mock a decrease of strategy balance by ers.interestLeft -// uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); -// uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - (ers.interestLeft * 2); -// vm.mockCall( -// address(eTST), -// abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), -// abi.encode(aggrCurrentStrategyBalanceAfterNegYield) -// ); -// vm.prank(user1); -// fourSixTwoSixAgg.harvest(address(eTST)); -// } -// } +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import { + FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy +} from "../common/FourSixTwoSixAggBase.t.sol"; + +contract GulpTest is FourSixTwoSixAggBase { + uint256 user1InitialBalance = 100000e18; + uint256 amountToDeposit = 10000e18; + + function setUp() public virtual override { + super.setUp(); + + uint256 initialStrategyAllocationPoints = 500e18; + _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); + + assetTST.mint(user1, user1InitialBalance); + + // 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); + { + 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); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + + assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + } + } + + function testGulpAfterNegativeYieldEqualToInterestLeft() public { + fourSixTwoSixAgg.gulp(); + FourSixTwoSixAgg.AggregationVaultSavingRate memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(ers.interestLeft, 0); + + vm.warp(block.timestamp + 2 days); + fourSixTwoSixAgg.gulp(); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + + vm.warp(block.timestamp + 1 days); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + uint256 yield; + { + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); + uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; + yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; + assetTST.mint(address(eTST), yield); + eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + } + vm.prank(user1); + fourSixTwoSixAgg.harvest(address(eTST)); + + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + + vm.warp(block.timestamp + 1 days); + // interest per day 23.809523809523 + assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); + fourSixTwoSixAgg.gulp(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(ers.interestLeft, yield - 23809523809523809523); + + // move close to end of smearing + vm.warp(block.timestamp + 11 days); + fourSixTwoSixAgg.gulp(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + + // mock a decrease of strategy balance by ers.interestLeft + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - ers.interestLeft; + vm.mockCall( + address(eTST), + abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encode(aggrCurrentStrategyBalanceAfterNegYield) + ); + vm.prank(user1); + fourSixTwoSixAgg.harvest(address(eTST)); + } + + function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { + fourSixTwoSixAgg.gulp(); + FourSixTwoSixAgg.AggregationVaultSavingRate memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(ers.interestLeft, 0); + + vm.warp(block.timestamp + 2 days); + fourSixTwoSixAgg.gulp(); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + + vm.warp(block.timestamp + 1 days); + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + uint256 yield; + { + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); + uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; + yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; + assetTST.mint(address(eTST), yield); + eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + } + vm.prank(user1); + fourSixTwoSixAgg.harvest(address(eTST)); + + assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + + vm.warp(block.timestamp + 1 days); + // interest per day 23.809523809523 + assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); + fourSixTwoSixAgg.gulp(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(ers.interestLeft, yield - 23809523809523809523); + + // move close to end of smearing + vm.warp(block.timestamp + 11 days); + fourSixTwoSixAgg.gulp(); + ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + + // mock a decrease of strategy balance by ers.interestLeft + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - (ers.interestLeft * 2); + vm.mockCall( + address(eTST), + abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encode(aggrCurrentStrategyBalanceAfterNegYield) + ); + vm.prank(user1); + fourSixTwoSixAgg.harvest(address(eTST)); + } +} From 680dd20e9137b07402a870d80d88c0fca128cb93 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:03:03 +0300 Subject: [PATCH 18/19] chore: natspec and renames --- ...woSixAgg.sol => AggregationLayerVault.sol} | 16 +- ...y.sol => AggregationLayerVaultFactory.sol} | 22 +- src/WithdrawalQueue.sol | 41 +- src/lib/EventsLib.sol | 2 +- src/lib/StorageLib.sol | 2 +- ....t.sol => AggregationLayerVaultBase.t.sol} | 74 ++-- test/e2e/BalanceForwarderE2ETest.t.sol | 134 +++--- ...positRebalanceHarvestWithdrawE2ETest.t.sol | 389 +++++++++--------- test/e2e/HooksE2ETest.t.sol | 50 +-- test/e2e/PerformanceFeeE2ETest.t.sol | 98 ++--- test/e2e/StrategyCapE2ETest.t.sol | 95 ++--- test/e2e/StrategyRewardsE2ETest.t.sol | 20 +- .../fuzz/AdjustAllocationPointsFuzzTest.t.sol | 16 +- .../DepositWithdrawMintBurnFuzzTest.t.sol | 68 +-- test/unit/AddStrategyTest.t.sol | 12 +- test/unit/AdjustAllocationPointsTest.t.sol | 23 +- test/unit/GulpTest.t.sol | 118 +++--- test/unit/HarvestTest.t.sol | 139 ++++--- test/unit/RebalanceTest.t.sol | 223 +++++----- test/unit/RemoveStrategy.t.sol | 39 +- test/unit/ReorderWithdrawalQueueTest.t.sol | 23 +- 21 files changed, 844 insertions(+), 760 deletions(-) rename src/{FourSixTwoSixAgg.sol => AggregationLayerVault.sol} (98%) rename src/{FourSixTwoSixAggFactory.sol => AggregationLayerVaultFactory.sol} (80%) rename test/common/{FourSixTwoSixAggBase.t.sol => AggregationLayerVaultBase.t.sol} (53%) diff --git a/src/FourSixTwoSixAgg.sol b/src/AggregationLayerVault.sol similarity index 98% rename from src/FourSixTwoSixAgg.sol rename to src/AggregationLayerVault.sol index e814d251..074ac10e 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/AggregationLayerVault.sol @@ -7,8 +7,8 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IBalanceTracker} from "reward-streams/interfaces/IBalanceTracker.sol"; import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; import {IWithdrawalQueue} from "./interface/IWithdrawalQueue.sol"; -import {Dispatch} from "./Dispatch.sol"; // contracts +import {Dispatch} from "./Dispatch.sol"; import { ERC4626Upgradeable, ERC20Upgradeable @@ -27,9 +27,13 @@ import {EventsLib} from "./lib/EventsLib.sol"; /// @dev Do NOT use with fee on transfer tokens /// @dev Do NOT use with rebasing tokens -/// @dev Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol /// @dev inspired by Yearn v3 ❤️ -contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradeable, Dispatch, IFourSixTwoSixAgg { +contract AggregationLayerVault is + ERC4626Upgradeable, + AccessControlEnumerableUpgradeable, + Dispatch, + IFourSixTwoSixAgg +{ using SafeERC20 for IERC20; using SafeCast for uint256; @@ -49,6 +53,7 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea uint256 public constant INTEREST_SMEAR = 2 weeks; /// @dev Euler saving rate struct + /// @dev Based on https://github.com/euler-xyz/euler-vault-kit/blob/master/src/Synths/EulerSavingsRate.sol /// lastInterestUpdate: last timestamo where interest was updated. /// interestSmearEnd: timestamp when the smearing of interest end. /// interestLeft: amount of interest left to smear. @@ -204,7 +209,6 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea /// @notice Harvest strategy. /// @param strategy address of strategy - //TODO: is this safe without the reentrancy check function harvest(address strategy) external { _harvest(strategy); @@ -287,12 +291,12 @@ contract FourSixTwoSixAgg is ERC4626Upgradeable, AccessControlEnumerableUpgradea ) external { _isCallerWithdrawalQueue(); - _gulp(); - AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); $.totalAssetsDeposited -= assets; super._withdraw(caller, receiver, owner, assets, shares); + + _gulp(); } /// @notice Get strategy params. diff --git a/src/FourSixTwoSixAggFactory.sol b/src/AggregationLayerVaultFactory.sol similarity index 80% rename from src/FourSixTwoSixAggFactory.sol rename to src/AggregationLayerVaultFactory.sol index 713d5188..e105e3bd 100644 --- a/src/FourSixTwoSixAggFactory.sol +++ b/src/AggregationLayerVaultFactory.sol @@ -6,11 +6,11 @@ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Rewards} from "./modules/Rewards.sol"; import {Hooks} from "./modules/Hooks.sol"; import {Fee} from "./modules/Fee.sol"; -// core peripheries +// core plugins import {WithdrawalQueue} from "./WithdrawalQueue.sol"; -import {FourSixTwoSixAgg} from "./FourSixTwoSixAgg.sol"; +import {AggregationLayerVault} from "./AggregationLayerVault.sol"; -contract FourSixTwoSixAggFactory { +contract AggregationLayerVaultFactory { /// core dependencies address public immutable evc; address public immutable balanceTracker; @@ -19,7 +19,7 @@ contract FourSixTwoSixAggFactory { address public immutable hooksModuleImpl; address public immutable feeModuleImpl; address public immutable allocationpointsModuleImpl; - /// peripheries + /// plugins /// @dev Rebalancer periphery, one instance can serve different aggregation vaults address public immutable rebalancerAddr; /// @dev Withdrawal queue perihperhy, need to be deployed per aggregation vault @@ -58,14 +58,14 @@ contract FourSixTwoSixAggFactory { address feeModuleAddr = Clones.clone(feeModuleImpl); address allocationpointsModuleAddr = Clones.clone(allocationpointsModuleImpl); - // cloning peripheries + // cloning plugins WithdrawalQueue withdrawalQueue = WithdrawalQueue(Clones.clone(withdrawalQueueAddr)); // deploy new aggregation vault - FourSixTwoSixAgg fourSixTwoSixAgg = - new FourSixTwoSixAgg(rewardsModuleAddr, hooksModuleAddr, feeModuleAddr, allocationpointsModuleAddr); + AggregationLayerVault aggregationLayerVault = + new AggregationLayerVault(rewardsModuleAddr, hooksModuleAddr, feeModuleAddr, allocationpointsModuleAddr); - FourSixTwoSixAgg.InitParams memory aggregationVaultInitParams = FourSixTwoSixAgg.InitParams({ + AggregationLayerVault.InitParams memory aggregationVaultInitParams = AggregationLayerVault.InitParams({ evc: evc, balanceTracker: balanceTracker, withdrawalQueuePeriphery: address(withdrawalQueue), @@ -77,9 +77,9 @@ contract FourSixTwoSixAggFactory { initialCashAllocationPoints: _initialCashAllocationPoints }); - withdrawalQueue.init(msg.sender, address(fourSixTwoSixAgg)); - fourSixTwoSixAgg.init(aggregationVaultInitParams); + withdrawalQueue.init(msg.sender, address(aggregationLayerVault)); + aggregationLayerVault.init(aggregationVaultInitParams); - return address(fourSixTwoSixAgg); + return address(aggregationLayerVault); } } diff --git a/src/WithdrawalQueue.sol b/src/WithdrawalQueue.sol index 5c67d253..9a409ef8 100644 --- a/src/WithdrawalQueue.sol +++ b/src/WithdrawalQueue.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -// external dep -import {AccessControlEnumerableUpgradeable} from - "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +// interfaces import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; // internal dep import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +// contracts +import {AccessControlEnumerableUpgradeable} from + "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; contract WithdrawalQueue is AccessControlEnumerableUpgradeable { error OutOfBounds(); @@ -29,6 +30,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { event ReorderWithdrawalQueue(uint8 index1, uint8 index2); + /// @notice Initialize WithdrawalQueue. + /// @param _owner Aggregation layer vault owner. + /// @param _eulerAggregationVault Address of aggregation layer vault. function init(address _owner, address _eulerAggregationVault) external initializer { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); $.eulerAggregationVault = _eulerAggregationVault; @@ -38,6 +42,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { _setRoleAdmin(WITHDRAW_QUEUE_MANAGER, WITHDRAW_QUEUE_MANAGER_ADMIN); } + /// @notice Add a strategy to withdrawal queue array. + /// @dev Can only be called by the aggregation layer vault's address. + /// @param _strategy Strategy address to add function addStrategyToWithdrawalQueue(address _strategy) external { _isCallerAggregationVault(); @@ -46,6 +53,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { $.withdrawalQueue.push(_strategy); } + /// @notice Remove a strategy from withdrawal queue array. + /// @dev Can only be called by the aggregation layer vault's address. + /// @param _strategy Strategy address to add. function removeStrategyFromWithdrawalQueue(address _strategy) external { _isCallerAggregationVault(); @@ -65,9 +75,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { } /// @notice Swap two strategies indexes in the withdrawal queue. - /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER. - /// @param _index1 index of first strategy - /// @param _index2 index of second strategy + /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER role. + /// @param _index1 index of first strategy. + /// @param _index2 index of second strategy. function reorderWithdrawalQueue(uint8 _index1, uint8 _index2) external onlyRole(WITHDRAW_QUEUE_MANAGER) { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); @@ -86,6 +96,14 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { emit ReorderWithdrawalQueue(_index1, _index2); } + /// @notice Execute the withdraw initiated in the aggregation layer vault. + /// @dev Can only be called by the aggregation layer vault's address. + /// @param caller Initiator's address of withdraw. + /// @param receiver Withdraw receiver address. + /// @param owner Shares's owner to burn. + /// @param assets Amount of asset to withdraw. + /// @param shares Amount of shares to burn. + /// @param availableAssets Amount of available asset in aggregation layer vault's cash reserve. function callWithdrawalQueue( address caller, address receiver, @@ -121,6 +139,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { break; } } + + // re-calculate shares in case of socialized loss + shares = IERC4626(eulerAggregationVaultCached).previewWithdraw(assets); } if (availableAssets < assets) { @@ -132,6 +153,9 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { ); } + /// @notice Get strategy address from withdrawal queue by index. + /// @param _index Index to fetch. + /// @return address Strategy address. function getWithdrawalQueueAtIndex(uint256 _index) external view returns (address) { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); @@ -139,19 +163,22 @@ contract WithdrawalQueue is AccessControlEnumerableUpgradeable { } /// @notice Return the withdrawal queue length. - /// @return uint256 length + /// @return uint256 length. function withdrawalQueueLength() external pure returns (uint256) { WithdrawalQueueStorage memory $ = _getWithdrawalQueueStorage(); return $.withdrawalQueue.length; } + /// @dev Check if the msg.sender is the aggregation layer vault. function _isCallerAggregationVault() private view { WithdrawalQueueStorage storage $ = _getWithdrawalQueueStorage(); if (msg.sender != $.eulerAggregationVault) revert NotAuthorized(); } + /// @dev Return storage pointer. + /// @return $ WithdrawalQueueStorage storage struct. function _getWithdrawalQueueStorage() private pure returns (WithdrawalQueueStorage storage $) { assembly { $.slot := WithdrawalQueueStorageLocation diff --git a/src/lib/EventsLib.sol b/src/lib/EventsLib.sol index d8c9e8a2..952b8595 100644 --- a/src/lib/EventsLib.sol +++ b/src/lib/EventsLib.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; library EventsLib { - /// @dev FourSixTwoSixAgg events + /// @dev AggregationLayerVault events event Gulp(uint256 interestLeft, uint256 interestSmearEnd); event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); event AccruePerformanceFee(address indexed feeRecipient, uint256 yield, uint256 feeAssets); diff --git a/src/lib/StorageLib.sol b/src/lib/StorageLib.sol index 33435110..cab1b385 100644 --- a/src/lib/StorageLib.sol +++ b/src/lib/StorageLib.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; struct AggregationVaultStorage { /// @dev EVC address address evc; - /// @dev Total amount of _asset deposited into FourSixTwoSixAgg contract + /// @dev Total amount of _asset deposited into AggregationLayerVault contract uint256 totalAssetsDeposited; /// @dev Total amount of _asset deposited across all strategies. uint256 totalAllocated; diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/AggregationLayerVaultBase.t.sol similarity index 53% rename from test/common/FourSixTwoSixAggBase.t.sol rename to test/common/AggregationLayerVaultBase.t.sol index acb583e7..988011ec 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/AggregationLayerVaultBase.t.sol @@ -2,19 +2,19 @@ pragma solidity ^0.8.0; import "evk/test/unit/evault/EVaultTestBase.t.sol"; -import {FourSixTwoSixAgg, Strategy} from "../../src/FourSixTwoSixAgg.sol"; +import {AggregationLayerVault, Strategy} from "../../src/AggregationLayerVault.sol"; import {Rebalancer} from "../../src/Rebalancer.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; import {Hooks, HooksModule} from "../../src/modules/Hooks.sol"; import {Rewards} from "../../src/modules/Rewards.sol"; import {Fee} from "../../src/modules/Fee.sol"; -import {FourSixTwoSixAggFactory} from "../../src/FourSixTwoSixAggFactory.sol"; +import {AggregationLayerVaultFactory} from "../../src/AggregationLayerVaultFactory.sol"; import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; import {ErrorsLib} from "../../src/lib/ErrorsLib.sol"; import {AllocationPoints} from "../../src/modules/AllocationPoints.sol"; -contract FourSixTwoSixAggBase is EVaultTestBase { +contract AggregationLayerVaultBase is EVaultTestBase { uint256 public constant CASH_RESERVE_ALLOCATION_POINTS = 1000e18; address deployer; @@ -27,13 +27,12 @@ contract FourSixTwoSixAggBase is EVaultTestBase { Hooks hooksImpl; Fee feeModuleImpl; AllocationPoints allocationPointsModuleImpl; - // peripheries + // plugins Rebalancer rebalancer; WithdrawalQueue withdrawalQueueImpl; - FourSixTwoSixAggFactory fourSixTwoSixAggFactory; - - FourSixTwoSixAgg fourSixTwoSixAgg; + AggregationLayerVaultFactory aggregationLayerVaultFactory; + AggregationLayerVault aggregationLayerVault; WithdrawalQueue withdrawalQueue; function setUp() public virtual override { @@ -51,7 +50,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { rebalancer = new Rebalancer(); withdrawalQueueImpl = new WithdrawalQueue(); - fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory( + aggregationLayerVaultFactory = new AggregationLayerVaultFactory( address(evc), address(0), address(rewardsImpl), @@ -62,74 +61,75 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address(withdrawalQueueImpl) ); - fourSixTwoSixAgg = FourSixTwoSixAgg( - fourSixTwoSixAggFactory.deployEulerAggregationLayer( + aggregationLayerVault = AggregationLayerVault( + aggregationLayerVaultFactory.deployEulerAggregationLayer( address(assetTST), "assetTST_Agg", "assetTST_Agg", CASH_RESERVE_ALLOCATION_POINTS ) ); - withdrawalQueue = WithdrawalQueue(fourSixTwoSixAgg.withdrawalQueue()); + withdrawalQueue = WithdrawalQueue(aggregationLayerVault.withdrawalQueue()); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.REBALANCER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.ALLOCATIONS_MANAGER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_ADDER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_REMOVER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.REBALANCER_ADMIN(), deployer); withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.ALLOCATIONS_MANAGER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_ADDER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_REMOVER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER(), manager); withdrawalQueue.grantRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager); vm.stopPrank(); } function testInitialParams() public { - Strategy memory cashReserve = fourSixTwoSixAgg.getStrategy(address(0)); + Strategy memory cashReserve = aggregationLayerVault.getStrategy(address(0)); assertEq(cashReserve.allocated, 0); assertEq(cashReserve.allocationPoints, CASH_RESERVE_ALLOCATION_POINTS); assertEq(cashReserve.active, true); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.ALLOCATIONS_MANAGER()), - fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN() + aggregationLayerVault.getRoleAdmin(aggregationLayerVault.ALLOCATIONS_MANAGER()), + aggregationLayerVault.ALLOCATIONS_MANAGER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_ADDER()), fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN() + aggregationLayerVault.getRoleAdmin(aggregationLayerVault.STRATEGY_ADDER()), + aggregationLayerVault.STRATEGY_ADDER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_REMOVER()), - fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN() + aggregationLayerVault.getRoleAdmin(aggregationLayerVault.STRATEGY_REMOVER()), + aggregationLayerVault.STRATEGY_REMOVER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER()), - fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN() + aggregationLayerVault.getRoleAdmin(aggregationLayerVault.AGGREGATION_VAULT_MANAGER()), + aggregationLayerVault.AGGREGATION_VAULT_MANAGER_ADMIN() ); assertEq( withdrawalQueue.getRoleAdmin(withdrawalQueue.WITHDRAW_QUEUE_MANAGER()), withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN() ); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.ALLOCATIONS_MANAGER_ADMIN(), deployer)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.STRATEGY_ADDER_ADMIN(), deployer)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.STRATEGY_REMOVER_ADMIN(), deployer)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer)); assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.ALLOCATIONS_MANAGER(), manager)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.STRATEGY_ADDER(), manager)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.STRATEGY_REMOVER(), manager)); + assertTrue(aggregationLayerVault.hasRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER(), manager)); assertTrue(withdrawalQueue.hasRole(withdrawalQueue.WITHDRAW_QUEUE_MANAGER(), manager)); } function _addStrategy(address from, address strategy, uint256 allocationPoints) internal { vm.prank(from); - fourSixTwoSixAgg.addStrategy(strategy, allocationPoints); + aggregationLayerVault.addStrategy(strategy, allocationPoints); } function _getWithdrawalQueueLength() internal view returns (uint256) { diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index e2c5c8da..3b6ea6fc 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -2,19 +2,19 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, IRMTestDefault, TestERC20, - FourSixTwoSixAggFactory, + AggregationLayerVaultFactory, Rewards -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; -contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { +contract BalanceForwarderE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; address trackingReward; @@ -25,7 +25,7 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { vm.startPrank(deployer); trackingReward = address(new TrackingRewardStreams(address(evc), 2 weeks)); - fourSixTwoSixAggFactory = new FourSixTwoSixAggFactory( + aggregationLayerVaultFactory = new AggregationLayerVaultFactory( address(evc), trackingReward, address(rewardsImpl), @@ -35,25 +35,25 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { address(rebalancer), address(withdrawalQueueImpl) ); - fourSixTwoSixAgg = FourSixTwoSixAgg( - fourSixTwoSixAggFactory.deployEulerAggregationLayer( + aggregationLayerVault = AggregationLayerVault( + aggregationLayerVaultFactory.deployEulerAggregationLayer( address(assetTST), "assetTST_Agg", "assetTST_Agg", CASH_RESERVE_ALLOCATION_POINTS ) ); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER_ADMIN(), deployer); - // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.ALLOCATIONS_MANAGER_ADMIN(), deployer); + // aggregationLayerVault.grantRole(aggregationLayerVault.WITHDRAW_QUEUE_MANAGER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_ADDER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_REMOVER_ADMIN(), deployer); + aggregationLayerVault.grantRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER_ADMIN(), deployer); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.ALLOCATIONS_MANAGER(), manager); - // fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.AGGREGATION_VAULT_MANAGER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.ALLOCATIONS_MANAGER(), manager); + // aggregationLayerVault.grantRole(aggregationLayerVault.WITHDRAW_QUEUE_MANAGER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_ADDER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.STRATEGY_REMOVER(), manager); + aggregationLayerVault.grantRole(aggregationLayerVault.AGGREGATION_VAULT_MANAGER(), manager); vm.stopPrank(); uint256 initialStrategyAllocationPoints = 500e18; @@ -63,125 +63,125 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { // deposit into aggregator uint256 amountToDeposit = 10000e18; { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } } function testBalanceForwarderrAddress_Integrity() public view { - assertEq(fourSixTwoSixAgg.balanceTrackerAddress(), trackingReward); + assertEq(aggregationLayerVault.balanceTrackerAddress(), trackingReward); } function testEnableBalanceForwarder() public { vm.prank(user1); - fourSixTwoSixAgg.enableBalanceForwarder(); + aggregationLayerVault.enableBalanceForwarder(); - assertTrue(fourSixTwoSixAgg.balanceForwarderEnabled(user1)); + assertTrue(aggregationLayerVault.balanceForwarderEnabled(user1)); assertEq( - TrackingRewardStreams(trackingReward).balanceOf(user1, address(fourSixTwoSixAgg)), - fourSixTwoSixAgg.balanceOf(user1) + TrackingRewardStreams(trackingReward).balanceOf(user1, address(aggregationLayerVault)), + aggregationLayerVault.balanceOf(user1) ); } function testDisableBalanceForwarder() public { vm.prank(user1); - fourSixTwoSixAgg.enableBalanceForwarder(); + aggregationLayerVault.enableBalanceForwarder(); - assertTrue(fourSixTwoSixAgg.balanceForwarderEnabled(user1)); + assertTrue(aggregationLayerVault.balanceForwarderEnabled(user1)); vm.prank(user1); - fourSixTwoSixAgg.disableBalanceForwarder(); + aggregationLayerVault.disableBalanceForwarder(); - assertFalse(fourSixTwoSixAgg.balanceForwarderEnabled(user1)); - assertEq(TrackingRewardStreams(trackingReward).balanceOf(user1, address(fourSixTwoSixAgg)), 0); + assertFalse(aggregationLayerVault.balanceForwarderEnabled(user1)); + assertEq(TrackingRewardStreams(trackingReward).balanceOf(user1, address(aggregationLayerVault)), 0); } function testHookWhenReceiverEnabled() public { vm.prank(user1); - fourSixTwoSixAgg.enableBalanceForwarder(); + aggregationLayerVault.enableBalanceForwarder(); // deposit into aggregator uint256 amountToDeposit = 10000e18; { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); assertEq( - TrackingRewardStreams(trackingReward).balanceOf(user1, address(fourSixTwoSixAgg)), - fourSixTwoSixAgg.balanceOf(user1) + TrackingRewardStreams(trackingReward).balanceOf(user1, address(aggregationLayerVault)), + aggregationLayerVault.balanceOf(user1) ); } } function testHookWhenSenderEnabled() public { vm.prank(user1); - fourSixTwoSixAgg.enableBalanceForwarder(); + aggregationLayerVault.enableBalanceForwarder(); // deposit into aggregator uint256 amountToDeposit = 10000e18; { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); assertEq( - TrackingRewardStreams(trackingReward).balanceOf(user1, address(fourSixTwoSixAgg)), - fourSixTwoSixAgg.balanceOf(user1) + TrackingRewardStreams(trackingReward).balanceOf(user1, address(aggregationLayerVault)), + aggregationLayerVault.balanceOf(user1) ); } { - uint256 amountToWithdraw = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); - uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); - assertEq(eTST.balanceOf(address(fourSixTwoSixAgg)), 0); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertEq(eTST.balanceOf(address(aggregationLayerVault)), 0); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertEq( assetTST.balanceOf(user1), - user1AssetTSTBalanceBefore + fourSixTwoSixAgg.convertToAssets(amountToWithdraw) + user1AssetTSTBalanceBefore + aggregationLayerVault.convertToAssets(amountToWithdraw) ); - assertEq(TrackingRewardStreams(trackingReward).balanceOf(user1, address(fourSixTwoSixAgg)), 0); + assertEq(TrackingRewardStreams(trackingReward).balanceOf(user1, address(aggregationLayerVault)), 0); } } } diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index 4038be6b..96f902fa 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, @@ -11,9 +11,9 @@ import { TestERC20, WithdrawalQueue, Strategy -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; -contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { +contract DepositRebalanceHarvestWithdrawE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -30,41 +30,42 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + expectedStrategyCash + (aggregationLayerVault.getStrategy(address(eTST))).allocated, + strategyBefore.allocated + expectedStrategyCash ); } @@ -72,37 +73,37 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // partial withdraw, no need to withdraw from strategy as cash reserve is enough uint256 amountToWithdraw = 6000e18; { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - uint256 strategyShareBalanceBefore = eTST.balanceOf(address(fourSixTwoSixAgg)); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); - uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + uint256 strategyShareBalanceBefore = eTST.balanceOf(address(aggregationLayerVault)); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.withdraw(amountToWithdraw, user1, user1); + aggregationLayerVault.withdraw(amountToWithdraw, user1, user1); - assertEq(eTST.balanceOf(address(fourSixTwoSixAgg)), strategyShareBalanceBefore); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertEq(eTST.balanceOf(address(aggregationLayerVault)), strategyShareBalanceBefore); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertEq(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToWithdraw); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated); } // full withdraw, will have to withdraw from strategy as cash reserve is not enough { amountToWithdraw = amountToDeposit - amountToWithdraw; - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); - uint256 aggregatorTotalSupplyBefore = fourSixTwoSixAgg.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.withdraw(amountToWithdraw, user1, user1); + aggregationLayerVault.withdraw(amountToWithdraw, user1, user1); - assertEq(eTST.balanceOf(address(fourSixTwoSixAgg)), 0); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertEq(eTST.balanceOf(address(aggregationLayerVault)), 0); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertEq(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToWithdraw); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, 0); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, 0); } } @@ -111,67 +112,67 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); + assertEq((aggregationLayerVault.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 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(aggregationLayerVault)); 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)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); // 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 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); - assertEq(eTST.balanceOf(address(fourSixTwoSixAgg)), yield); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertEq(eTST.balanceOf(address(aggregationLayerVault)), yield); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertEq( assetTST.balanceOf(user1), - user1AssetTSTBalanceBefore + fourSixTwoSixAgg.convertToAssets(amountToWithdraw) + user1AssetTSTBalanceBefore + aggregationLayerVault.convertToAssets(amountToWithdraw) ); } } @@ -196,19 +197,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -217,19 +218,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = aggregationLayerVault.getStrategy(address(eTSTsecondary)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), eTSTstrategyBefore.allocated); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), eTSTsecondarystrategyBefore.allocated ); - uint256 expectedeTSTStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTstrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); - uint256 expectedeTSTsecondaryStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTsecondarystrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedeTSTStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTstrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); + uint256 expectedeTSTsecondaryStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTsecondarystrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); assertTrue(expectedeTSTStrategyCash != 0); assertTrue(expectedeTSTsecondaryStrategyCash != 0); @@ -238,29 +239,31 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + aggregationLayerVault.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash + ); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedeTSTStrategyCash); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), expectedeTSTsecondaryStrategyCash ); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash + (aggregationLayerVault.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash ); assertEq( - assetTST.balanceOf(address(fourSixTwoSixAgg)), + assetTST.balanceOf(address(aggregationLayerVault)), amountToDeposit - (expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash) ); } vm.warp(block.timestamp + 86400); // mock an increase of aggregator balance due to yield - uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTShareBalance); - uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTsecondaryUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTsecondaryShareBalance); uint256 aggrNeweTSTUnderlyingBalance = aggrCurrenteTSTUnderlyingBalance * 11e17 / 1e18; uint256 aggrNeweTSTsecondaryUnderlyingBalance = aggrCurrenteTSTsecondaryUnderlyingBalance * 11e17 / 1e18; @@ -269,25 +272,25 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { assetTST.mint(address(eTST), eTSTYield); assetTST.mint(address(eTSTsecondary), eTSTsecondaryYield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); - eTSTsecondary.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); + eTSTsecondary.skim(type(uint256).max, address(aggregationLayerVault)); // 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 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); - assertEq(eTST.balanceOf(address(fourSixTwoSixAgg)), 0); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertEq(eTST.balanceOf(address(aggregationLayerVault)), 0); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertEq( assetTST.balanceOf(user1), - user1AssetTSTBalanceBefore + fourSixTwoSixAgg.convertToAssets(amountToWithdraw) + user1AssetTSTBalanceBefore + aggregationLayerVault.convertToAssets(amountToWithdraw) ); } } @@ -297,70 +300,72 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); + assertEq((aggregationLayerVault.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 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(aggregationLayerVault)); 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)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); // harvest vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.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 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.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(eTST.balanceOf(address(aggregationLayerVault)), 0, 1); + assertApproxEqAbs( + aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw, 1 + ); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertApproxEqAbs(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToDeposit + yield, 1); } } @@ -385,19 +390,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -406,19 +411,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = aggregationLayerVault.getStrategy(address(eTSTsecondary)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), eTSTstrategyBefore.allocated); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), eTSTsecondarystrategyBefore.allocated ); - uint256 expectedeTSTStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTstrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); - uint256 expectedeTSTsecondaryStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTsecondarystrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedeTSTStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTstrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); + uint256 expectedeTSTsecondaryStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTsecondarystrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); assertTrue(expectedeTSTStrategyCash != 0); assertTrue(expectedeTSTsecondaryStrategyCash != 0); @@ -427,20 +432,22 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + aggregationLayerVault.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash + ); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedeTSTStrategyCash); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), expectedeTSTsecondaryStrategyCash ); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash + (aggregationLayerVault.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash ); assertEq( - assetTST.balanceOf(address(fourSixTwoSixAgg)), + assetTST.balanceOf(address(aggregationLayerVault)), amountToDeposit - (expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash) ); } @@ -450,9 +457,9 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { uint256 eTSTsecondaryYield; { // mock an increase of aggregator balance due to yield - uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTShareBalance); - uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTsecondaryUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTsecondaryShareBalance); uint256 aggrNeweTSTUnderlyingBalance = aggrCurrenteTSTUnderlyingBalance * 11e17 / 1e18; @@ -462,8 +469,8 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { assetTST.mint(address(eTST), eTSTYield); assetTST.mint(address(eTSTsecondary), eTSTsecondaryYield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); - eTSTsecondary.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); + eTSTsecondary.skim(type(uint256).max, address(aggregationLayerVault)); } // harvest @@ -471,22 +478,24 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToHarvest[0] = address(eTST); strategiesToHarvest[1] = address(eTSTsecondary); vm.prank(user1); - fourSixTwoSixAgg.harvestMultipleStrategies(strategiesToHarvest); + aggregationLayerVault.harvestMultipleStrategies(strategiesToHarvest); 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 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); - assertApproxEqAbs(eTST.balanceOf(address(fourSixTwoSixAgg)), 0, 0); - assertApproxEqAbs(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw, 1); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertApproxEqAbs(eTST.balanceOf(address(aggregationLayerVault)), 0, 0); + assertApproxEqAbs( + aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - amountToWithdraw, 1 + ); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertApproxEqAbs( assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + amountToDeposit + eTSTYield + eTSTsecondaryYield, @@ -515,19 +524,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -536,19 +545,19 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { // 10k deposited; 4000 for reserve, 2000 for eTST, 4000 for eTSTsecondary vm.warp(block.timestamp + 86400); { - Strategy memory eTSTstrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - Strategy memory eTSTsecondarystrategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory eTSTstrategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + Strategy memory eTSTsecondarystrategyBefore = aggregationLayerVault.getStrategy(address(eTSTsecondary)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTstrategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), eTSTstrategyBefore.allocated); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), eTSTsecondarystrategyBefore.allocated ); - uint256 expectedeTSTStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTstrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); - uint256 expectedeTSTsecondaryStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * eTSTsecondarystrategyBefore.allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedeTSTStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTstrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); + uint256 expectedeTSTsecondaryStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * eTSTsecondarystrategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); assertTrue(expectedeTSTStrategyCash != 0); assertTrue(expectedeTSTsecondaryStrategyCash != 0); @@ -557,20 +566,22 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + aggregationLayerVault.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash + ); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedeTSTStrategyCash); + assertEq( + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), expectedeTSTsecondaryStrategyCash ); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedeTSTStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash + (aggregationLayerVault.getStrategy(address(eTSTsecondary))).allocated, expectedeTSTsecondaryStrategyCash ); assertEq( - assetTST.balanceOf(address(fourSixTwoSixAgg)), + assetTST.balanceOf(address(aggregationLayerVault)), amountToDeposit - (expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash) ); } @@ -580,9 +591,9 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { uint256 eTSTsecondaryYield; { // mock an increase of aggregator balance due to yield - uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTShareBalance); - uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrenteTSTsecondaryShareBalance = eTSTsecondary.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrenteTSTsecondaryUnderlyingBalance = eTST.convertToAssets(aggrCurrenteTSTsecondaryShareBalance); uint256 aggrNeweTSTUnderlyingBalance = aggrCurrenteTSTUnderlyingBalance * 11e17 / 1e18; @@ -592,26 +603,26 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { assetTST.mint(address(eTST), eTSTYield); assetTST.mint(address(eTSTsecondary), eTSTsecondaryYield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); - eTSTsecondary.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); + eTSTsecondary.skim(type(uint256).max, address(aggregationLayerVault)); } // harvest address[] memory strategiesToHarvest = new address[](1); strategiesToHarvest[0] = address(eTST); vm.prank(user1); - fourSixTwoSixAgg.harvestMultipleStrategies(strategiesToHarvest); + aggregationLayerVault.harvestMultipleStrategies(strategiesToHarvest); vm.warp(block.timestamp + 2 weeks); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(address(eTSTsecondary)); + aggregationLayerVault.removeStrategy(address(eTSTsecondary)); { - uint256 amountToWithdraw = fourSixTwoSixAgg.balanceOf(user1); + uint256 amountToWithdraw = aggregationLayerVault.balanceOf(user1); vm.prank(user1); vm.expectRevert(WithdrawalQueue.NotEnoughAssets.selector); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); } } } diff --git a/test/e2e/HooksE2ETest.t.sol b/test/e2e/HooksE2ETest.t.sol index a7570b48..59bb388b 100644 --- a/test/e2e/HooksE2ETest.t.sol +++ b/test/e2e/HooksE2ETest.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, @@ -11,9 +11,9 @@ import { TestERC20, IHookTarget, ErrorsLib -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; -contract HooksE2ETest is FourSixTwoSixAggBase { +contract HooksE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -26,38 +26,38 @@ contract HooksE2ETest is FourSixTwoSixAggBase { } function testSetHooksConfig() public { - uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + uint32 expectedHookedFns = aggregationLayerVault.DEPOSIT() | aggregationLayerVault.WITHDRAW() + | aggregationLayerVault.ADD_STRATEGY() | aggregationLayerVault.REMOVE_STRATEGY(); vm.startPrank(manager); address hooksContract = address(new HooksContract()); - fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); + aggregationLayerVault.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); - (address hookTarget, uint32 hookedFns) = fourSixTwoSixAgg.getHooksConfig(); + (address hookTarget, uint32 hookedFns) = aggregationLayerVault.getHooksConfig(); assertEq(hookTarget, hooksContract); assertEq(hookedFns, expectedHookedFns); } function testSetHooksConfigWithAddressZero() public { - uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + uint32 expectedHookedFns = aggregationLayerVault.DEPOSIT() | aggregationLayerVault.WITHDRAW() + | aggregationLayerVault.ADD_STRATEGY() | aggregationLayerVault.REMOVE_STRATEGY(); vm.startPrank(manager); vm.expectRevert(ErrorsLib.InvalidHooksTarget.selector); - fourSixTwoSixAgg.setHooksConfig(address(0), expectedHookedFns); + aggregationLayerVault.setHooksConfig(address(0), expectedHookedFns); vm.stopPrank(); } function testSetHooksConfigWithNotHooksContract() public { - uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + uint32 expectedHookedFns = aggregationLayerVault.DEPOSIT() | aggregationLayerVault.WITHDRAW() + | aggregationLayerVault.ADD_STRATEGY() | aggregationLayerVault.REMOVE_STRATEGY(); vm.startPrank(manager); address hooksContract = address(new NotHooksContract()); vm.expectRevert(ErrorsLib.NotHooksContract.selector); - fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); + aggregationLayerVault.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } @@ -66,33 +66,33 @@ contract HooksE2ETest is FourSixTwoSixAggBase { vm.startPrank(manager); address hooksContract = address(new HooksContract()); vm.expectRevert(ErrorsLib.InvalidHookedFns.selector); - fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); + aggregationLayerVault.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); } function testHookedDeposit() public { - uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT(); + uint32 expectedHookedFns = aggregationLayerVault.DEPOSIT(); vm.startPrank(manager); address hooksContract = address(new HooksContract()); - fourSixTwoSixAgg.setHooksConfig(hooksContract, expectedHookedFns); + aggregationLayerVault.setHooksConfig(hooksContract, expectedHookedFns); vm.stopPrank(); uint256 amountToDeposit = 10000e18; // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } } diff --git a/test/e2e/PerformanceFeeE2ETest.t.sol b/test/e2e/PerformanceFeeE2ETest.t.sol index 5b3a811b..1bb5cb55 100644 --- a/test/e2e/PerformanceFeeE2ETest.t.sol +++ b/test/e2e/PerformanceFeeE2ETest.t.sol @@ -2,17 +2,17 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, IRMTestDefault, TestERC20, Strategy -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; -contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { +contract PerformanceFeeE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; address feeRecipient; @@ -30,18 +30,18 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { function testSetPerformanceFee() public { { - (, uint256 fee) = fourSixTwoSixAgg.performanceFeeConfig(); + (, uint256 fee) = aggregationLayerVault.performanceFeeConfig(); assertEq(fee, 0); } uint256 newPerformanceFee = 3e17; vm.startPrank(manager); - fourSixTwoSixAgg.setFeeRecipient(feeRecipient); - fourSixTwoSixAgg.setPerformanceFee(newPerformanceFee); + aggregationLayerVault.setFeeRecipient(feeRecipient); + aggregationLayerVault.setPerformanceFee(newPerformanceFee); vm.stopPrank(); - (address feeRecipientAddr, uint256 fee) = fourSixTwoSixAgg.performanceFeeConfig(); + (address feeRecipientAddr, uint256 fee) = aggregationLayerVault.performanceFeeConfig(); assertEq(fee, newPerformanceFee); assertEq(feeRecipientAddr, feeRecipient); } @@ -50,107 +50,111 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { uint256 newPerformanceFee = 3e17; vm.startPrank(manager); - fourSixTwoSixAgg.setFeeRecipient(feeRecipient); - fourSixTwoSixAgg.setPerformanceFee(newPerformanceFee); + aggregationLayerVault.setFeeRecipient(feeRecipient); + aggregationLayerVault.setPerformanceFee(newPerformanceFee); vm.stopPrank(); uint256 amountToDeposit = 10000e18; // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedStrategyCash); } vm.warp(block.timestamp + 86400); // mock an increase of strategy balance by 10% uint256 yield; { - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); } - (, uint256 performanceFee) = fourSixTwoSixAgg.performanceFeeConfig(); + (, uint256 performanceFee) = aggregationLayerVault.performanceFeeConfig(); uint256 expectedPerformanceFee = yield * performanceFee / 1e18; - Strategy memory strategyBeforeHarvest = fourSixTwoSixAgg.getStrategy(address(eTST)); - uint256 totalAllocatedBefore = fourSixTwoSixAgg.totalAllocated(); + Strategy memory strategyBeforeHarvest = aggregationLayerVault.getStrategy(address(eTST)); + uint256 totalAllocatedBefore = aggregationLayerVault.totalAllocated(); // harvest vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); assertEq(assetTST.balanceOf(feeRecipient), expectedPerformanceFee); assertEq( - fourSixTwoSixAgg.getStrategy(address(eTST)).allocated, + aggregationLayerVault.getStrategy(address(eTST)).allocated, strategyBeforeHarvest.allocated + yield - expectedPerformanceFee ); - assertEq(fourSixTwoSixAgg.totalAllocated(), totalAllocatedBefore + yield - expectedPerformanceFee); + assertEq(aggregationLayerVault.totalAllocated(), totalAllocatedBefore + yield - expectedPerformanceFee); // 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 amountToWithdraw = aggregationLayerVault.balanceOf(user1); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); + uint256 aggregatorTotalSupplyBefore = aggregationLayerVault.totalSupply(); uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); - uint256 expectedAssetTST = fourSixTwoSixAgg.convertToAssets(fourSixTwoSixAgg.balanceOf(user1)); + uint256 expectedAssetTST = aggregationLayerVault.convertToAssets(aggregationLayerVault.balanceOf(user1)); vm.prank(user1); - fourSixTwoSixAgg.redeem(amountToWithdraw, user1, user1); + aggregationLayerVault.redeem(amountToWithdraw, user1, user1); - assertApproxEqAbs(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - expectedAssetTST, 1); - assertEq(fourSixTwoSixAgg.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); + assertApproxEqAbs( + aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - expectedAssetTST, 1 + ); + assertEq(aggregationLayerVault.totalSupply(), aggregatorTotalSupplyBefore - amountToWithdraw); assertApproxEqAbs(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + expectedAssetTST, 1); } // full withdraw of recipient fees { - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 assetTSTBalanceBefore = assetTST.balanceOf(feeRecipient); - uint256 feeShares = fourSixTwoSixAgg.balanceOf(feeRecipient); - uint256 expectedAssets = fourSixTwoSixAgg.convertToAssets(feeShares); + uint256 feeShares = aggregationLayerVault.balanceOf(feeRecipient); + uint256 expectedAssets = aggregationLayerVault.convertToAssets(feeShares); vm.prank(feeRecipient); - fourSixTwoSixAgg.redeem(feeShares, feeRecipient, feeRecipient); + aggregationLayerVault.redeem(feeShares, feeRecipient, feeRecipient); - assertApproxEqAbs(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - expectedAssets, 1); - assertEq(fourSixTwoSixAgg.totalSupply(), 0); + assertApproxEqAbs( + aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - expectedAssets, 1 + ); + assertEq(aggregationLayerVault.totalSupply(), 0); assertApproxEqAbs(assetTST.balanceOf(feeRecipient), assetTSTBalanceBefore + expectedAssets, 1); } } diff --git a/test/e2e/StrategyCapE2ETest.t.sol b/test/e2e/StrategyCapE2ETest.t.sol index d7373ad4..c5ebcfc6 100644 --- a/test/e2e/StrategyCapE2ETest.t.sol +++ b/test/e2e/StrategyCapE2ETest.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, @@ -11,9 +11,9 @@ import { TestERC20, Strategy, ErrorsLib -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; -contract StrategyCapE2ETest is FourSixTwoSixAggBase { +contract StrategyCapE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -28,12 +28,12 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { function testSetCap() public { uint256 cap = 1000000e18; - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).cap, 0); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).cap, 0); vm.prank(manager); - fourSixTwoSixAgg.setStrategyCap(address(eTST), cap); + aggregationLayerVault.setStrategyCap(address(eTST), cap); - Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = aggregationLayerVault.getStrategy(address(eTST)); assertEq(strategy.cap, cap); } @@ -43,53 +43,54 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { vm.prank(manager); vm.expectRevert(ErrorsLib.InactiveStrategy.selector); - fourSixTwoSixAgg.setStrategyCap(address(0x2), cap); + aggregationLayerVault.setStrategyCap(address(0x2), cap); } function testRebalanceAfterHittingCap() public { uint256 cap = 3333333333333333333333; vm.prank(manager); - fourSixTwoSixAgg.setStrategyCap(address(eTST), cap); + aggregationLayerVault.setStrategyCap(address(eTST), cap); uint256 amountToDeposit = 10000e18; // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + expectedStrategyCash + (aggregationLayerVault.getStrategy(address(eTST))).allocated, + strategyBefore.allocated + expectedStrategyCash ); } @@ -97,17 +98,17 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { vm.warp(block.timestamp + 86400); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); - uint256 strategyAllocatedBefore = (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated; + uint256 strategyAllocatedBefore = (aggregationLayerVault.getStrategy(address(eTST))).allocated; address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); vm.stopPrank(); - assertEq(strategyAllocatedBefore, (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated); + assertEq(strategyAllocatedBefore, (aggregationLayerVault.getStrategy(address(eTST))).allocated); } function testRebalanceWhentargetAllocationGreaterThanCap() public { @@ -115,45 +116,45 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); // set cap 10% less than target allocation uint256 cap = expectedStrategyCash * 9e17 / 1e18; vm.prank(manager); - fourSixTwoSixAgg.setStrategyCap(address(eTST), cap); + aggregationLayerVault.setStrategyCap(address(eTST), cap); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), cap); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), cap); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + cap); + assertEq(aggregationLayerVault.totalAllocated(), cap); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), cap); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated + cap); } } } diff --git a/test/e2e/StrategyRewardsE2ETest.t.sol b/test/e2e/StrategyRewardsE2ETest.t.sol index edbcb9a9..91303088 100644 --- a/test/e2e/StrategyRewardsE2ETest.t.sol +++ b/test/e2e/StrategyRewardsE2ETest.t.sol @@ -2,17 +2,17 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, IRMTestDefault, TestERC20 -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; -contract StrategyRewardsE2ETest is FourSixTwoSixAggBase { +contract StrategyRewardsE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -26,19 +26,19 @@ contract StrategyRewardsE2ETest is FourSixTwoSixAggBase { function testOptInStrategyRewards() public { vm.prank(manager); - fourSixTwoSixAgg.optInStrategyRewards(address(eTST)); + aggregationLayerVault.optInStrategyRewards(address(eTST)); - assertTrue(eTST.balanceForwarderEnabled(address(fourSixTwoSixAgg))); + assertTrue(eTST.balanceForwarderEnabled(address(aggregationLayerVault))); } function testOptOutStrategyRewards() public { vm.prank(manager); - fourSixTwoSixAgg.optInStrategyRewards(address(eTST)); - assertTrue(eTST.balanceForwarderEnabled(address(fourSixTwoSixAgg))); + aggregationLayerVault.optInStrategyRewards(address(eTST)); + assertTrue(eTST.balanceForwarderEnabled(address(aggregationLayerVault))); vm.prank(manager); - fourSixTwoSixAgg.optOutStrategyRewards(address(eTST)); + aggregationLayerVault.optOutStrategyRewards(address(eTST)); - assertFalse(eTST.balanceForwarderEnabled(address(fourSixTwoSixAgg))); + assertFalse(eTST.balanceForwarderEnabled(address(aggregationLayerVault))); } } diff --git a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol index 07c38942..ceac1837 100644 --- a/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol +++ b/test/fuzz/AdjustAllocationPointsFuzzTest.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; +import {AggregationLayerVaultBase, AggregationLayerVault, Strategy} from "../common/AggregationLayerVaultBase.t.sol"; -contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { +contract AdjustAllocationsPointsFuzzTest is AggregationLayerVaultBase { function setUp() public virtual override { super.setUp(); @@ -14,23 +14,23 @@ contract AdjustAllocationsPointsFuzzTest is FourSixTwoSixAggBase { function testFuzzAdjustAllocationPoints(uint256 _newAllocationPoints) public { _newAllocationPoints = bound(_newAllocationPoints, 0, type(uint120).max); - uint256 strategyAllocationPoints = (fourSixTwoSixAgg.getStrategy(address(eTST))).allocationPoints; - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 strategyAllocationPoints = (aggregationLayerVault.getStrategy(address(eTST))).allocationPoints; + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), _newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST), _newAllocationPoints); - Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = aggregationLayerVault.getStrategy(address(eTST)); if (_newAllocationPoints < strategyAllocationPoints) { assertEq( - fourSixTwoSixAgg.totalAllocationPoints(), + aggregationLayerVault.totalAllocationPoints(), totalAllocationPointsBefore - (strategyAllocationPoints - _newAllocationPoints) ); } else { assertEq( - fourSixTwoSixAgg.totalAllocationPoints(), + aggregationLayerVault.totalAllocationPoints(), totalAllocationPointsBefore + (_newAllocationPoints - strategyAllocationPoints) ); } diff --git a/test/fuzz/DepositWithdrawMintBurnFuzzTest.t.sol b/test/fuzz/DepositWithdrawMintBurnFuzzTest.t.sol index ae9b9efc..dea9b6df 100644 --- a/test/fuzz/DepositWithdrawMintBurnFuzzTest.t.sol +++ b/test/fuzz/DepositWithdrawMintBurnFuzzTest.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {console2, FourSixTwoSixAggBase, FourSixTwoSixAgg} from "../common/FourSixTwoSixAggBase.t.sol"; +import {console2, AggregationLayerVaultBase, AggregationLayerVault} from "../common/AggregationLayerVaultBase.t.sol"; -contract DepositWithdrawMintBurnFuzzTest is FourSixTwoSixAggBase { +contract DepositWithdrawMintBurnFuzzTest is AggregationLayerVaultBase { uint256 constant MAX_ALLOWED = type(uint256).max; function setUp() public virtual override { @@ -14,16 +14,16 @@ contract DepositWithdrawMintBurnFuzzTest is FourSixTwoSixAggBase { // moch the scenario of _assets ownership assetTST.mint(user1, _assets); - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); _deposit(user1, _assets); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + _assets); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + _assets); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + _assets); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + _assets); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + _assets); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + _assets); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - _assets); } @@ -45,36 +45,36 @@ contract DepositWithdrawMintBurnFuzzTest is FourSixTwoSixAggBase { vm.warp(block.timestamp + _timestampAfterDeposit); // fuzz partial & full withdraws - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 receiverAssetBalanceBefore = assetTST.balanceOf(_receiver); vm.startPrank(user1); - fourSixTwoSixAgg.withdraw(_assetsToWithdraw, _receiver, user1); + aggregationLayerVault.withdraw(_assetsToWithdraw, _receiver, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore - _assetsToWithdraw); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore - _assetsToWithdraw); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - _assetsToWithdraw); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore - _assetsToWithdraw); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore - _assetsToWithdraw); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - _assetsToWithdraw); assertEq(assetTST.balanceOf(_receiver), receiverAssetBalanceBefore + _assetsToWithdraw); } function testFuzzMint(uint256 _shares) public { // moch the scenario of _assets ownership - uint256 assets = fourSixTwoSixAgg.previewMint(_shares); + uint256 assets = aggregationLayerVault.previewMint(_shares); assetTST.mint(user1, assets); - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); _mint(user1, assets, _shares); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + _shares); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + _shares); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + assets); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + _shares); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + _shares); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + assets); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - assets); } @@ -91,38 +91,38 @@ contract DepositWithdrawMintBurnFuzzTest is FourSixTwoSixAggBase { _timestampAfterDeposit = bound(_timestampAfterDeposit, 0, 86400); // deposit - uint256 assetsToDeposit = fourSixTwoSixAgg.previewMint(_sharesToMint); + uint256 assetsToDeposit = aggregationLayerVault.previewMint(_sharesToMint); assetTST.mint(user1, assetsToDeposit); _mint(user1, assetsToDeposit, _sharesToMint); vm.warp(block.timestamp + _timestampAfterDeposit); // fuzz partial & full redeem - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 receiverAssetBalanceBefore = assetTST.balanceOf(_receiver); vm.startPrank(user1); - uint256 assetsToWithdraw = fourSixTwoSixAgg.redeem(_sharesToRedeem, _receiver, user1); + uint256 assetsToWithdraw = aggregationLayerVault.redeem(_sharesToRedeem, _receiver, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore - _sharesToRedeem); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore - _sharesToRedeem); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore - assetsToWithdraw); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore - _sharesToRedeem); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore - _sharesToRedeem); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore - assetsToWithdraw); assertEq(assetTST.balanceOf(_receiver), receiverAssetBalanceBefore + assetsToWithdraw); } function _deposit(address _from, uint256 _assets) private { vm.startPrank(_from); - assetTST.approve(address(fourSixTwoSixAgg), _assets); - fourSixTwoSixAgg.deposit(_assets, _from); + assetTST.approve(address(aggregationLayerVault), _assets); + aggregationLayerVault.deposit(_assets, _from); vm.stopPrank(); } function _mint(address _from, uint256 _assets, uint256 _shares) private { vm.startPrank(_from); - assetTST.approve(address(fourSixTwoSixAgg), _assets); - fourSixTwoSixAgg.mint(_shares, _from); + assetTST.approve(address(aggregationLayerVault), _assets); + aggregationLayerVault.mint(_shares, _from); vm.stopPrank(); } } diff --git a/test/unit/AddStrategyTest.t.sol b/test/unit/AddStrategyTest.t.sol index 493c13e9..0efc9e8c 100644 --- a/test/unit/AddStrategyTest.t.sol +++ b/test/unit/AddStrategyTest.t.sol @@ -1,22 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg} from "../common/FourSixTwoSixAggBase.t.sol"; +import {AggregationLayerVaultBase, AggregationLayerVault} from "../common/AggregationLayerVaultBase.t.sol"; -contract AddStrategyTest is FourSixTwoSixAggBase { +contract AddStrategyTest is AggregationLayerVaultBase { function setUp() public virtual override { super.setUp(); } function testAddStrategy() public { uint256 allocationPoints = 500e18; - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); assertEq(_getWithdrawalQueueLength(), 0); _addStrategy(manager, address(eTST), allocationPoints); - assertEq(fourSixTwoSixAgg.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); + assertEq(aggregationLayerVault.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); assertEq(_getWithdrawalQueueLength(), 1); } @@ -40,13 +40,13 @@ contract AddStrategyTest is FourSixTwoSixAggBase { function testAddStrategy_AlreadyAddedStrategy() public { uint256 allocationPoints = 500e18; - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); assertEq(_getWithdrawalQueueLength(), 0); _addStrategy(manager, address(eTST), allocationPoints); - assertEq(fourSixTwoSixAgg.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); + assertEq(aggregationLayerVault.totalAllocationPoints(), allocationPoints + totalAllocationPointsBefore); assertEq(_getWithdrawalQueueLength(), 1); vm.expectRevert(); diff --git a/test/unit/AdjustAllocationPointsTest.t.sol b/test/unit/AdjustAllocationPointsTest.t.sol index 12cf042b..bf440e35 100644 --- a/test/unit/AdjustAllocationPointsTest.t.sol +++ b/test/unit/AdjustAllocationPointsTest.t.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, Strategy, ErrorsLib} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { +import { + AggregationLayerVaultBase, + AggregationLayerVault, + Strategy, + ErrorsLib +} from "../common/AggregationLayerVaultBase.t.sol"; + +contract AdjustAllocationsPointsTest is AggregationLayerVaultBase { uint256 initialStrategyAllocationPoints = 500e18; function setUp() public virtual override { @@ -14,16 +19,16 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { function testAdjustAllocationPoints() public { uint256 newAllocationPoints = 859e18; - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST), newAllocationPoints); - Strategy memory strategy = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategy = aggregationLayerVault.getStrategy(address(eTST)); assertEq( - fourSixTwoSixAgg.totalAllocationPoints(), + aggregationLayerVault.totalAllocationPoints(), totalAllocationPointsBefore + (newAllocationPoints - initialStrategyAllocationPoints) ); assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore); @@ -35,7 +40,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { vm.startPrank(deployer); vm.expectRevert(); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST), newAllocationPoints); vm.stopPrank(); } @@ -44,7 +49,7 @@ contract AdjustAllocationsPointsTest is FourSixTwoSixAggBase { vm.startPrank(manager); vm.expectRevert(ErrorsLib.InactiveStrategy.selector); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST2), newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST2), newAllocationPoints); vm.stopPrank(); } } diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index fca77edb..f1ac33ff 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -2,10 +2,14 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy -} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract GulpTest is FourSixTwoSixAggBase { + AggregationLayerVaultBase, + AggregationLayerVault, + console2, + EVault, + Strategy +} from "../common/AggregationLayerVaultBase.t.sol"; + +contract GulpTest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; uint256 amountToDeposit = 10000e18; @@ -19,140 +23,142 @@ contract GulpTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedStrategyCash); } } function testGulpAfterNegativeYieldEqualToInterestLeft() public { - fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.AggregationVaultSavingRate memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + aggregationLayerVault.gulp(); + AggregationLayerVault.AggregationVaultSavingRate memory ers = + aggregationLayerVault.getAggregationVaultSavingRate(); + assertEq(aggregationLayerVault.interestAccrued(), 0); assertEq(ers.interestLeft, 0); vm.warp(block.timestamp + 2 days); - fourSixTwoSixAgg.gulp(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + aggregationLayerVault.gulp(); + assertEq(aggregationLayerVault.interestAccrued(), 0); vm.warp(block.timestamp + 1 days); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(aggregationLayerVault.interestAccrued(), 0); uint256 yield; { - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); } vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(aggregationLayerVault.interestAccrued(), 0); vm.warp(block.timestamp + 1 days); // interest per day 23.809523809523 - assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(aggregationLayerVault.interestAccrued(), 23809523809523809523); + aggregationLayerVault.gulp(); + ers = aggregationLayerVault.getAggregationVaultSavingRate(); assertEq(ers.interestLeft, yield - 23809523809523809523); // move close to end of smearing vm.warp(block.timestamp + 11 days); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + aggregationLayerVault.gulp(); + ers = aggregationLayerVault.getAggregationVaultSavingRate(); // mock a decrease of strategy balance by ers.interestLeft - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - ers.interestLeft; vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); } function testGulpAfterNegativeYieldBiggerThanInterestLeft() public { - fourSixTwoSixAgg.gulp(); - FourSixTwoSixAgg.AggregationVaultSavingRate memory ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + aggregationLayerVault.gulp(); + AggregationLayerVault.AggregationVaultSavingRate memory ers = + aggregationLayerVault.getAggregationVaultSavingRate(); + assertEq(aggregationLayerVault.interestAccrued(), 0); assertEq(ers.interestLeft, 0); vm.warp(block.timestamp + 2 days); - fourSixTwoSixAgg.gulp(); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + aggregationLayerVault.gulp(); + assertEq(aggregationLayerVault.interestAccrued(), 0); vm.warp(block.timestamp + 1 days); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(aggregationLayerVault.interestAccrued(), 0); uint256 yield; { - uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyShareBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyUnderlyingBalance = eTST.convertToAssets(aggrCurrentStrategyShareBalance); uint256 aggrNewStrategyUnderlyingBalance = aggrCurrentStrategyUnderlyingBalance * 11e17 / 1e18; yield = aggrNewStrategyUnderlyingBalance - aggrCurrentStrategyUnderlyingBalance; assetTST.mint(address(eTST), yield); - eTST.skim(type(uint256).max, address(fourSixTwoSixAgg)); + eTST.skim(type(uint256).max, address(aggregationLayerVault)); } vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); - assertEq(fourSixTwoSixAgg.interestAccrued(), 0); + assertEq(aggregationLayerVault.interestAccrued(), 0); vm.warp(block.timestamp + 1 days); // interest per day 23.809523809523 - assertEq(fourSixTwoSixAgg.interestAccrued(), 23809523809523809523); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + assertEq(aggregationLayerVault.interestAccrued(), 23809523809523809523); + aggregationLayerVault.gulp(); + ers = aggregationLayerVault.getAggregationVaultSavingRate(); assertEq(ers.interestLeft, yield - 23809523809523809523); // move close to end of smearing vm.warp(block.timestamp + 11 days); - fourSixTwoSixAgg.gulp(); - ers = fourSixTwoSixAgg.getAggregationVaultSavingRate(); + aggregationLayerVault.gulp(); + ers = aggregationLayerVault.getAggregationVaultSavingRate(); // mock a decrease of strategy balance by ers.interestLeft - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance - (ers.interestLeft * 2); vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); } } diff --git a/test/unit/HarvestTest.t.sol b/test/unit/HarvestTest.t.sol index 6b6efddb..4687df28 100644 --- a/test/unit/HarvestTest.t.sol +++ b/test/unit/HarvestTest.t.sol @@ -2,10 +2,14 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, FourSixTwoSixAgg, console2, EVault, Strategy -} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract HarvestTest is FourSixTwoSixAggBase { + AggregationLayerVaultBase, + AggregationLayerVault, + console2, + EVault, + Strategy +} from "../common/AggregationLayerVaultBase.t.sol"; + +contract HarvestTest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; uint256 amountToDeposit = 10000e18; @@ -19,76 +23,77 @@ contract HarvestTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() + * strategyBefore.allocationPoints / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedStrategyCash); } } function testHarvest() public { // no yield increase - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - uint256 totalAllocatedBefore = fourSixTwoSixAgg.totalAllocated(); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + uint256 totalAllocatedBefore = aggregationLayerVault.totalAllocated(); - assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) == strategyBefore.allocated); + assertTrue(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))) == strategyBefore.allocated); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated); - assertEq(fourSixTwoSixAgg.totalAllocated(), totalAllocatedBefore); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated); + assertEq(aggregationLayerVault.totalAllocated(), totalAllocatedBefore); // positive yield vm.warp(block.timestamp + 86400); // mock an increase of strategy balance by 10% - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalance * 11e17 / 1e18) ); - assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) > strategyBefore.allocated); - uint256 expectedAllocated = eTST.maxWithdraw(address(fourSixTwoSixAgg)); + assertTrue(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))) > strategyBefore.allocated); + uint256 expectedAllocated = eTST.maxWithdraw(address(aggregationLayerVault)); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, expectedAllocated); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, expectedAllocated); assertEq( - fourSixTwoSixAgg.totalAllocated(), totalAllocatedBefore + (expectedAllocated - strategyBefore.allocated) + aggregationLayerVault.totalAllocated(), + totalAllocatedBefore + (expectedAllocated - strategyBefore.allocated) ); } @@ -96,49 +101,49 @@ contract HarvestTest is FourSixTwoSixAggBase { vm.warp(block.timestamp + 86400); // mock a decrease of strategy balance by 10% - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalance * 9e17 / 1e18) ); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); + assertTrue(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))) < strategyBefore.allocated); vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); } function testHarvestNegativeYieldAndWithdrawSingleUser() public { vm.warp(block.timestamp + 86400); // mock a decrease of strategy balance by 10% - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance * 9e17 / 1e18; vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); - uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(fourSixTwoSixAgg)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + assertTrue(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))) < strategyBefore.allocated); + uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(aggregationLayerVault)); - uint256 user1SharesBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 user1SocializedLoss = user1SharesBefore * negativeYield / fourSixTwoSixAgg.totalSupply(); + uint256 user1SharesBefore = aggregationLayerVault.balanceOf(user1); + uint256 user1SocializedLoss = user1SharesBefore * negativeYield / aggregationLayerVault.totalSupply(); uint256 expectedUser1Assets = - user1SharesBefore * amountToDeposit / fourSixTwoSixAgg.totalSupply() - user1SocializedLoss; + user1SharesBefore * amountToDeposit / aggregationLayerVault.totalSupply() - user1SocializedLoss; uint256 user1AssetTSTBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); - fourSixTwoSixAgg.redeem(user1SharesBefore, user1, user1); + aggregationLayerVault.harvest(address(eTST)); + aggregationLayerVault.redeem(user1SharesBefore, user1, user1); vm.stopPrank(); - uint256 user1SharesAfter = fourSixTwoSixAgg.balanceOf(user1); + uint256 user1SharesAfter = aggregationLayerVault.balanceOf(user1); assertEq(user1SharesAfter, 0); assertApproxEqAbs(assetTST.balanceOf(user1), user1AssetTSTBalanceBefore + expectedUser1Assets, 1); @@ -150,42 +155,42 @@ contract HarvestTest is FourSixTwoSixAggBase { // deposit into aggregator { vm.startPrank(user2); - assetTST.approve(address(fourSixTwoSixAgg), user2InitialBalance); - fourSixTwoSixAgg.deposit(user2InitialBalance, user2); + assetTST.approve(address(aggregationLayerVault), user2InitialBalance); + aggregationLayerVault.deposit(user2InitialBalance, user2); vm.stopPrank(); } vm.warp(block.timestamp + 86400); // mock a decrease of strategy balance by 10% - uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(fourSixTwoSixAgg)); + uint256 aggrCurrentStrategyBalance = eTST.balanceOf(address(aggregationLayerVault)); uint256 aggrCurrentStrategyBalanceAfterNegYield = aggrCurrentStrategyBalance * 9e17 / 1e18; vm.mockCall( address(eTST), - abi.encodeWithSelector(EVault.balanceOf.selector, address(fourSixTwoSixAgg)), + abi.encodeWithSelector(EVault.balanceOf.selector, address(aggregationLayerVault)), abi.encode(aggrCurrentStrategyBalanceAfterNegYield) ); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); - assertTrue(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))) < strategyBefore.allocated); - uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(fourSixTwoSixAgg)); - uint256 user1SharesBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 user1SocializedLoss = user1SharesBefore * negativeYield / fourSixTwoSixAgg.totalSupply(); - uint256 user2SharesBefore = fourSixTwoSixAgg.balanceOf(user2); - uint256 user2SocializedLoss = user2SharesBefore * negativeYield / fourSixTwoSixAgg.totalSupply(); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); + assertTrue(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))) < strategyBefore.allocated); + uint256 negativeYield = strategyBefore.allocated - eTST.maxWithdraw(address(aggregationLayerVault)); + uint256 user1SharesBefore = aggregationLayerVault.balanceOf(user1); + uint256 user1SocializedLoss = user1SharesBefore * negativeYield / aggregationLayerVault.totalSupply(); + uint256 user2SharesBefore = aggregationLayerVault.balanceOf(user2); + uint256 user2SocializedLoss = user2SharesBefore * negativeYield / aggregationLayerVault.totalSupply(); uint256 expectedUser1Assets = user1SharesBefore * (amountToDeposit + user2InitialBalance) - / fourSixTwoSixAgg.totalSupply() - user1SocializedLoss; + / aggregationLayerVault.totalSupply() - user1SocializedLoss; uint256 expectedUser2Assets = user2SharesBefore * (amountToDeposit + user2InitialBalance) - / fourSixTwoSixAgg.totalSupply() - user2SocializedLoss; + / aggregationLayerVault.totalSupply() - user2SocializedLoss; vm.prank(user1); - fourSixTwoSixAgg.harvest(address(eTST)); + aggregationLayerVault.harvest(address(eTST)); - uint256 user1SharesAfter = fourSixTwoSixAgg.balanceOf(user1); - uint256 user1AssetsAfter = fourSixTwoSixAgg.convertToAssets(user1SharesAfter); - uint256 user2SharesAfter = fourSixTwoSixAgg.balanceOf(user2); - uint256 user2AssetsAfter = fourSixTwoSixAgg.convertToAssets(user2SharesAfter); + uint256 user1SharesAfter = aggregationLayerVault.balanceOf(user1); + uint256 user1AssetsAfter = aggregationLayerVault.convertToAssets(user1SharesAfter); + uint256 user2SharesAfter = aggregationLayerVault.balanceOf(user2); + uint256 user2AssetsAfter = aggregationLayerVault.convertToAssets(user2SharesAfter); assertApproxEqAbs(user1AssetsAfter, expectedUser1Assets, 1); assertApproxEqAbs(user2AssetsAfter, expectedUser2Assets, 1); diff --git a/test/unit/RebalanceTest.t.sol b/test/unit/RebalanceTest.t.sol index 23378e97..ca0f69fb 100644 --- a/test/unit/RebalanceTest.t.sol +++ b/test/unit/RebalanceTest.t.sol @@ -2,17 +2,17 @@ pragma solidity ^0.8.0; import { - FourSixTwoSixAggBase, - FourSixTwoSixAgg, + AggregationLayerVaultBase, + AggregationLayerVault, console2, EVault, IEVault, IRMTestDefault, TestERC20, Strategy -} from "../common/FourSixTwoSixAggBase.t.sol"; +} from "../common/AggregationLayerVaultBase.t.sol"; -contract RebalanceTest is FourSixTwoSixAggBase { +contract RebalanceTest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18; function setUp() public virtual override { @@ -29,40 +29,41 @@ contract RebalanceTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() * strategyBefore.allocationPoints + / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + expectedStrategyCash + (aggregationLayerVault.getStrategy(address(eTST))).allocated, + strategyBefore.allocated + expectedStrategyCash ); } @@ -71,45 +72,47 @@ contract RebalanceTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() * strategyBefore.allocationPoints + / aggregationLayerVault.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) + address(eTST), abi.encodeCall(eTST.maxDeposit, (address(aggregationLayerVault))), abi.encode(eTSTMaxDeposit) ); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTMaxDeposit); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTMaxDeposit); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated + eTSTMaxDeposit); + assertEq(aggregationLayerVault.totalAllocated(), eTSTMaxDeposit); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), eTSTMaxDeposit); + assertEq( + (aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated + eTSTMaxDeposit + ); } function testRebalanceByDepositingWhenToDepositIsGreaterThanCashAvailable() public { @@ -117,19 +120,19 @@ contract RebalanceTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -138,7 +141,7 @@ contract RebalanceTest is FourSixTwoSixAggBase { vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); // create new strategy & add it IEVault eTSTsecondary; @@ -155,29 +158,32 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into eTSTsecondary vm.warp(block.timestamp + 86400); { - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTSTsecondary)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTSTsecondary)); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated ); - uint256 targetCash = fourSixTwoSixAgg.totalAssetsAllocatable() - * fourSixTwoSixAgg.getStrategy(address(0)).allocationPoints / fourSixTwoSixAgg.totalAllocationPoints(); - uint256 currentCash = fourSixTwoSixAgg.totalAssetsAllocatable() - fourSixTwoSixAgg.totalAllocated(); + uint256 targetCash = aggregationLayerVault.totalAssetsAllocatable() + * aggregationLayerVault.getStrategy(address(0)).allocationPoints + / aggregationLayerVault.totalAllocationPoints(); + uint256 currentCash = + aggregationLayerVault.totalAssetsAllocatable() - aggregationLayerVault.totalAllocated(); uint256 expectedStrategyCash = currentCash - targetCash; vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTSTsecondary); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - // assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTsecondaryMaxDeposit); + // assertEq(aggregationLayerVault.totalAllocated(), eTSTsecondaryMaxDeposit); assertEq( - eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash + eTSTsecondary.convertToAssets(eTSTsecondary.balanceOf(address(aggregationLayerVault))), + expectedStrategyCash ); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTSTsecondary))).allocated, + (aggregationLayerVault.getStrategy(address(eTSTsecondary))).allocated, strategyBefore.allocated + expectedStrategyCash ); } @@ -188,42 +194,42 @@ contract RebalanceTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } // rebalance into strategy vm.warp(block.timestamp + 86400); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); uint256 eTSTMaxDeposit = 0; // mock max deposit vm.mockCall( - address(eTST), abi.encodeCall(eTST.maxDeposit, (address(fourSixTwoSixAgg))), abi.encode(eTSTMaxDeposit) + address(eTST), abi.encodeCall(eTST.maxDeposit, (address(aggregationLayerVault))), abi.encode(eTSTMaxDeposit) ); vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); - assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated); + assertEq(aggregationLayerVault.totalAllocated(), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); + assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated); } function testRebalanceByWithdrawing() public { @@ -231,19 +237,19 @@ contract RebalanceTest is FourSixTwoSixAggBase { // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -252,53 +258,52 @@ contract RebalanceTest is FourSixTwoSixAggBase { vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); // decrease allocation points uint256 newAllocationPoints = 300e18; vm.prank(manager); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST), newAllocationPoints); vm.warp(block.timestamp + 86400); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() * strategyBefore.allocationPoints + / aggregationLayerVault.totalAllocationPoints(); vm.prank(user1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); + assertEq(aggregationLayerVault.totalAllocated(), expectedStrategyCash); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), expectedStrategyCash); assertEq( - (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, + (aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated - (strategyBefore.allocated - expectedStrategyCash) ); } - /// TODO: update this test function testRebalanceByWithdrawingWhenToWithdrawIsGreaterThanMaxWithdraw() public { uint256 amountToDeposit = 10000e18; // deposit into aggregator { - uint256 balanceBefore = fourSixTwoSixAgg.balanceOf(user1); - uint256 totalSupplyBefore = fourSixTwoSixAgg.totalSupply(); - uint256 totalAssetsDepositedBefore = fourSixTwoSixAgg.totalAssetsDeposited(); + uint256 balanceBefore = aggregationLayerVault.balanceOf(user1); + uint256 totalSupplyBefore = aggregationLayerVault.totalSupply(); + uint256 totalAssetsDepositedBefore = aggregationLayerVault.totalAssetsDeposited(); uint256 userAssetBalanceBefore = assetTST.balanceOf(user1); vm.startPrank(user1); - assetTST.approve(address(fourSixTwoSixAgg), amountToDeposit); - fourSixTwoSixAgg.deposit(amountToDeposit, user1); + assetTST.approve(address(aggregationLayerVault), amountToDeposit); + aggregationLayerVault.deposit(amountToDeposit, user1); vm.stopPrank(); - assertEq(fourSixTwoSixAgg.balanceOf(user1), balanceBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalSupply(), totalSupplyBefore + amountToDeposit); - assertEq(fourSixTwoSixAgg.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); + assertEq(aggregationLayerVault.balanceOf(user1), balanceBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalSupply(), totalSupplyBefore + amountToDeposit); + assertEq(aggregationLayerVault.totalAssetsDeposited(), totalAssetsDepositedBefore + amountToDeposit); assertEq(assetTST.balanceOf(user1), userAssetBalanceBefore - amountToDeposit); } @@ -307,36 +312,38 @@ contract RebalanceTest is FourSixTwoSixAggBase { vm.prank(user1); address[] memory strategiesToRebalance = new address[](1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); // decrease allocation points uint256 newAllocationPoints = 300e18; vm.prank(manager); - fourSixTwoSixAgg.adjustAllocationPoints(address(eTST), newAllocationPoints); + aggregationLayerVault.adjustAllocationPoints(address(eTST), newAllocationPoints); vm.warp(block.timestamp + 86400); - Strategy memory strategyBefore = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyBefore = aggregationLayerVault.getStrategy(address(eTST)); - assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); + assertEq(eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated); - uint256 expectedStrategyCash = fourSixTwoSixAgg.totalAssetsAllocatable() * strategyBefore.allocationPoints - / fourSixTwoSixAgg.totalAllocationPoints(); + uint256 expectedStrategyCash = aggregationLayerVault.totalAssetsAllocatable() * strategyBefore.allocationPoints + / aggregationLayerVault.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) + address(eTST), + abi.encodeCall(eTST.maxWithdraw, (address(aggregationLayerVault))), + abi.encode(eTSTMaxWithdraw) ); vm.prank(user1); strategiesToRebalance[0] = address(eTST); - rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); + rebalancer.executeRebalance(address(aggregationLayerVault), strategiesToRebalance); - // assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated - eTSTMaxWithdraw); + // assertEq(aggregationLayerVault.totalAllocated(), strategyBefore.allocated - eTSTMaxWithdraw); // assertEq( - // eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated - eTSTMaxWithdraw + // eTST.convertToAssets(eTST.balanceOf(address(aggregationLayerVault))), strategyBefore.allocated - eTSTMaxWithdraw // ); - // assertEq((fourSixTwoSixAgg.getStrategy(address(eTST))).allocated, strategyBefore.allocated - eTSTMaxWithdraw); + // assertEq((aggregationLayerVault.getStrategy(address(eTST))).allocated, strategyBefore.allocated - eTSTMaxWithdraw); } } diff --git a/test/unit/RemoveStrategy.t.sol b/test/unit/RemoveStrategy.t.sol index ea12437d..3846ccd2 100644 --- a/test/unit/RemoveStrategy.t.sol +++ b/test/unit/RemoveStrategy.t.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault, Strategy} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract RemoveStrategyTest is FourSixTwoSixAggBase { +import { + AggregationLayerVaultBase, + AggregationLayerVault, + IEVault, + Strategy +} from "../common/AggregationLayerVaultBase.t.sol"; + +contract RemoveStrategyTest is AggregationLayerVaultBase { uint256 strategyAllocationPoints; IEVault anotherStrategy; @@ -16,17 +21,17 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { } function testRemoveStrategy() public { - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(address(eTST)); + aggregationLayerVault.removeStrategy(address(eTST)); - Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyAfter = aggregationLayerVault.getStrategy(address(eTST)); assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); - assertEq(fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); + assertEq(aggregationLayerVault.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore - 1); } @@ -36,17 +41,17 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { ); _addStrategy(manager, address(anotherStrategy), strategyAllocationPoints); - uint256 totalAllocationPointsBefore = fourSixTwoSixAgg.totalAllocationPoints(); + uint256 totalAllocationPointsBefore = aggregationLayerVault.totalAllocationPoints(); uint256 withdrawalQueueLengthBefore = _getWithdrawalQueueLength(); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(address(eTST)); + aggregationLayerVault.removeStrategy(address(eTST)); - Strategy memory strategyAfter = fourSixTwoSixAgg.getStrategy(address(eTST)); + Strategy memory strategyAfter = aggregationLayerVault.getStrategy(address(eTST)); assertEq(strategyAfter.active, false); assertEq(strategyAfter.allocationPoints, 0); - assertEq(fourSixTwoSixAgg.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); + assertEq(aggregationLayerVault.totalAllocationPoints(), totalAllocationPointsBefore - strategyAllocationPoints); assertEq(_getWithdrawalQueueLength(), withdrawalQueueLengthBefore - 1); } @@ -70,7 +75,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(withdrawalQueue[3], strategy3); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(strategy2); + aggregationLayerVault.removeStrategy(strategy2); withdrawalQueue = _getWithdrawalQueue(); assertEq(withdrawalQueue.length, 3); @@ -79,7 +84,7 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(withdrawalQueue[2], strategy3); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(strategy3); + aggregationLayerVault.removeStrategy(strategy3); withdrawalQueue = _getWithdrawalQueue(); assertEq(withdrawalQueue.length, 2); @@ -87,14 +92,14 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { assertEq(withdrawalQueue[1], strategy1); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(address(eTST)); + aggregationLayerVault.removeStrategy(address(eTST)); withdrawalQueue = _getWithdrawalQueue(); assertEq(withdrawalQueue.length, 1); assertEq(withdrawalQueue[0], strategy1); vm.prank(manager); - fourSixTwoSixAgg.removeStrategy(strategy1); + aggregationLayerVault.removeStrategy(strategy1); withdrawalQueue = _getWithdrawalQueue(); assertEq(withdrawalQueue.length, 0); @@ -103,12 +108,12 @@ contract RemoveStrategyTest is FourSixTwoSixAggBase { function testRemoveStrategy_fromUnauthorized() public { vm.prank(deployer); vm.expectRevert(); - fourSixTwoSixAgg.removeStrategy(address(eTST)); + aggregationLayerVault.removeStrategy(address(eTST)); } function testRemoveStrategy_AlreadyRemoved() public { vm.prank(manager); vm.expectRevert(); - fourSixTwoSixAgg.removeStrategy(address(eTST2)); + aggregationLayerVault.removeStrategy(address(eTST2)); } } diff --git a/test/unit/ReorderWithdrawalQueueTest.t.sol b/test/unit/ReorderWithdrawalQueueTest.t.sol index 019ee516..3251194e 100644 --- a/test/unit/ReorderWithdrawalQueueTest.t.sol +++ b/test/unit/ReorderWithdrawalQueueTest.t.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {FourSixTwoSixAggBase, FourSixTwoSixAgg, IEVault, WithdrawalQueue} from "../common/FourSixTwoSixAggBase.t.sol"; - -contract ReorderWithdrawalQueueTest is FourSixTwoSixAggBase { +import { + AggregationLayerVaultBase, + AggregationLayerVault, + IEVault, + WithdrawalQueue +} from "../common/AggregationLayerVaultBase.t.sol"; + +contract ReorderWithdrawalQueueTest is AggregationLayerVaultBase { uint256 eTSTAllocationPoints = 500e18; uint256 eTSTsecondaryAllocationPoints = 700e18; @@ -25,14 +30,18 @@ contract ReorderWithdrawalQueueTest is FourSixTwoSixAggBase { } function testReorderWithdrawalQueue() public { - assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTAllocationPoints); - assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[1]).allocationPoints, eTSTsecondaryAllocationPoints); + assertEq(aggregationLayerVault.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTAllocationPoints); + assertEq( + aggregationLayerVault.getStrategy(_getWithdrawalQueue()[1]).allocationPoints, eTSTsecondaryAllocationPoints + ); vm.prank(manager); withdrawalQueue.reorderWithdrawalQueue(0, 1); - assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTsecondaryAllocationPoints); - assertEq(fourSixTwoSixAgg.getStrategy(_getWithdrawalQueue()[1]).allocationPoints, eTSTAllocationPoints); + assertEq( + aggregationLayerVault.getStrategy(_getWithdrawalQueue()[0]).allocationPoints, eTSTsecondaryAllocationPoints + ); + assertEq(aggregationLayerVault.getStrategy(_getWithdrawalQueue()[1]).allocationPoints, eTSTAllocationPoints); } function testReorderWithdrawalQueueWhenOutOfBounds() public { From a4752948c874b40999e064bd21b322e5d77dc9dd Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:12:50 +0300 Subject: [PATCH 19/19] chore: renames --- src/AggregationLayerVaultFactory.sol | 8 ++++---- src/Dispatch.sol | 6 +++--- src/Shared.sol | 6 ++++-- src/{modules => module}/AllocationPoints.sol | 0 src/{modules => module}/Fee.sol | 0 src/{modules => module}/Hooks.sol | 0 src/{modules => module}/Rewards.sol | 0 src/{ => plugin}/Rebalancer.sol | 3 ++- src/{ => plugin}/WithdrawalQueue.sol | 2 +- test/common/AggregationLayerVaultBase.t.sol | 19 +++++++++++-------- test/e2e/BalanceForwarderE2ETest.t.sol | 2 +- 11 files changed, 26 insertions(+), 20 deletions(-) rename src/{modules => module}/AllocationPoints.sol (100%) rename src/{modules => module}/Fee.sol (100%) rename src/{modules => module}/Hooks.sol (100%) rename src/{modules => module}/Rewards.sol (100%) rename src/{ => plugin}/Rebalancer.sol (97%) rename src/{ => plugin}/WithdrawalQueue.sol (99%) diff --git a/src/AggregationLayerVaultFactory.sol b/src/AggregationLayerVaultFactory.sol index e105e3bd..fa22c4c0 100644 --- a/src/AggregationLayerVaultFactory.sol +++ b/src/AggregationLayerVaultFactory.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.0; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; // core modules -import {Rewards} from "./modules/Rewards.sol"; -import {Hooks} from "./modules/Hooks.sol"; -import {Fee} from "./modules/Fee.sol"; +import {Rewards} from "./module/Rewards.sol"; +import {Hooks} from "./module/Hooks.sol"; +import {Fee} from "./module/Fee.sol"; // core plugins -import {WithdrawalQueue} from "./WithdrawalQueue.sol"; +import {WithdrawalQueue} from "./plugin/WithdrawalQueue.sol"; import {AggregationLayerVault} from "./AggregationLayerVault.sol"; contract AggregationLayerVaultFactory { diff --git a/src/Dispatch.sol b/src/Dispatch.sol index f485d408..465c43d9 100644 --- a/src/Dispatch.sol +++ b/src/Dispatch.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later - pragma solidity ^0.8.0; +//contracts import {Shared} from "./Shared.sol"; -import {HooksModule} from "./modules/Hooks.sol"; -import {RewardsModule} from "./modules/Rewards.sol"; +import {HooksModule} from "./module/Hooks.sol"; +import {RewardsModule} from "./module/Rewards.sol"; abstract contract Dispatch is RewardsModule, HooksModule { address public immutable MODULE_REWARDS; diff --git a/src/Shared.sol b/src/Shared.sol index 8d8d9f2b..c87a970f 100644 --- a/src/Shared.sol +++ b/src/Shared.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {StorageLib, AggregationVaultStorage} from "./lib/StorageLib.sol"; -import {ErrorsLib} from "./lib/ErrorsLib.sol"; +// interfaces import {IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; +// libs +import {StorageLib, AggregationVaultStorage} from "./lib/StorageLib.sol"; +import {ErrorsLib} from "./lib/ErrorsLib.sol"; import {HooksLib} from "./lib/HooksLib.sol"; contract Shared { diff --git a/src/modules/AllocationPoints.sol b/src/module/AllocationPoints.sol similarity index 100% rename from src/modules/AllocationPoints.sol rename to src/module/AllocationPoints.sol diff --git a/src/modules/Fee.sol b/src/module/Fee.sol similarity index 100% rename from src/modules/Fee.sol rename to src/module/Fee.sol diff --git a/src/modules/Hooks.sol b/src/module/Hooks.sol similarity index 100% rename from src/modules/Hooks.sol rename to src/module/Hooks.sol diff --git a/src/modules/Rewards.sol b/src/module/Rewards.sol similarity index 100% rename from src/modules/Rewards.sol rename to src/module/Rewards.sol diff --git a/src/Rebalancer.sol b/src/plugin/Rebalancer.sol similarity index 97% rename from src/Rebalancer.sol rename to src/plugin/Rebalancer.sol index e783d700..86f4c42d 100644 --- a/src/Rebalancer.sol +++ b/src/plugin/Rebalancer.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import {IFourSixTwoSixAgg, Strategy} from "./interface/IFourSixTwoSixAgg.sol"; +// interfaces +import {IFourSixTwoSixAgg, Strategy} from "../interface/IFourSixTwoSixAgg.sol"; import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; contract Rebalancer { diff --git a/src/WithdrawalQueue.sol b/src/plugin/WithdrawalQueue.sol similarity index 99% rename from src/WithdrawalQueue.sol rename to src/plugin/WithdrawalQueue.sol index 9a409ef8..2349355b 100644 --- a/src/WithdrawalQueue.sol +++ b/src/plugin/WithdrawalQueue.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; // interfaces import {IERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; // internal dep -import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {IFourSixTwoSixAgg} from "../interface/IFourSixTwoSixAgg.sol"; // contracts import {AccessControlEnumerableUpgradeable} from "@openzeppelin-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; diff --git a/test/common/AggregationLayerVaultBase.t.sol b/test/common/AggregationLayerVaultBase.t.sol index 988011ec..a945e6f9 100644 --- a/test/common/AggregationLayerVaultBase.t.sol +++ b/test/common/AggregationLayerVaultBase.t.sol @@ -1,18 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +// interfaces +import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; +import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; +// contracts import "evk/test/unit/evault/EVaultTestBase.t.sol"; import {AggregationLayerVault, Strategy} from "../../src/AggregationLayerVault.sol"; -import {Rebalancer} from "../../src/Rebalancer.sol"; -import {IHookTarget} from "evk/src/interfaces/IHookTarget.sol"; -import {Hooks, HooksModule} from "../../src/modules/Hooks.sol"; -import {Rewards} from "../../src/modules/Rewards.sol"; -import {Fee} from "../../src/modules/Fee.sol"; +import {Rebalancer} from "../../src/plugin/Rebalancer.sol"; +import {Hooks, HooksModule} from "../../src/module/Hooks.sol"; +import {Rewards} from "../../src/module/Rewards.sol"; +import {Fee} from "../../src/module/Fee.sol"; import {AggregationLayerVaultFactory} from "../../src/AggregationLayerVaultFactory.sol"; -import {WithdrawalQueue} from "../../src/WithdrawalQueue.sol"; -import {IWithdrawalQueue} from "../../src/interface/IWithdrawalQueue.sol"; +import {WithdrawalQueue} from "../../src/plugin/WithdrawalQueue.sol"; +import {AllocationPoints} from "../../src/module/AllocationPoints.sol"; +// libs import {ErrorsLib} from "../../src/lib/ErrorsLib.sol"; -import {AllocationPoints} from "../../src/modules/AllocationPoints.sol"; contract AggregationLayerVaultBase is EVaultTestBase { uint256 public constant CASH_RESERVE_ALLOCATION_POINTS = 1000e18; diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index 3b6ea6fc..45fea881 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; +import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; import { AggregationLayerVaultBase, AggregationLayerVault, @@ -12,7 +13,6 @@ import { AggregationLayerVaultFactory, Rewards } from "../common/AggregationLayerVaultBase.t.sol"; -import {TrackingRewardStreams} from "reward-streams/TrackingRewardStreams.sol"; contract BalanceForwarderE2ETest is AggregationLayerVaultBase { uint256 user1InitialBalance = 100000e18;