diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a04ee8bc..16f20b19 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: version: nightly - name: Run foundry build - run: forge build + run: forge build --sizes - name: Run foundry fmt check run: forge fmt --check diff --git a/foundry.toml b/foundry.toml index da8c522e..518361a2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,7 @@ out = "out" libs = ["lib"] test = 'test' optimizer = true -optimizer_runs = 20_000 +optimizer_runs = 1000 # solc = "0.8.0" gas_reports = ["*"] fs_permissions = [{ access = "read", path = "./"}] diff --git a/src/FourSixTwoSixAgg.sol b/src/FourSixTwoSixAgg.sol index 000b4307..179af3b1 100644 --- a/src/FourSixTwoSixAgg.sol +++ b/src/FourSixTwoSixAgg.sol @@ -1,22 +1,25 @@ // SPDX-License-Identifier: GPL-2.0-or-later 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 {EVCUtil, IEVC} from "ethereum-vault-connector/utils/EVCUtil.sol"; -import {BalanceForwarder, IBalanceForwarder} from "./BalanceForwarder.sol"; import {IRewardStreams} from "reward-streams/interfaces/IRewardStreams.sol"; -import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol"; +// internal dep import {Hooks} from "./Hooks.sol"; +import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {BalanceForwarder, IBalanceForwarder} from "./BalanceForwarder.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 BalanceForwarder, EVCUtil, ERC4626, AccessControlEnumerable, Hooks { +contract FourSixTwoSixAgg is IFourSixTwoSixAgg, BalanceForwarder, EVCUtil, ERC4626, AccessControlEnumerable, Hooks { using SafeERC20 for IERC20; using SafeCast for uint256; @@ -41,16 +44,18 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn uint8 internal constant REENTRANCYLOCK__LOCKED = 2; // Roles - bytes32 public constant STRATEGY_MANAGER_ROLE = keccak256("STRATEGY_MANAGER_ROLE"); - bytes32 public constant STRATEGY_MANAGER_ROLE_ADMINROLE = keccak256("STRATEGY_MANAGER_ROLE_ADMINROLE"); - bytes32 public constant WITHDRAW_QUEUE_MANAGER_ROLE = keccak256("WITHDRAW_QUEUE_MANAGER_ROLE"); - bytes32 public constant WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE = keccak256("WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE"); - bytes32 public constant STRATEGY_ADDER_ROLE = keccak256("STRATEGY_ADDER_ROLE"); - bytes32 public constant STRATEGY_ADDER_ROLE_ADMINROLE = keccak256("STRATEGY_ADDER_ROLE_ADMINROLE"); - bytes32 public constant STRATEGY_REMOVER_ROLE = keccak256("STRATEGY_REMOVER_ROLE"); - bytes32 public constant STRATEGY_REMOVER_ROLE_ADMINROLE = keccak256("STRATEGY_REMOVER_ROLE_ADMINROLE"); - bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); - bytes32 public constant MANAGER_ROLE_ADMINROLE = keccak256("MANAGER_ROLE_ADMINROLE"); + 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"); + 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 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; @@ -82,25 +87,10 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn uint8 locked; } - /// @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; - } - 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 Rebalance( - address indexed strategy, uint256 currentAllocation, uint256 targetAllocation, uint256 amountToRebalance - ); event Gulp(uint256 interestLeft, uint256 interestSmearEnd); event Harvest(address indexed strategy, uint256 strategyBalanceAmount, uint256 strategyAllocatedAmount); event AdjustAllocationPoints(address indexed strategy, uint256 oldPoints, uint256 newPoints); @@ -109,14 +99,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn event RemoveStrategy(address indexed _strategy); event AccruePerformanceFee(address indexed feeRecipient, uint256 performanceFee, uint256 yield, uint256 feeShares); event SetStrategyCap(address indexed strategy, uint256 cap); - - /// @notice Modifier to require an account status check on the EVC. - /// @dev Calls `requireAccountStatusCheck` function from EVC for the specified account after the function body. - /// @param account The address of the account to check. - modifier requireAccountStatusCheck(address account) { - _; - evc.requireAccountStatusCheck(account); - } + event Rebalance(address indexed strategy, uint256 _amountToRebalance, bool _isDeposit); /// @dev Non reentrancy modifier for interest rate updates modifier nonReentrant() { @@ -176,16 +159,16 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); // Setup role admins - _setRoleAdmin(STRATEGY_MANAGER_ROLE, STRATEGY_MANAGER_ROLE_ADMINROLE); - _setRoleAdmin(WITHDRAW_QUEUE_MANAGER_ROLE, WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE); - _setRoleAdmin(STRATEGY_ADDER_ROLE, STRATEGY_ADDER_ROLE_ADMINROLE); - _setRoleAdmin(STRATEGY_REMOVER_ROLE, STRATEGY_REMOVER_ROLE_ADMINROLE); - _setRoleAdmin(MANAGER_ROLE, MANAGER_ROLE_ADMINROLE); + _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); } /// @notice Set performance fee recipient address /// @notice @param _newFeeRecipient Recipient address - function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER_ROLE) { + function setFeeRecipient(address _newFeeRecipient) external onlyRole(MANAGER) { if (_newFeeRecipient == feeRecipient) revert FeeRecipientAlreadySet(); emit SetFeeRecipient(feeRecipient, _newFeeRecipient); @@ -195,7 +178,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @notice Set performance fee (1e18 == 100%) /// @notice @param _newFee Fee rate - function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER_ROLE) { + function setPerformanceFee(uint256 _newFee) external onlyRole(MANAGER) { if (_newFee > MAX_PERFORMANCE_FEE) revert MaxPerformanceFeeExceeded(); if (feeRecipient == address(0)) revert FeeRecipientNotSet(); if (_newFee == performanceFee) revert PerformanceFeeAlreadySet(); @@ -207,7 +190,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @notice Opt in to strategy rewards /// @param _strategy Strategy address - function optInStrategyRewards(address _strategy) external onlyRole(MANAGER_ROLE) { + function optInStrategyRewards(address _strategy) external onlyRole(MANAGER) { if (!strategies[_strategy].active) revert InactiveStrategy(); IBalanceForwarder(_strategy).enableBalanceForwarder(); @@ -217,7 +200,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @notice Opt out of strategy rewards /// @param _strategy Strategy address - function optOutStrategyRewards(address _strategy) external onlyRole(MANAGER_ROLE) { + function optOutStrategyRewards(address _strategy) external onlyRole(MANAGER) { IBalanceForwarder(_strategy).disableBalanceForwarder(); emit OptOutStrategyRewards(_strategy); @@ -235,7 +218,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn address _reward, address _recipient, bool _forfeitRecentReward - ) external onlyRole(MANAGER_ROLE) { + ) external onlyRole(MANAGER) { address rewardStreams = IBalanceForwarder(_strategy).balanceTrackerAddress(); IRewardStreams(rewardStreams).claimReward(_rewarded, _reward, _recipient, _forfeitRecentReward); @@ -256,24 +239,6 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn _disableBalanceForwarder(_msgSender()); } - /// @notice Rebalance strategy's allocation. - /// @param _strategy Address of strategy to rebalance. - function rebalance(address _strategy) external nonReentrant { - _rebalance(_strategy); - - _gulp(); - } - - /// @notice Rebalance multiple strategies. - /// @param _strategies An array of strategy addresses to rebalance. - function rebalanceMultipleStrategies(address[] calldata _strategies) external nonReentrant { - for (uint256 i; i < _strategies.length; ++i) { - _rebalance(_strategies[i]); - } - - _gulp(); - } - /// @notice Harvest strategy. /// @param strategy address of strategy function harvest(address strategy) external nonReentrant { @@ -291,14 +256,36 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn _gulp(); } + function rebalance(address _strategy, uint256 _amountToRebalance, bool _isDeposit) + external + nonReentrant + onlyRole(REBALANCER) + { + Strategy memory strategyData = strategies[_strategy]; + + if (_isDeposit) { + // Do required approval (safely) and deposit + IERC20(asset()).safeApprove(_strategy, _amountToRebalance); + IERC4626(_strategy).deposit(_amountToRebalance, address(this)); + 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; + } + + emit 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_ROLE + /// @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 nonReentrant - onlyRole(STRATEGY_MANAGER_ROLE) + onlyRole(STRATEGY_MANAGER) { Strategy memory strategyDataCache = strategies[_strategy]; @@ -316,7 +303,7 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @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_ROLE) { + function setStrategyCap(address _strategy, uint256 _cap) external nonReentrant onlyRole(STRATEGY_MANAGER) { Strategy memory strategyDataCache = strategies[_strategy]; if (!strategyDataCache.active) { @@ -329,13 +316,13 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn } /// @notice Swap two strategies indexes in the withdrawal queue. - /// @dev Can only be called by an address that have the WITHDRAW_QUEUE_MANAGER_ROLE. + /// @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_ROLE) + onlyRole(WITHDRAW_QUEUE_MANAGER) { uint256 length = withdrawalQueue.length; if (_index1 >= length || _index2 >= length) { @@ -352,14 +339,10 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn } /// @notice Add new strategy with it's allocation points. - /// @dev Can only be called by an address that have STRATEGY_ADDER_ROLE. + /// @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_ROLE) - { + function addStrategy(address _strategy, uint256 _allocationPoints) external nonReentrant onlyRole(STRATEGY_ADDER) { if (IERC4626(_strategy).asset() != asset()) { revert InvalidStrategyAsset(); } @@ -381,9 +364,9 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @notice Remove strategy and set its allocation points to zero. /// @dev This function does not pull funds, `harvest()` needs to be called to withdraw - /// @dev Can only be called by an address that have the STRATEGY_REMOVER_ROLE + /// @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_ROLE) { + function removeStrategy(address _strategy) external nonReentrant onlyRole(STRATEGY_REMOVER) { if (_strategy == address(0)) revert CanNotRemoveCashReserve(); Strategy storage strategyStorage = strategies[_strategy]; @@ -455,14 +438,12 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn /// @param to The recipient of the transfer. /// @param amount The amount shares to transfer. /// @return A boolean indicating whether the transfer was successful. - function transfer(address to, uint256 amount) - public - override (ERC20, IERC20) - nonReentrant - requireAccountStatusCheck(_msgSender()) - returns (bool) - { - return super.transfer(to, amount); + function transfer(address to, uint256 amount) public override (ERC20, IERC20) nonReentrant returns (bool) { + super.transfer(to, amount); + + _requireAccountStatusCheck(_msgSender()); + + return true; } /// @notice Transfers a certain amount of tokens from a sender to a recipient. @@ -474,10 +455,13 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn public override (ERC20, IERC20) nonReentrant - requireAccountStatusCheck(from) returns (bool) { - return super.transferFrom(from, to, amount); + super.transferFrom(from, to, amount); + + _requireAccountStatusCheck(from); + + return true; } /// @dev See {IERC4626-deposit}. @@ -496,12 +480,13 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn public override nonReentrant - requireAccountStatusCheck(owner) returns (uint256 shares) { // Move interest to totalAssetsDeposited _updateInterestAccrued(); - return super.withdraw(assets, receiver, owner); + shares = super.withdraw(assets, receiver, owner); + + _requireAccountStatusCheck(owner); } /// @dev See {IERC4626-redeem}. @@ -510,19 +495,20 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn public override nonReentrant - requireAccountStatusCheck(owner) returns (uint256 assets) { // Move interest to totalAssetsDeposited _updateInterestAccrued(); - return super.redeem(shares, receiver, owner); + assets = super.redeem(shares, receiver, owner); + + _requireAccountStatusCheck(owner); } /// @notice Set hooks contract and hooked functions. /// @dev This funtion should be overriden to implement access control. /// @param _hookTarget Hooks contract. /// @param _hookedFns Hooked functions. - function setHooksConfig(address _hookTarget, uint32 _hookedFns) public override onlyRole(MANAGER_ROLE) { + function setHooksConfig(address _hookTarget, uint32 _hookedFns) public override onlyRole(MANAGER) { super.setHooksConfig(_hookTarget, _hookedFns); } @@ -645,80 +631,6 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn emit Gulp(esrSlotCache.interestLeft, esrSlotCache.interestSmearEnd); } - /// @notice Rebalance strategy allocation. - /// @dev This function will first harvest yield, gulps and update interest. - /// @dev If current allocation is greater than target allocation, the aggregator will withdraw the excess assets. - /// If current allocation is less than target allocation, the aggregator will: - /// - Try to deposit the delta, if the cash is not sufficient, deposit all the available cash - /// - If all the available cash is greater than the max deposit, deposit the max deposit - /// @param _strategy Address of strategy to rebalance. - function _rebalance(address _strategy) internal { - if (_strategy == address(0)) { - return; //nothing to rebalance as this is the cash reserve - } - - _callHookTarget(REBALANCE, _msgSender()); - - _harvest(_strategy); - - Strategy memory strategyData = strategies[_strategy]; - - // no rebalance if strategy have an allocated amount greater than cap - if ((strategyData.cap > 0) && (strategyData.allocated >= strategyData.cap)) return; - - uint256 totalAllocationPointsCache = totalAllocationPoints; - uint256 totalAssetsAllocatableCache = totalAssetsAllocatable(); - uint256 targetAllocation = - totalAssetsAllocatableCache * strategyData.allocationPoints / totalAllocationPointsCache; - - if ((strategyData.cap > 0) && (targetAllocation > strategyData.cap)) targetAllocation = strategyData.cap; - - uint256 amountToRebalance; - if (strategyData.allocated > targetAllocation) { - // Withdraw - amountToRebalance = strategyData.allocated - targetAllocation; - - uint256 maxWithdraw = IERC4626(_strategy).maxWithdraw(address(this)); - if (amountToRebalance > maxWithdraw) { - amountToRebalance = maxWithdraw; - } - - IERC4626(_strategy).withdraw(amountToRebalance, address(this), address(this)); - strategies[_strategy].allocated = (strategyData.allocated - amountToRebalance).toUint120(); - totalAllocated -= amountToRebalance; - } else if (strategyData.allocated < targetAllocation) { - // Deposit - uint256 targetCash = - totalAssetsAllocatableCache * strategies[address(0)].allocationPoints / totalAllocationPointsCache; - uint256 currentCash = totalAssetsAllocatableCache - totalAllocated; - - // Calculate available cash to put in strategies - uint256 cashAvailable = (currentCash > targetCash) ? currentCash - targetCash : 0; - - amountToRebalance = targetAllocation - strategyData.allocated; - if (amountToRebalance > cashAvailable) { - amountToRebalance = cashAvailable; - } - - uint256 maxDeposit = IERC4626(_strategy).maxDeposit(address(this)); - if (amountToRebalance > maxDeposit) { - amountToRebalance = maxDeposit; - } - - if (amountToRebalance == 0) { - return; // No cash to deposit - } - - // Do required approval (safely) and deposit - IERC20(asset()).safeApprove(_strategy, amountToRebalance); - IERC4626(_strategy).deposit(amountToRebalance, address(this)); - strategies[_strategy].allocated = uint120(strategyData.allocated + amountToRebalance); - totalAllocated += amountToRebalance; - } - - emit Rebalance(_strategy, strategyData.allocated, targetAllocation, amountToRebalance); - } - function _harvest(address _strategy) internal { Strategy memory strategyData = strategies[_strategy]; @@ -813,4 +725,11 @@ contract FourSixTwoSixAgg is BalanceForwarder, EVCUtil, ERC4626, AccessControlEn function _msgSender() internal view override (Context, EVCUtil) returns (address) { return EVCUtil._msgSender(); } + + /// @notice Function to require an account status check on the EVC. + /// @dev Calls `requireAccountStatusCheck` function from EVC for the specified account after the function body. + /// @param _account The address of the account to check. + function _requireAccountStatusCheck(address _account) private { + evc.requireAccountStatusCheck(_account); + } } diff --git a/src/Hooks.sol b/src/Hooks.sol index 17206686..c107df23 100644 --- a/src/Hooks.sol +++ b/src/Hooks.sol @@ -14,11 +14,10 @@ abstract contract Hooks { uint32 public constant DEPOSIT = 1 << 0; uint32 public constant WITHDRAW = 1 << 1; - uint32 public constant REBALANCE = 1 << 2; - uint32 public constant ADD_STRATEGY = 1 << 3; - uint32 public constant REMOVE_STRATEGY = 1 << 4; + uint32 public constant ADD_STRATEGY = 1 << 2; + uint32 public constant REMOVE_STRATEGY = 1 << 3; - uint32 constant ACTIONS_COUNTER = 1 << 5; + uint32 constant ACTIONS_COUNTER = 1 << 4; /// @dev Contract with hooks implementation address public hookTarget; diff --git a/src/Rebalancer.sol b/src/Rebalancer.sol new file mode 100644 index 00000000..2be3806f --- /dev/null +++ b/src/Rebalancer.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {IFourSixTwoSixAgg} from "./interface/IFourSixTwoSixAgg.sol"; +import {IERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol"; + +contract Rebalancer { + event ExecuteRebalance( + address indexed curatedVault, + address indexed strategy, + uint256 currentAllocation, + uint256 targetAllocation, + uint256 amountToRebalance + ); + + /// @notice Rebalance strategies allocation for a specific curated vault. + /// @param _curatedVault Curated vault address. + /// @param _strategies Strategies addresses. + function executeRebalance(address _curatedVault, address[] calldata _strategies) external { + for (uint256 i; i < _strategies.length; ++i) { + _rebalance(_curatedVault, _strategies[i]); + } + } + + /// @dev This function will first harvest yield, gulps and update interest. + /// @dev If current allocation is greater than target allocation, the aggregator will withdraw the excess assets. + /// If current allocation is less than target allocation, the aggregator will: + /// - Try to deposit the delta, if the cash is not sufficient, deposit all the available cash + /// - If all the available cash is greater than the max deposit, deposit the max deposit + /// @param _curatedVault Curated vault address. + /// @param _strategy Strategy address. + function _rebalance(address _curatedVault, address _strategy) private { + if (_strategy == address(0)) { + return; //nothing to rebalance as that's the cash reserve + } + + IFourSixTwoSixAgg(_curatedVault).harvest(_strategy); + + IFourSixTwoSixAgg.Strategy memory strategyData = IFourSixTwoSixAgg(_curatedVault).getStrategy(_strategy); + + // no rebalance if strategy have an allocated amount greater than cap + if ((strategyData.cap > 0) && (strategyData.allocated >= strategyData.cap)) return; + + uint256 totalAllocationPointsCache = IFourSixTwoSixAgg(_curatedVault).totalAllocationPoints(); + uint256 totalAssetsAllocatableCache = IFourSixTwoSixAgg(_curatedVault).totalAssetsAllocatable(); + uint256 targetAllocation = + totalAssetsAllocatableCache * strategyData.allocationPoints / totalAllocationPointsCache; + + if ((strategyData.cap > 0) && (targetAllocation > strategyData.cap)) targetAllocation = strategyData.cap; + + uint256 amountToRebalance; + bool isDeposit; + if (strategyData.allocated > targetAllocation) { + // Withdraw + amountToRebalance = strategyData.allocated - targetAllocation; + + uint256 maxWithdraw = IERC4626(_strategy).maxWithdraw(_curatedVault); + if (amountToRebalance > maxWithdraw) { + amountToRebalance = maxWithdraw; + } + + isDeposit = false; + } else if (strategyData.allocated < targetAllocation) { + // Deposit + uint256 targetCash = totalAssetsAllocatableCache + * IFourSixTwoSixAgg(_curatedVault).getStrategy(address(0)).allocationPoints / totalAllocationPointsCache; + uint256 currentCash = totalAssetsAllocatableCache - IFourSixTwoSixAgg(_curatedVault).totalAllocated(); + + // Calculate available cash to put in strategies + uint256 cashAvailable = (currentCash > targetCash) ? currentCash - targetCash : 0; + + amountToRebalance = targetAllocation - strategyData.allocated; + if (amountToRebalance > cashAvailable) { + amountToRebalance = cashAvailable; + } + + uint256 maxDeposit = IERC4626(_strategy).maxDeposit(_curatedVault); + if (amountToRebalance > maxDeposit) { + amountToRebalance = maxDeposit; + } + + if (amountToRebalance == 0) { + return; // No cash to deposit + } + + isDeposit = true; + } + + IFourSixTwoSixAgg(_curatedVault).rebalance(_strategy, amountToRebalance, isDeposit); + + emit ExecuteRebalance(_curatedVault, _strategy, strategyData.allocated, targetAllocation, amountToRebalance); + } +} diff --git a/src/interface/IFourSixTwoSixAgg.sol b/src/interface/IFourSixTwoSixAgg.sol new file mode 100644 index 00000000..b8323236 --- /dev/null +++ b/src/interface/IFourSixTwoSixAgg.sol @@ -0,0 +1,25 @@ +// 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; + } + + function rebalance(address _strategy, uint256 _amountToRebalance, bool _isDeposit) external; + function gulp() external; + function harvest(address strategy) external; + + function getStrategy(address _strategy) external view returns (Strategy memory); + function totalAllocationPoints() external view returns (uint256); + function totalAllocated() external view returns (uint256); + function totalAssetsAllocatable() external view returns (uint256); +} diff --git a/test/common/FourSixTwoSixAggBase.t.sol b/test/common/FourSixTwoSixAggBase.t.sol index b605561c..34e98c5a 100644 --- a/test/common/FourSixTwoSixAggBase.t.sol +++ b/test/common/FourSixTwoSixAggBase.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import "evk/test/unit/evault/EVaultTestBase.t.sol"; 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"; @@ -15,6 +16,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { address manager; FourSixTwoSixAgg fourSixTwoSixAgg; + Rebalancer rebalancer; function setUp() public virtual override { super.setUp(); @@ -24,6 +26,7 @@ contract FourSixTwoSixAggBase is EVaultTestBase { user2 = makeAddr("User_2"); vm.startPrank(deployer); + rebalancer = new Rebalancer(); fourSixTwoSixAgg = new FourSixTwoSixAgg( evc, address(0), @@ -36,18 +39,22 @@ contract FourSixTwoSixAggBase is EVaultTestBase { ); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ROLE_ADMINROLE(), 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); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ROLE(), 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)); vm.stopPrank(); } @@ -60,36 +67,33 @@ contract FourSixTwoSixAggBase is EVaultTestBase { assertEq(cashReserve.active, true); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE()), - fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE_ADMINROLE() - ); - assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE()), - fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE() + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_MANAGER()), + fourSixTwoSixAgg.STRATEGY_MANAGER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE()), - fourSixTwoSixAgg.STRATEGY_ADDER_ROLE_ADMINROLE() + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER()), + fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE()), - fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE_ADMINROLE() + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_ADDER()), fourSixTwoSixAgg.STRATEGY_ADDER_ADMIN() ); assertEq( - fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.MANAGER_ROLE()), fourSixTwoSixAgg.MANAGER_ROLE_ADMINROLE() + fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.STRATEGY_REMOVER()), + fourSixTwoSixAgg.STRATEGY_REMOVER_ADMIN() ); - - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE_ADMINROLE(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE_ADMINROLE(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE_ADMINROLE(), deployer)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER_ROLE_ADMINROLE(), deployer)); - - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE(), manager)); - assertTrue(fourSixTwoSixAgg.hasRole(fourSixTwoSixAgg.MANAGER_ROLE(), manager)); + assertEq(fourSixTwoSixAgg.getRoleAdmin(fourSixTwoSixAgg.MANAGER()), fourSixTwoSixAgg.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(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)); } function _addStrategy(address from, address strategy, uint256 allocationPoints) internal { diff --git a/test/e2e/BalanceForwarderE2ETest.t.sol b/test/e2e/BalanceForwarderE2ETest.t.sol index b327e1b4..75873f97 100644 --- a/test/e2e/BalanceForwarderE2ETest.t.sol +++ b/test/e2e/BalanceForwarderE2ETest.t.sol @@ -35,18 +35,18 @@ contract BalanceForwarderE2ETest is FourSixTwoSixAggBase { ); // grant admin roles to deployer - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE_ADMINROLE(), deployer); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ROLE_ADMINROLE(), 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); // grant roles to manager - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_MANAGER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.WITHDRAW_QUEUE_MANAGER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_ADDER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.STRATEGY_REMOVER_ROLE(), manager); - fourSixTwoSixAgg.grantRole(fourSixTwoSixAgg.MANAGER_ROLE(), 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); vm.stopPrank(); uint256 initialStrategyAllocationPoints = 500e18; diff --git a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol index 2da74da6..8aae2567 100644 --- a/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol +++ b/test/e2e/DepositRebalanceHarvestWithdrawE2ETest.t.sol @@ -55,7 +55,9 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); @@ -134,7 +136,9 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); @@ -233,7 +237,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - fourSixTwoSixAgg.rebalanceMultipleStrategies(strategiesToRebalance); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); @@ -319,7 +323,9 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); @@ -421,7 +427,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - fourSixTwoSixAgg.rebalanceMultipleStrategies(strategiesToRebalance); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); @@ -552,7 +558,7 @@ contract DepositRebalanceHarvestWithdrawE2ETest is FourSixTwoSixAggBase { strategiesToRebalance[0] = address(eTST); strategiesToRebalance[1] = address(eTSTsecondary); vm.prank(user1); - fourSixTwoSixAgg.rebalanceMultipleStrategies(strategiesToRebalance); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), expectedeTSTStrategyCash + expectedeTSTsecondaryStrategyCash); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedeTSTStrategyCash); diff --git a/test/e2e/HooksE2ETest.t.sol b/test/e2e/HooksE2ETest.t.sol index 62176738..efff9207 100644 --- a/test/e2e/HooksE2ETest.t.sol +++ b/test/e2e/HooksE2ETest.t.sol @@ -27,7 +27,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { function testSetHooksConfig() public { uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.REBALANCE() | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); vm.startPrank(manager); address hooksContract = address(new HooksContract()); @@ -42,7 +42,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { function testSetHooksConfigWithAddressZero() public { uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.REBALANCE() | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); vm.startPrank(manager); vm.expectRevert(Hooks.InvalidHooksTarget.selector); @@ -52,7 +52,7 @@ contract HooksE2ETest is FourSixTwoSixAggBase { function testSetHooksConfigWithNotHooksContract() public { uint32 expectedHookedFns = fourSixTwoSixAgg.DEPOSIT() | fourSixTwoSixAgg.WITHDRAW() - | fourSixTwoSixAgg.REBALANCE() | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); + | fourSixTwoSixAgg.ADD_STRATEGY() | fourSixTwoSixAgg.REMOVE_STRATEGY(); vm.startPrank(manager); address hooksContract = address(new NotHooksContract()); diff --git a/test/e2e/PerformanceFeeE2ETest.t.sol b/test/e2e/PerformanceFeeE2ETest.t.sol index 238dbb93..f8cec8a9 100644 --- a/test/e2e/PerformanceFeeE2ETest.t.sol +++ b/test/e2e/PerformanceFeeE2ETest.t.sol @@ -80,7 +80,9 @@ contract PerformanceFeeE2ETest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); diff --git a/test/e2e/StrategyCapE2ETest.t.sol b/test/e2e/StrategyCapE2ETest.t.sol index a364f8d0..e0235ecd 100644 --- a/test/e2e/StrategyCapE2ETest.t.sol +++ b/test/e2e/StrategyCapE2ETest.t.sol @@ -80,7 +80,9 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); @@ -98,7 +100,9 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { uint256 strategyAllocatedBefore = (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated; - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); vm.stopPrank(); assertEq(strategyAllocatedBefore, (fourSixTwoSixAgg.getStrategy(address(eTST))).allocated); @@ -141,7 +145,9 @@ contract StrategyCapE2ETest is FourSixTwoSixAggBase { fourSixTwoSixAgg.setStrategyCap(address(eTST), cap); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), cap); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), cap); diff --git a/test/unit/GulpTest.t.sol b/test/unit/GulpTest.t.sol index 3f6da94d..a31f4b92 100644 --- a/test/unit/GulpTest.t.sol +++ b/test/unit/GulpTest.t.sol @@ -44,7 +44,9 @@ contract GulpTest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); diff --git a/test/unit/HarvestTest.t.sol b/test/unit/HarvestTest.t.sol index c660dac4..7ee54b8d 100644 --- a/test/unit/HarvestTest.t.sol +++ b/test/unit/HarvestTest.t.sol @@ -44,7 +44,9 @@ contract HarvestTest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); diff --git a/test/unit/RebalanceTest.t.sol b/test/unit/RebalanceTest.t.sol index cbac406e..5342e9b0 100644 --- a/test/unit/RebalanceTest.t.sol +++ b/test/unit/RebalanceTest.t.sol @@ -54,7 +54,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + 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); @@ -100,7 +102,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { ); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTMaxDeposit); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), eTSTMaxDeposit); @@ -131,7 +135,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into first strategy vm.warp(block.timestamp + 86400); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); // create new strategy & add it IEVault eTSTsecondary; @@ -161,7 +167,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { uint256 expectedStrategyCash = currentCash - targetCash; vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTSTsecondary)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTSTsecondary); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); // assertEq(fourSixTwoSixAgg.totalAllocated(), eTSTsecondaryMaxDeposit); assertEq( @@ -208,7 +216,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { ); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), strategyBefore.allocated); @@ -239,7 +249,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); // decrease allocation points uint256 newAllocationPoints = 300e18; @@ -256,7 +268,8 @@ contract RebalanceTest is FourSixTwoSixAggBase { / fourSixTwoSixAgg.totalAllocationPoints(); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); assertEq(fourSixTwoSixAgg.totalAllocated(), expectedStrategyCash); assertEq(eTST.convertToAssets(eTST.balanceOf(address(fourSixTwoSixAgg))), expectedStrategyCash); @@ -291,7 +304,9 @@ contract RebalanceTest is FourSixTwoSixAggBase { // rebalance into strategy vm.warp(block.timestamp + 86400); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + address[] memory strategiesToRebalance = new address[](1); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); // decrease allocation points uint256 newAllocationPoints = 300e18; @@ -314,7 +329,8 @@ contract RebalanceTest is FourSixTwoSixAggBase { ); vm.prank(user1); - fourSixTwoSixAgg.rebalance(address(eTST)); + strategiesToRebalance[0] = address(eTST); + rebalancer.executeRebalance(address(fourSixTwoSixAgg), strategiesToRebalance); // assertEq(fourSixTwoSixAgg.totalAllocated(), strategyBefore.allocated - eTSTMaxWithdraw); // assertEq(