diff --git a/contracts/debtAllocators/YieldManager/Keeper.sol b/contracts/debtAllocators/YieldManager/Keeper.sol deleted file mode 100644 index 8a9e459..0000000 --- a/contracts/debtAllocators/YieldManager/Keeper.sol +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity 0.8.18; - -import {Governance} from "@periphery/utils/Governance.sol"; -import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol"; - -/// @notice Holds the `keeper` role of a V3 strategy so that a -/// multiple addresses can call report. -contract Keeper is Governance { - /// @notice Emitted when a strategy is removed. - event StrategyRemoved(address indexed strategy); - - /// @notice An event emitted when a keeper is added or removed. - event UpdateKeeper(address indexed keeper, bool allowed); - - /// @notice Emitted when a new strategy is added to the manager. - event StrategyAdded(address indexed strategy, address indexed owner); - - /// @notice Only the `_strategy` specific owner can call. - modifier onlyStrategyOwner(address _strategy) { - _isStrategyOwner(_strategy); - _; - } - - /// @notice Only the keepers can call. - modifier onlyKeepers() { - _isKeeper(); - _; - } - - /// @notice Checks if the msg sender is the owner of the strategy. - function _isStrategyOwner(address _strategy) internal view virtual { - require(strategyOwner[_strategy] == msg.sender, "!owner"); - } - - /// @notice Checks if the msg sender is a keeper. - function _isKeeper() internal view virtual { - require(keepers[msg.sender], "!keeper"); - } - - /// @notice Address check for keepers allowed to call. - mapping(address => bool) public keepers; - - /// @notice strategy address => struct with info. - mapping(address => address) public strategyOwner; - - constructor(address _governance) Governance(_governance) {} - - /** - * @notice Add a new strategy, using the current `management` as the owner. - * @param _strategy The address of the strategy. - */ - function addNewStrategy(address _strategy) external virtual onlyGovernance { - require(strategyOwner[_strategy] == address(0), "already active"); - require(IStrategy(_strategy).keeper() == address(this), "!keeper"); - - address currentManager = IStrategy(_strategy).management(); - - // Store the owner of the strategy. - strategyOwner[_strategy] = currentManager; - - emit StrategyAdded(_strategy, currentManager); - } - - /** - * @notice Updates the owner of a strategy. - * @param _strategy The address of the strategy. - * @param _newOwner The address of the new owner. - */ - function updateStrategyOwner( - address _strategy, - address _newOwner - ) external virtual onlyStrategyOwner(_strategy) { - require( - _newOwner != address(0) && - _newOwner != address(this) && - _newOwner != _strategy, - "bad address" - ); - strategyOwner[_strategy] = _newOwner; - } - - /** - * @notice Removes the strategy. - * @param _strategy The address of the strategy. - */ - function removeStrategy(address _strategy) external virtual { - // Only governance or the strategy owner can call. - if (msg.sender != governance) _isStrategyOwner(_strategy); - - delete strategyOwner[_strategy]; - - emit StrategyRemoved(_strategy); - } - - /** - * @notice Reports full profit for a strategy. - * @param _strategy The address of the strategy. - */ - function report(address _strategy) external virtual onlyKeepers { - // If the strategy has been added to the keeper. - if (strategyOwner[_strategy] != address(0)) { - // Report profits. - IStrategy(_strategy).report(); - } - } - - /** - * @notice Tends a strategy. - * @param _strategy The address of the strategy. - */ - function tend(address _strategy) external virtual onlyKeepers { - // If the strategy has been added to the keeper. - if (strategyOwner[_strategy] != address(0)) { - // Tend. - IStrategy(_strategy).tend(); - } - } - - /** - * @notice Set if a keeper can update debt. - * @param _address The address to set mapping for. - * @param _allowed If the address can call {update_debt}. - */ - function setKeeper( - address _address, - bool _allowed - ) external virtual onlyGovernance { - keepers[_address] = _allowed; - - emit UpdateKeeper(_address, _allowed); - } -} diff --git a/contracts/debtAllocators/YieldManager/YieldManager.sol b/contracts/debtAllocators/YieldManager/YieldManager.sol deleted file mode 100644 index 2671918..0000000 --- a/contracts/debtAllocators/YieldManager/YieldManager.sol +++ /dev/null @@ -1,478 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity 0.8.18; - -import {IVault} from "@yearn-vaults/interfaces/IVault.sol"; -import {AprOracle} from "@periphery/AprOracle/AprOracle.sol"; - -import {Keeper, Governance} from "./Keeper.sol"; -import {DebtAllocator} from "../DebtAllocator.sol"; - -/** - * @title YearnV3 Yield Yield Based Debt Allocator - * @author yearn.finance - * @notice - * This Debt Allocator is meant to be used alongside - * a Yearn V3 vault to allocate funds to the optimal strategy. - */ -contract YieldManager is Governance { - /// @notice Emitted when the open flag is updated. - event UpdateOpen(bool status); - - /// @notice Emitted when a proposer status is updated. - event UpdateProposer(address indexed proposer, bool status); - - /// @notice Emitted when a vaults status is updated. - event UpdateVaultAllocator(address indexed vault, address allocator); - - // Struct that contains the address of the strategy and its best allocation. - struct Allocation { - // Address of the strategy. - address strategy; - // Debt for the strategy to end with. - // Can take 79 Billion 18 decimal tokens. - uint96 newDebt; - } - - /// @notice Only allow the sender to be an allocator if not opened. - modifier onlyProposersOrOpen() { - _isProposerOrOpen(); - _; - } - - /// @notice Check if it has been opened or is an allocator. - function _isProposerOrOpen() internal view { - require(proposer[msg.sender] || open, "!allocator or open"); - } - - uint256 internal constant MAX_BPS = 10_000; - - /// @notice Contract that holds the logic and oracles for each strategy. - AprOracle internal constant aprOracle = - AprOracle(0x27aD2fFc74F74Ed27e1C0A19F1858dD0963277aE); - - /// @notice Flag to set to allow anyone to propose allocations. - bool public open; - - /// @notice Address that should hold the strategies `management` role. - address public immutable keeper; - - /// @notice Addresses that are allowed to propose allocations. - mapping(address => bool) public proposer; - - /// @notice Mapping for vaults that can be allocated for => its debt allocator. - mapping(address => address) public vaultAllocator; - - constructor(address _governance, address _keeper) Governance(_governance) { - keeper = _keeper; - } - - /** - * @notice Update a `_vault`s target allocation of debt. - * @dev This takes the address of a vault and an array of - * its strategies and their specific target allocations. - * - * The `_newAllocations` array should: - * - Contain all strategies that hold any amount of debt from the vault - * even if the debt wont be adjusted in order to get the correct - * on chain rate. - * - Be ordered so that all debt decreases are at the beginning of the array - * and debt increases at the end. - * - Account for all limiting values such as the vaults max_debt and min_total_idle - * as well as the strategies maxDeposit/maxRedeem that are enforced on debt updates. - * - Account for the expected differences in amounts caused by unrealised losses or profits. - * - * @param _vault The address of the vault to propose an allocation for. - * @param _newAllocations Array of strategies and their new proposed allocation. - * @return _currentRate The current weighted rate that the collective strategies are earning. - * @return _expectedRate The expected weighted rate that the collective strategies would earn. - */ - function updateAllocation( - address _vault, - Allocation[] memory _newAllocations - ) - external - virtual - onlyProposersOrOpen - returns (uint256 _currentRate, uint256 _expectedRate) - { - address allocator = vaultAllocator[_vault]; - require(allocator != address(0), "vault not added"); - - // Get the total assets the vault has. - uint256 _totalAssets = IVault(_vault).totalAssets(); - - // If 0 nothing to do. - if (_totalAssets == 0) return (0, 0); - - // Always first account for the amount idle in the vault. - uint256 _accountedFor = IVault(_vault).totalIdle(); - // Create local variables used through loops. - address _strategy; - uint256 _currentDebt; - uint256 _newDebt; - uint256 _strategyRate; - uint256 _targetRatio; - for (uint256 i = 0; i < _newAllocations.length; ++i) { - _strategy = _newAllocations[i].strategy; - _newDebt = uint256(_newAllocations[i].newDebt); - // Get the debt the strategy current has. - _currentDebt = IVault(_vault).strategies(_strategy).current_debt; - // Add to what we have accounted for. - _accountedFor += _currentDebt; - - // Get the current weighted rate the strategy is earning - _strategyRate = (aprOracle.getStrategyApr(_strategy, 0) * - _currentDebt); - - // Add to the amount currently being earned. - _currentRate += _strategyRate; - - // If we are withdrawing. - if (_currentDebt > _newDebt) { - // If we are pulling all debt from a strategy. - if (_newDebt == 0) { - // Try to report profits to have them start to unlock. - Keeper(keeper).report(_strategy); - } - - if ( - // We cannot decrease debt if the strategy has any unrealised losses. - IVault(_vault).assess_share_of_unrealised_losses( - _strategy, - _currentDebt - ) != 0 - ) { - // Realize the loss. - (, uint256 _loss) = IVault(_vault).process_report( - _strategy - ); - // Update balances. - _currentDebt -= _loss; - _totalAssets -= _loss; - _accountedFor -= _loss; - } - - // Make sure the vault can withdraw that amount. - require( - _maxWithdraw(_vault, _strategy) >= _currentDebt - _newDebt, - "max withdraw" - ); - } else if (_currentDebt < _newDebt) { - // Make sure the strategy is allowed that much. - require( - IVault(_vault).strategies(_strategy).max_debt >= _newDebt, - "max debt" - ); - // Make sure the vault can deposit the desired amount. - require( - IVault(_strategy).maxDeposit(_vault) >= - _newDebt - _currentDebt, - "max deposit" - ); - } - - // Get the target based on the new debt. - _targetRatio = _newDebt < _totalAssets - ? (_newDebt * MAX_BPS) / _totalAssets - : MAX_BPS; - - // If different than the current target. - if ( - DebtAllocator(allocator).getStrategyTargetRatio(_strategy) != - _targetRatio - ) { - // Update allocation. - DebtAllocator(allocator).setStrategyDebtRatio( - _strategy, - _targetRatio - ); - } - - // If the new and current debt are the same. - if (_newDebt == _currentDebt) { - // We assume the new rate will be the same as current. - _expectedRate += _strategyRate; - } else if (_newDebt != 0) { - _expectedRate += (aprOracle.getStrategyApr( - _strategy, - int256(_newDebt) - int256(_currentDebt) // Debt change. - ) * _newDebt); - } - } - - // Make sure the minimum_total_idle was respected. - _checkMinimumTotalIdle(_vault, allocator); - // Make sure the ending amounts are the same otherwise rates could be wrong. - require(_totalAssets == _accountedFor, "cheater"); - // Make sure we expect to earn more than we currently are. - require(_expectedRate > _currentRate, "fail"); - } - - /** - * @notice Validates that all assets of a vault are accounted for in - * the proposed allocation array. - * - * If not the APR calculation will not be correct. - * - * @param _vault The address of the vault to propose an allocation for. - * @param _newAllocations Array of strategies and their new proposed allocation. - */ - function validateAllocation( - address _vault, - Allocation[] calldata _newAllocations - ) external view virtual returns (bool) { - // Get the total assets the vault has. - uint256 _totalAssets = IVault(_vault).totalAssets(); - - // If 0 nothing to do. - if (_totalAssets == 0) return false; - - // Always first account for the amount idle in the vault. - uint256 _accountedFor = IVault(_vault).totalIdle(); - for (uint256 i = 0; i < _newAllocations.length; ++i) { - // Add the debt for each strategy in the array. - _accountedFor += IVault(_vault) - .strategies(_newAllocations[i].strategy) - .current_debt; - } - - // Make sure the ending amounts are the same. - return _totalAssets == _accountedFor; - } - - /** - * @notice Get the current weighted yield rate the vault is earning - * and the expected rate based on the proposed changes. - * - * Must divide by the totalAssets to get the APR as 1e18. - * - * @param _vault The address of the vault to propose an allocation for. - * @param _newAllocations Array of strategies and their new proposed allocation. - * @return _currentRate The current weighted rate that the collective strategies are earning. - * @return _expectedRate The expected weighted rate that the collective strategies would earn. - */ - function getCurrentAndExpectedRate( - address _vault, - Allocation[] calldata _newAllocations - ) - external - view - virtual - returns (uint256 _currentRate, uint256 _expectedRate) - { - // Get the total assets the vault has. - uint256 _totalAssets = IVault(_vault).totalAssets(); - - // If 0 nothing to do. - if (_totalAssets == 0) return (0, 0); - - uint256 _newDebt; - address _strategy; - uint256 _currentDebt; - uint256 _strategyRate; - for (uint256 i = 0; i < _newAllocations.length; ++i) { - _newDebt = uint256(_newAllocations[i].newDebt); - _strategy = _newAllocations[i].strategy; - _currentDebt = IVault(_vault).strategies(_strategy).current_debt; - - // Get the current weighted rate the strategy is earning - _strategyRate = (aprOracle.getStrategyApr(_strategy, 0) * - _currentDebt); - - // Add to the amount currently being earned. - _currentRate += _strategyRate; - - // If the strategies debt is not changing. - if (_currentDebt == _newDebt) { - // No need to call the APR oracle again. - _expectedRate += _strategyRate; - } else { - // We add the expected rate with the new debt. - _expectedRate += (aprOracle.getStrategyApr( - _strategy, - int256(_newDebt) - int256(_currentDebt) - ) * _newDebt); - } - } - } - - /** - * @notice Update a `_vault`s allocation of debt. - * @dev This takes the address of a vault and an array of - * its strategies and their specific allocation. - * - * The `_newAllocations` array should: - * - Contain all strategies that hold any amount of debt from the vault - * even if the debt wont be adjusted in order to get the correct - * on chain rate. - * - Be ordered so that all debt decreases are at the beginning of the array - * and debt increases at the end. - * - Account for all limiting values such as the vaults max_debt and min_total_idle - * as well as the strategies maxDeposit/maxRedeem that are enforced on debt updates. - * - Account for the expected differences in amounts caused by unrealised losses or profits. - * - * This will not do any APR checks and assumes the sender has completed - * any and all necessary checks before sending. - * - * @param _vault The address of the vault to propose an allocation for. - * @param _newAllocations Array of strategies and their new proposed allocation. - */ - function updateAllocationPermissioned( - address _vault, - Allocation[] calldata _newAllocations - ) external virtual onlyGovernance { - address allocator = vaultAllocator[_vault]; - require(allocator != address(0), "vault not added"); - address _strategy; - uint256 _newDebt; - uint256 _currentDebt; - uint256 _targetRatio; - uint256 _totalAssets = IVault(_vault).totalAssets(); - for (uint256 i = 0; i < _newAllocations.length; ++i) { - _strategy = _newAllocations[i].strategy; - _newDebt = uint256(_newAllocations[i].newDebt); - // Get the debt the strategy current has. - _currentDebt = IVault(_vault).strategies(_strategy).current_debt; - - // If we are withdrawing. - if (_currentDebt > _newDebt) { - // If we are pulling all debt from a strategy. - if (_newDebt == 0) { - // We need to report profits and have them immediately unlock to not lose out on locked profit. - Keeper(keeper).report(_strategy); - } - - if ( - // We cannot decrease debt if the strategy has any unrealised losses. - IVault(_vault).assess_share_of_unrealised_losses( - _strategy, - _currentDebt - ) != 0 - ) { - // Realize the loss. - (, uint256 _loss) = IVault(_vault).process_report( - _strategy - ); - // Update balances. - _currentDebt -= _loss; - _totalAssets -= _loss; - } - - // Make sure the vault can withdraw that amount. - require( - _maxWithdraw(_vault, _strategy) >= _currentDebt - _newDebt, - "max withdraw" - ); - } else if (_currentDebt < _newDebt) { - // Make sure the strategy is allowed that much. - require( - IVault(_vault).strategies(_strategy).max_debt >= _newDebt, - "max debt" - ); - // Make sure the vault can deposit the desired amount. - require( - IVault(_strategy).maxDeposit(_vault) >= - _newDebt - _currentDebt, - "max deposit" - ); - } - - // Get the target based on the new debt. - _targetRatio = _newDebt < _totalAssets - ? (_newDebt * MAX_BPS) / _totalAssets - : MAX_BPS; - - if ( - DebtAllocator(allocator).getStrategyTargetRatio(_strategy) != - _targetRatio - ) { - // Update allocation. - DebtAllocator(allocator).setStrategyDebtRatio( - _strategy, - _targetRatio - ); - } - } - } - - /** - * @dev Helper function to get the max a vault can withdraw from a strategy to - * avoid stack to deep. - * - * Uses maxRedeem and convertToAssets since that is what the vault uses. - */ - function _maxWithdraw( - address _vault, - address _strategy - ) internal view virtual returns (uint256) { - return - IVault(_strategy).convertToAssets( - IVault(_strategy).maxRedeem(_vault) - ); - } - - /** - * @dev Helper function to check that the minimum_total_idle of the vault - * is accounted for in the allocation given. - * - * The expected Rate could be wrong if it allocated funds not allowed to be deployed. - * - * Use a separate function to avoid stack to deep. - */ - function _checkMinimumTotalIdle( - address _vault, - address _allocator - ) internal view virtual { - uint256 totalRatio = DebtAllocator(_allocator).totalDebtRatio(); - uint256 minIdle = IVault(_vault).minimum_total_idle(); - - // No need if minIdle is 0. - if (minIdle != 0) { - // Make sure we wouldn't allocate more than allowed. - require( - // Use 1e18 precision for more exact checks. - 1e18 - (1e14 * totalRatio) >= - (minIdle * 1e18) / IVault(_vault).totalAssets(), - "min idle" - ); - } - } - - /** - * @notice Sets the permission for a proposer. - * @param _address The address of the proposer. - * @param _allowed The permission to set for the proposer. - */ - function setProposer( - address _address, - bool _allowed - ) external virtual onlyGovernance { - proposer[_address] = _allowed; - - emit UpdateProposer(_address, _allowed); - } - - /** - * @notice Sets the mapping of vaults allowed. - * @param _vault The address of the _vault. - * @param _allocator The vault specific debt allocator. - */ - function setVaultAllocator( - address _vault, - address _allocator - ) external virtual onlyGovernance { - vaultAllocator[_vault] = _allocator; - - emit UpdateVaultAllocator(_vault, _allocator); - } - - /** - * @notice Sets the open status of the contract. - * @param _open The new open status to set. - */ - function setOpen(bool _open) external virtual onlyGovernance { - open = _open; - - emit UpdateOpen(_open); - } -} diff --git a/scripts/deploy_yield_manager.py b/scripts/deploy_yield_manager.py deleted file mode 100644 index 796dc53..0000000 --- a/scripts/deploy_yield_manager.py +++ /dev/null @@ -1,45 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_yield_manager(): - - print("Deploying Yield Manager on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - yield_manager = project.YieldManager - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Yield Manager") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - print(f"Deploying the Yield Manager...") - print("Enter the addresses to use on deployment.") - - gov = input("Governance? ") - keeper = input("Keeper? ") - - constructor = yield_manager.constructor.encode_input( - gov, - keeper, - ) - - deploy_bytecode = HexBytes( - HexBytes(yield_manager.contract_type.deployment_bytecode.bytecode) + constructor - ) - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print(f"Encoded Constructor to use for verifaction {constructor.hex()[2:]}") - - -def main(): - deploy_yield_manager() diff --git a/tests/conftest.py b/tests/conftest.py index 2da7a4e..e4bc436 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -460,45 +460,3 @@ def role_manager( ) return role_manager - - -@pytest.fixture(scope="session") -def deploy_keeper(project, daddy): - def deploy_keeper(): - keeper = daddy.deploy(project.Keeper, daddy) - - return keeper - - yield deploy_keeper - - -@pytest.fixture(scope="session") -def keeper(deploy_keeper): - keeper = deploy_keeper() - - yield keeper - - -@pytest.fixture(scope="session") -def deploy_yield_manager(project, daddy, keeper): - def deploy_yield_manager(): - yield_manager = daddy.deploy(project.YieldManager, daddy, keeper) - - return yield_manager - - yield deploy_yield_manager - - -@pytest.fixture(scope="session") -def yield_manager(deploy_yield_manager): - yield_manager = deploy_yield_manager() - - yield yield_manager - - -@pytest.fixture(scope="session") -def apr_oracle(project): - oracle = project.MockOracle - address = "0x27aD2fFc74F74Ed27e1C0A19F1858dD0963277aE" - networks.provider.set_code(address, oracle.contract_type.runtime_bytecode.bytecode) - yield oracle.at(address) diff --git a/tests/debtAllocators/yield/test_keeper_contract.py b/tests/debtAllocators/yield/test_keeper_contract.py deleted file mode 100644 index 3ae3561..0000000 --- a/tests/debtAllocators/yield/test_keeper_contract.py +++ /dev/null @@ -1,164 +0,0 @@ -import ape -from ape import chain, project -from utils.constants import ZERO_ADDRESS, MAX_INT, ROLES - - -def test_keeper_setup(keeper, mock_tokenized, daddy, yield_manager): - assert keeper.strategyOwner(mock_tokenized) == ZERO_ADDRESS - assert keeper.keepers(yield_manager) == False - assert keeper.governance() == daddy - - -def test_add_new_strategy(keeper, mock_tokenized, daddy, yield_manager, management): - mock_tokenized.setKeeper(daddy, sender=management) - assert keeper.strategyOwner(mock_tokenized) == ZERO_ADDRESS - assert keeper.keepers(yield_manager) == False - - with ape.reverts("!governance"): - keeper.addNewStrategy(mock_tokenized, sender=management) - - with ape.reverts("!keeper"): - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - mock_tokenized.setKeeper(keeper, sender=management) - - tx = keeper.addNewStrategy(mock_tokenized, sender=daddy) - - assert mock_tokenized.keeper() == keeper - - event = list(tx.decode_logs(keeper.StrategyAdded))[0] - - assert event.strategy == mock_tokenized - assert event.owner == management - - assert keeper.strategyOwner(mock_tokenized) == management - - # cannot add it again - with ape.reverts("already active"): - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - -def test_remove_strategy(keeper, mock_tokenized, management, user, daddy): - # Will revert on modifier if not yet added. - with ape.reverts("!owner"): - keeper.removeStrategy(mock_tokenized, sender=management) - - mock_tokenized.setKeeper(keeper, sender=management) - - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - assert mock_tokenized.keeper() == keeper - assert keeper.strategyOwner(mock_tokenized) == management - - with ape.reverts("!owner"): - keeper.removeStrategy(mock_tokenized, sender=user) - - tx = keeper.removeStrategy(mock_tokenized, sender=management) - - event = list(tx.decode_logs(keeper.StrategyRemoved))[0] - - assert event.strategy == mock_tokenized - - assert keeper.strategyOwner(mock_tokenized) == ZERO_ADDRESS - assert mock_tokenized.management() == management - - -def test_update_owner(keeper, mock_tokenized, yield_manager, management, user, daddy): - with ape.reverts("!owner"): - keeper.updateStrategyOwner(mock_tokenized, user, sender=management) - - mock_tokenized.setKeeper(keeper, sender=management) - - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - assert mock_tokenized.keeper() == keeper - assert keeper.strategyOwner(mock_tokenized) == management - - with ape.reverts("!owner"): - keeper.updateStrategyOwner(mock_tokenized, user, sender=user) - - with ape.reverts("bad address"): - keeper.updateStrategyOwner(mock_tokenized, ZERO_ADDRESS, sender=management) - - with ape.reverts("bad address"): - keeper.updateStrategyOwner(mock_tokenized, mock_tokenized, sender=management) - - with ape.reverts("bad address"): - keeper.updateStrategyOwner(mock_tokenized, keeper, sender=management) - - keeper.updateStrategyOwner(mock_tokenized, user, sender=management) - - assert keeper.strategyOwner(mock_tokenized) == user - assert mock_tokenized.keeper() == keeper - - -def test_report(keeper, mock_tokenized, management, yield_manager, asset, user, daddy): - mock_tokenized.setKeeper(keeper, sender=management) - - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - assert mock_tokenized.keeper() == keeper - assert keeper.strategyOwner(mock_tokenized) == management - - # deposit into the strategy - to_deposit = asset.balanceOf(user) // 2 - profit = asset.balanceOf(user) - to_deposit - - asset.approve(mock_tokenized, to_deposit, sender=user) - mock_tokenized.deposit(to_deposit, user, sender=user) - - assert mock_tokenized.totalAssets() == to_deposit - assert mock_tokenized.totalSupply() == to_deposit - - # simulate profit - asset.transfer(mock_tokenized, profit, sender=user) - - assert mock_tokenized.totalAssets() == to_deposit - assert mock_tokenized.totalSupply() == to_deposit - current_unlock_time = mock_tokenized.profitMaxUnlockTime() - assert current_unlock_time == 0 - assert mock_tokenized.pricePerShare() == 10 ** asset.decimals() - - with ape.reverts("!keeper"): - keeper.report(mock_tokenized, sender=yield_manager) - - keeper.setKeeper(yield_manager, True, sender=daddy) - - keeper.report(mock_tokenized, sender=yield_manager) - - # Profit should be fully unlocked - assert mock_tokenized.totalAssets() == to_deposit + profit - assert mock_tokenized.totalSupply() == to_deposit - assert mock_tokenized.pricePerShare() > 10 ** asset.decimals() - - -def test_tend(keeper, mock_tokenized, management, yield_manager, asset, user, daddy): - mock_tokenized.setKeeper(keeper, sender=management) - - keeper.addNewStrategy(mock_tokenized, sender=daddy) - - assert mock_tokenized.keeper() == keeper - assert keeper.strategyOwner(mock_tokenized) == management - - # deposit into the strategy - to_deposit = asset.balanceOf(user) // 2 - profit = asset.balanceOf(user) - to_deposit - - asset.approve(mock_tokenized, to_deposit, sender=user) - mock_tokenized.deposit(to_deposit, user, sender=user) - - assert mock_tokenized.totalAssets() == to_deposit - assert mock_tokenized.totalSupply() == to_deposit - - # simulate profit - asset.transfer(mock_tokenized, profit, sender=user) - - assert mock_tokenized.totalAssets() == to_deposit - assert mock_tokenized.totalSupply() == to_deposit - - with ape.reverts("!keeper"): - keeper.tend(mock_tokenized, sender=yield_manager) - - keeper.setKeeper(yield_manager, True, sender=daddy) - - keeper.tend(mock_tokenized, sender=yield_manager) diff --git a/tests/debtAllocators/yield/test_yield_manager.py b/tests/debtAllocators/yield/test_yield_manager.py deleted file mode 100644 index dbac718..0000000 --- a/tests/debtAllocators/yield/test_yield_manager.py +++ /dev/null @@ -1,935 +0,0 @@ -import ape -from ape import chain, project, networks -from utils.constants import ZERO_ADDRESS, MAX_INT, ROLES, MAX_BPS - - -def setup_vault(vault, strategies, oracle, chad): - for strategy in strategies: - vault.add_strategy(strategy, sender=chad) - vault.update_max_debt_for_strategy(strategy, 2**256 - 1, sender=chad) - management = strategy.management() - strategy.setProfitMaxUnlockTime(1, sender=management) - oracle.setOracle(strategy, strategy, sender=management) - - -def full_setup( - deploy_mock_tokenized, - vault, - apr_oracle, - daddy, - brain, - yield_manager, - debt_allocator, - keeper, - management, - user, -): - # Strategy two will have the higher apr - strategy_one = deploy_mock_tokenized("One", int(1e16)) - strategy_two = deploy_mock_tokenized("two", int(1e17)) - setup_vault(vault, [strategy_one, strategy_two], apr_oracle, daddy) - yield_manager.setVaultAllocator(vault, debt_allocator, sender=daddy) - debt_allocator.setManager(yield_manager, True, sender=brain) - debt_allocator.setMinimumChange(1, sender=brain) - vault.add_role(yield_manager, ROLES.REPORTING_MANAGER, sender=daddy) - vault.set_role( - debt_allocator, - ROLES.DEBT_MANAGER | ROLES.REPORTING_MANAGER, - sender=daddy, - ) - strategy_one.setKeeper(keeper, sender=management) - keeper.addNewStrategy(strategy_one, sender=daddy) - keeper.setKeeper(yield_manager, True, sender=daddy) - yield_manager.setProposer(user, True, sender=daddy) - - return (strategy_one, strategy_two) - - -def test_yield_manager_setup(yield_manager, daddy, vault, management, keeper): - assert yield_manager.keeper() == keeper - assert yield_manager.governance() == daddy - assert yield_manager.open() == False - assert yield_manager.proposer(management) == False - assert yield_manager.vaultAllocator(vault) == ZERO_ADDRESS - - -def test_setters(yield_manager, daddy, vault, debt_allocator, management): - assert yield_manager.vaultAllocator(vault) == ZERO_ADDRESS - assert yield_manager.proposer(management) == False - assert yield_manager.open() == False - - with ape.reverts("!governance"): - yield_manager.setVaultAllocator(vault, debt_allocator, sender=management) - - tx = yield_manager.setVaultAllocator(vault, debt_allocator, sender=daddy) - - event = list(tx.decode_logs(yield_manager.UpdateVaultAllocator))[0] - - assert event.vault == vault - assert event.allocator == debt_allocator - assert yield_manager.vaultAllocator(vault) == debt_allocator - - with ape.reverts("!governance"): - yield_manager.setProposer(management, True, sender=management) - - tx = yield_manager.setProposer(management, True, sender=daddy) - - event = list(tx.decode_logs(yield_manager.UpdateProposer))[0] - - assert event.proposer == management - assert event.status == True - assert yield_manager.proposer(management) == True - - tx = yield_manager.setProposer(management, False, sender=daddy) - - event = list(tx.decode_logs(yield_manager.UpdateProposer))[0] - - assert event.proposer == management - assert event.status == False - assert yield_manager.proposer(management) == False - - with ape.reverts("!governance"): - yield_manager.setOpen(True, sender=management) - - tx = yield_manager.setOpen(True, sender=daddy) - - event = list(tx.decode_logs(yield_manager.UpdateOpen))[0] - - assert event.status == True - assert yield_manager.open() - - -def test_update_allocation( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - # Strategy two will have the higher apr - strategy_one = deploy_mock_tokenized("One", int(1e16)) - strategy_two = deploy_mock_tokenized("two", int(1e17)) - setup_vault(vault, [strategy_one, strategy_two], apr_oracle, daddy) - keeper.setKeeper(yield_manager, True, sender=daddy) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - # Can just pass in one at to allocate - allocation = [(strategy_two, amount)] - - with ape.reverts("!allocator or open"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - yield_manager.setProposer(user, True, sender=daddy) - - with ape.reverts("vault not added"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - yield_manager.setVaultAllocator(vault, debt_allocator, sender=daddy) - - # Must give allocator the manager role - with ape.reverts("!manager"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - debt_allocator.setManager(yield_manager, True, sender=brain) - debt_allocator.setMinimumChange(1, sender=brain) - vault.set_role( - debt_allocator, - ROLES.DEBT_MANAGER | ROLES.REPORTING_MANAGER, - sender=daddy, - ) - - # Set max debt for strategy to 0. - vault.update_max_debt_for_strategy(strategy_two, 0, sender=daddy) - - with ape.reverts("max debt"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - vault.update_max_debt_for_strategy(strategy_two, 2**256 - 1, sender=daddy) - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - (before, now) = tx.return_value - - assert debt_allocator.getConfig(strategy_two).targetRatio == MAX_BPS - assert debt_allocator.getConfig(strategy_one).targetRatio == 0 - assert debt_allocator.shouldUpdateDebt(strategy_one)[0] == False - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == True - assert debt_allocator.shouldUpdateDebt(strategy_two)[ - 1 - ] == vault.update_debt.encode_input(strategy_two.address, amount) - assert before == 0 - - debt_allocator.update_debt(strategy_two, amount, sender=brain) - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == False - - # assert now == int(1e17 * amount) - assert vault.totalIdle() == 0 - assert vault.totalDebt() == amount - assert vault.strategies(strategy_two).current_debt == amount - - allocation = [] - with ape.reverts("cheater"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - allocation = [(strategy_one, amount)] - with ape.reverts("ratio too high"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - allocation = [(strategy_two, amount // 2)] - with ape.reverts("fail"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - # strategy one is now earning more - strategy_one.setApr(int(1.5e17), sender=management) - - # only move part - to_move = amount // 2 - # will revert if in the wrong order - allocation = [(strategy_one, to_move), (strategy_two, amount - to_move)] - with ape.reverts("ratio too high"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - allocation = [(strategy_two, amount - to_move), (strategy_one, to_move)] - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert debt_allocator.getConfig(strategy_two).targetRatio != MAX_BPS - assert debt_allocator.getConfig(strategy_one).targetRatio != 0 - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == True - assert bytes_two == vault.update_debt.encode_input( - strategy_two.address, amount - to_move - ) - - debt_allocator.update_debt(strategy_two, amount - to_move, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool_one == True - assert bytes_one == vault.update_debt.encode_input(strategy_one, to_move) - - debt_allocator.update_debt(strategy_one, to_move, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == False - - assert vault.totalIdle() == 0 - assert vault.totalDebt() == amount - assert vault.totalAssets() == amount - assert vault.strategies(strategy_one).current_debt == to_move - assert vault.strategies(strategy_two).current_debt == amount - to_move - - # Try and move all - allocation = [(strategy_two, 0), (strategy_one, amount)] - - strategy_two.setKeeper(keeper, sender=management) - keeper.addNewStrategy(strategy_two, sender=daddy) - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert debt_allocator.getConfig(strategy_two).targetRatio == 0 - assert debt_allocator.getConfig(strategy_one).targetRatio == MAX_BPS - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == True - assert bytes_two == vault.update_debt.encode_input(strategy_two.address, 0) - - debt_allocator.update_debt(strategy_two, 0, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool_one == True - assert bytes_one == vault.update_debt.encode_input(strategy_one, amount) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == False - - # assert len(list(tx.decode_logs(strategy_two.Reported))) == 1 - assert vault.totalIdle() == 0 - assert vault.totalDebt() == amount - assert vault.totalAssets() == amount - assert vault.strategies(strategy_one).current_debt == amount - assert vault.strategies(strategy_two).current_debt == 0 - - # Try and move all them all back - allocation = [(strategy_one, 1), (strategy_two, amount)] - with ape.reverts("fail"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - -def test_update_allocation_pending_profit( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - profit = amount // 10 - amount = amount - profit - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - asset.transfer(strategy_one, profit, sender=user) - - allocation = [(strategy_one, 0), (strategy_two, amount + profit)] - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert len(list(tx.decode_logs(strategy_one.Reported))) == 1 - - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == False - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_one, 0) - - tx = debt_allocator.update_debt(strategy_one, 0, sender=brain) - - event = list(tx.decode_logs(vault.StrategyReported)) - assert len(event) == 1 - assert event[0].gain == profit - - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_two, amount + profit) - - -def test_update_allocation_pending_loss( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - loss = amount // 10 - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - asset.transfer(user, loss, sender=strategy_one) - - allocation = [(strategy_one, 0), (strategy_two, amount)] - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert len(list(tx.decode_logs(vault.StrategyReported))) == 1 - - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == False - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_one, 0) - - debt_allocator.update_debt(strategy_one, 0, sender=brain) - - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_two, amount - loss) - - -def test_update_allocation_pending_loss_move_half( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - loss = amount // 10 - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - # Record strategy loss - asset.transfer(user, loss, sender=strategy_one) - strategy_one.report(sender=keeper) - - to_move = amount // 2 - allocation = [(strategy_one, amount - loss - to_move), (strategy_two, to_move)] - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert len(list(tx.decode_logs(vault.StrategyReported))) == 1 - assert vault.totalAssets() < amount - - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == False - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool == True - - debt_allocator.update_debt(strategy_one, amount - to_move, sender=brain) - - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_two, to_move - loss) - - -def test_update_allocation_pending_loss_move_all( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - loss = amount // 10 - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - # Record strategy loss - asset.transfer(user, loss, sender=strategy_one) - strategy_one.report(sender=keeper) - - to_move = amount - allocation = [(strategy_one, amount - to_move), (strategy_two, to_move)] - - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - assert len(list(tx.decode_logs(vault.StrategyReported))) == 1 - assert vault.totalAssets() < amount - - assert debt_allocator.shouldUpdateDebt(strategy_two)[0] == False - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool == True - - debt_allocator.update_debt(strategy_one, amount - to_move, sender=brain) - - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_two, to_move - loss) - - -def test_validate_allocation( - apr_oracle, - yield_manager, - vault, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one = deploy_mock_tokenized("One") - strategy_two = deploy_mock_tokenized("two") - setup_vault(vault, [strategy_one, strategy_two], apr_oracle, daddy) - yield_manager.setVaultAllocator(vault, debt_allocator, sender=daddy) - vault.set_role( - debt_allocator, - ROLES.DEBT_MANAGER | ROLES.REPORTING_MANAGER, - sender=daddy, - ) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - # Can validate the allocation with no strategies when all is idle - assert vault.totalIdle() == amount - - assert yield_manager.validateAllocation(vault, []) - assert yield_manager.validateAllocation(vault, [(strategy_one, amount)]) - assert yield_manager.validateAllocation( - vault, [(strategy_one, amount), (strategy_two, 0)] - ) - - debt_allocator.update_debt(strategy_one, amount // 2, sender=brain) - - assert yield_manager.validateAllocation(vault, []) == False - assert yield_manager.validateAllocation(vault, [(strategy_one, amount)]) - assert yield_manager.validateAllocation( - vault, [(strategy_one, amount), (strategy_two, 0)] - ) - - # Now will be false - debt_allocator.update_debt(strategy_two, vault.totalIdle() // 2, sender=brain) - - assert yield_manager.validateAllocation(vault, []) == False - assert yield_manager.validateAllocation(vault, [(strategy_one, amount)]) == False - assert yield_manager.validateAllocation( - vault, [(strategy_one, amount), (strategy_two, 0)] - ) - - -def test_get_current_and_expected( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - user, - amount, - asset, - deploy_mock_tokenized, -): - # Strategy two will have the higher apr - strategy_one = deploy_mock_tokenized("One", int(1e16)) - strategy_two = deploy_mock_tokenized("two", int(1e17)) - setup_vault(vault, [strategy_one, strategy_two], apr_oracle, daddy) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - allocation = [] - (current, expected) = yield_manager.getCurrentAndExpectedRate(vault, allocation) - - assert current == 0 - assert expected == 0 - - allocation = [(strategy_one, 0), (strategy_two, 0)] - (current, expected) = yield_manager.getCurrentAndExpectedRate(vault, allocation) - assert current == 0 - assert expected == 0 - - allocation = [(strategy_one, amount), (strategy_two, 0)] - (current, expected) = yield_manager.getCurrentAndExpectedRate(vault, allocation) - assert current == 0 - assert expected != 0 - - vault.update_debt(strategy_one, amount, sender=daddy) - - allocation = [(strategy_one, 0), (strategy_two, amount)] - (current, new_expected) = yield_manager.getCurrentAndExpectedRate(vault, allocation) - assert current == expected - assert expected != 0 - assert expected > 0 - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - -def test_update_allocation_permissioned( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - # Can just pass in one at to allocate - allocation = [(strategy_two, amount)] - - with ape.reverts("!governance"): - yield_manager.updateAllocationPermissioned(vault, allocation, sender=user) - - yield_manager.setProposer(user, True, sender=daddy) - - # Still cant allocate even with allocator role - with ape.reverts("!governance"): - yield_manager.updateAllocationPermissioned(vault, allocation, sender=user) - - tx = yield_manager.updateAllocationPermissioned(vault, allocation, sender=daddy) - - assert debt_allocator.shouldUpdateDebt(strategy_one)[0] == False - (bool, bytes) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool == True - assert bytes == vault.update_debt.encode_input(strategy_two, amount) - - debt_allocator.update_debt(strategy_two, amount, sender=brain) - - allocation = [(strategy_one, amount)] - with ape.reverts("ratio too high"): - yield_manager.updateAllocationPermissioned(vault, allocation, sender=daddy) - - # strategy one is now earning more - strategy_one.setApr(int(1.5e17), sender=management) - - # only move part - to_move = amount // 2 - # will revert if in the wrong order - allocation = [(strategy_one, to_move), (strategy_two, amount - to_move)] - with ape.reverts("ratio too high"): - yield_manager.updateAllocationPermissioned(vault, allocation, sender=daddy) - - allocation = [(strategy_two, amount - to_move), (strategy_one, to_move)] - tx = yield_manager.updateAllocationPermissioned(vault, allocation, sender=daddy) - - assert debt_allocator.getConfig(strategy_two).targetRatio != MAX_BPS - assert debt_allocator.getConfig(strategy_one).targetRatio != 0 - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == True - assert bytes_two == vault.update_debt.encode_input( - strategy_two.address, amount - to_move - ) - - debt_allocator.update_debt(strategy_two, amount - to_move, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool_one == True - assert bytes_one == vault.update_debt.encode_input(strategy_one, to_move) - - debt_allocator.update_debt(strategy_one, to_move, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == False - - # Try and move all - allocation = [(strategy_two, 0), (strategy_one, amount)] - - strategy_two.setKeeper(keeper, sender=management) - keeper.addNewStrategy(strategy_two, sender=daddy) - keeper.setKeeper(yield_manager, True, sender=daddy) - - tx = yield_manager.updateAllocationPermissioned(vault, allocation, sender=daddy) - - assert len(list(tx.decode_logs(strategy_two.Reported))) == 1 - - assert debt_allocator.getConfig(strategy_two).targetRatio == 0 - assert debt_allocator.getConfig(strategy_one).targetRatio == MAX_BPS - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == True - assert bytes_two == vault.update_debt.encode_input(strategy_two.address, 0) - - debt_allocator.update_debt(strategy_two, 0, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - assert bool_one == True - assert bytes_one == vault.update_debt.encode_input(strategy_one, amount) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - (bool_one, bytes_one) = debt_allocator.shouldUpdateDebt(strategy_one) - (bool_two, bytes_two) = debt_allocator.shouldUpdateDebt(strategy_two) - assert bool_one == False - assert bool_two == False - - assert vault.totalIdle() == 0 - assert vault.totalDebt() == amount - assert vault.totalAssets() == amount - assert vault.strategies(strategy_one).current_debt == amount - assert vault.strategies(strategy_two).current_debt == 0 - - -def test_update_allocation__max_withdraw( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - limit = amount // 2 - - # Simulate a max withdraw limit - strategy_one.setLimit(limit, sender=daddy) - - # Try and move all - allocation = [(strategy_one, 0), (strategy_two, amount)] - - with ape.reverts("max withdraw"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - # Can withdraw up to the limit - allocation = [(strategy_one, amount - limit), (strategy_two, limit)] - yield_manager.updateAllocation(vault, allocation, sender=user) - - # lower the limit to 0 - strategy_one.setLimit(0, sender=daddy) - - # Now can move everything. - allocation = [(strategy_one, 0), (strategy_two, amount)] - yield_manager.updateAllocation(vault, allocation, sender=user) - - -def test_update_allocation__max_deposit( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - debt_allocator.update_debt(strategy_one, amount, sender=brain) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == amount - assert vault.strategies(strategy_one).current_debt == amount - - limit = amount // 2 - - # Simulate a max deposit limit - strategy_two.setMaxDebt(limit, sender=daddy) - - # Try and move all - allocation = [(strategy_one, 0), (strategy_two, amount)] - - with ape.reverts("max deposit"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - # Can deposit up to the limit - allocation = [(strategy_one, amount - limit), (strategy_two, limit)] - yield_manager.updateAllocation(vault, allocation, sender=user) - - # Increase the limit - strategy_two.setMaxDebt(2**256 - 1, sender=daddy) - - # Now can move everything. - allocation = [(strategy_one, 0), (strategy_two, amount)] - yield_manager.updateAllocation(vault, allocation, sender=user) - - -def test_update_allocation__min_idle( - apr_oracle, - yield_manager, - vault, - management, - keeper, - daddy, - brain, - user, - amount, - asset, - deploy_mock_tokenized, - debt_allocator, -): - strategy_one, strategy_two = full_setup( - deploy_mock_tokenized=deploy_mock_tokenized, - vault=vault, - apr_oracle=apr_oracle, - daddy=daddy, - brain=brain, - yield_manager=yield_manager, - debt_allocator=debt_allocator, - keeper=keeper, - management=management, - user=user, - ) - - asset.approve(vault, amount, sender=user) - vault.deposit(amount, user, sender=user) - - assert vault.totalAssets() == amount - assert vault.totalDebt() == 0 - - min_idle = amount // 2 - - # add a minimum total idle requirement - vault.set_minimum_total_idle(min_idle, sender=daddy) - - # Try and move all - allocation = [(strategy_one, amount)] - - with ape.reverts("min idle"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - # Even just over the limit reverts. - allocation = [(strategy_one, int(amount - min_idle + 1e18))] - - with ape.reverts("min idle"): - yield_manager.updateAllocation(vault, allocation, sender=user) - - # Can deposit up to the limit - allocation = [(strategy_one, amount - min_idle)] - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - event = list(tx.decode_logs(debt_allocator.UpdateStrategyDebtRatio))[0] - assert event.newTargetRatio == 5_000 - - # lower the min idle to 0 - vault.set_minimum_total_idle(0, sender=daddy) - - # Now can move everything. - allocation = [(strategy_one, 0), (strategy_two, amount)] - tx = yield_manager.updateAllocation(vault, allocation, sender=user) - - event = list(tx.decode_logs(debt_allocator.UpdateStrategyDebtRatio)) - - assert len(event) == 2 - assert event[0].newTargetRatio == 0 - assert event[1].newTargetRatio == 10_000