diff --git a/contracts/debtAllocators/Allocator.sol b/contracts/debtAllocators/Allocator.sol deleted file mode 100644 index 5a374f0..0000000 --- a/contracts/debtAllocators/Allocator.sol +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity 0.8.18; - -import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; - -import {Governance} from "@periphery/utils/Governance.sol"; -import {IVault} from "@yearn-vaults/interfaces/IVault.sol"; - -/** - * @title YearnV3 Generic Debt Allocator - * @author yearn.finance - * @notice - * This Generic Debt Allocator is meant to be used alongside - * a Yearn V3 vault to provide the needed triggers for a keeper - * to perform automated debt updates for the vaults strategies. - * - * Each allocator contract will serve one Vault and each strategy - * that should be managed by this allocator will need to be added - * manually by setting a `minimumChange` and a `targetRatio`. - * - * The allocator aims to allocate debt between the strategies - * based on their set target ratios. Which are denominated in basis - * points and represent the percent of total assets that specific - * strategy should hold. - */ -contract GenericDebtAllocator is Governance { - event SetTargetDebtRatio( - address indexed strategy, - uint256 targetRatio, - uint256 totalDebtRatio - ); - - event SetMinimumChange(uint256 minimumChange); - - event SetMaxAcceptableBaseFee(uint256 maxAcceptableBaseFee); - - event SetMinWait(uint256 newMinWait); - - // Struct for each strategies info. - struct Config { - // The percent in Basis Points the strategy should have. - uint256 targetRatio; - uint256 maxRatio; - uint256 lastUpdate; - } - - uint256 internal constant MAX_BPS = 10_000; - - // Mapping of strategy => its config. - mapping(address => Config) public configs; - - address public keeper; - - // Address of the vault this serves as allocator for. - address public vault; - - // Total debt ratio currently allocated in basis points. - // Can't be more than 10_000. - uint256 public debtRatio; - - // The minimum amount denominated in asset that will - // need to be moved to trigger a debt update. - uint256 public minimumChange; - - // Max the chains base fee can be during debt update. - // Will default to max uint256 and need to be set to be used. - uint256 public maxAcceptableBaseFee; - - uint256 public minWait; - - constructor(address _vault, address _governance) Governance(_governance) { - initialize(_vault, _governance); - } - - /** - * @notice Initializes the debt allocator. - * @dev Should be called atomically after cloning. - * @param _vault Address of the vault this allocates debt for. - * @param _governance Address to govern this contract. - */ - function initialize(address _vault, address _governance) public { - require(address(vault) == address(0), "!initialized"); - vault = _vault; - governance = _governance; - // Default max base fee to uint256 max - maxAcceptableBaseFee = type(uint256).max; - } - - function update_debt(address _strategy, uint256 _targetDebt) external { - require(msg.sender == keeper, "!keeper"); - IVault(vault).update_debt(_strategy, _targetDebt); - configs[_strategy].lastUpdate = block.timestamp; - } - - /** - * @notice Check if a strategy's debt should be updated. - * @dev This should be called by a keeper to decide if a strategies - * debt should be updated and if so by how much. - * - * This cannot be used to withdraw down to 0 debt. - * - * @param _strategy Address of the strategy to check. - * @return . Bool representing if the debt should be updated. - * @return . Calldata if `true` or reason if `false`. - */ - function shouldUpdateDebt( - address _strategy - ) external view returns (bool, bytes memory) { - // Check the base fee isn't too high. - if (block.basefee > maxAcceptableBaseFee) { - return (false, bytes("Base Fee")); - } - - // Cache the vault variable. - IVault _vault = IVault(vault); - // Retrieve the strategy specific parameters. - IVault.StrategyParams memory params = _vault.strategies(_strategy); - // Make sure its an active strategy. - require(params.activation != 0, "!active"); - - // Get the strategy specific debt config. - Config memory config = configs[_strategy]; - // Make sure we have a target debt. - require(config.targetRatio != 0, "no targetRatio"); - - if (block.timestamp - config.lastUpdate <= minWait) { - return (false, "min wait"); - } - - uint256 vaultAssets = _vault.totalAssets(); - - // Get the target debt for the strategy based on vault assets. - uint256 targetDebt = Math.min( - (vaultAssets * config.targetRatio) / MAX_BPS, - // Make sure it is not more than the max allowed. - params.max_debt - ); - - // Get the max debt we would want the strategy to have. - uint256 maxDebt = Math.min( - (vaultAssets * config.maxRatio) / MAX_BPS, - // Make sure it is not more than the max allowed. - params.max_debt - ); - - // If we need to add more. - if (targetDebt > params.current_debt) { - uint256 currentIdle = _vault.totalIdle(); - uint256 minIdle = _vault.minimum_total_idle(); - - // We can't add more than the available idle. - if (minIdle >= currentIdle) { - return (false, bytes("No Idle")); - } - - // Add up to the max if possible - uint256 toAdd = Math.min( - maxDebt - params.current_debt, - // Can't take more than is available. - Math.min( - currentIdle - minIdle, - IVault(_strategy).maxDeposit(vault) - ) - ); - - // If the amount to add is over our threshold. - if (toAdd > minimumChange) { - // Return true and the calldata. - return ( - true, - abi.encodeCall( - _vault.update_debt, - (_strategy, params.current_debt + toAdd) - ) - ); - } - // If target debt is lower than the current. - } else if (maxDebt < params.current_debt) { - // Find out by how much. - uint256 toPull = Math.min( - params.current_debt - targetDebt, - // Account for the current liquidity constraints. - IVault(_strategy).maxWithdraw(address(_vault)) - ); - - // Check if it's over the threshold. - if (toPull > minimumChange) { - // Can't lower debt if there is unrealised losses. - if ( - _vault.assess_share_of_unrealised_losses( - _strategy, - params.current_debt - ) > 0 - ) { - return (false, bytes("unrealised loss")); - } - - // If so return true and the calldata. - return ( - true, - abi.encodeCall( - _vault.update_debt, - (_strategy, params.current_debt - toPull) - ) - ); - } - } - - // Either no change or below our minimumChange. - return (false, bytes("Below Min")); - } - - /** - * @notice Sets a new target debt ratio for a strategy. - * @dev A `minimumChange` for that strategy must be set first. - * This is to prevent debt from being updated too frequently. - * - * @param _strategy Address of the strategy to set. - * @param _targetRatio Amount in Basis points to allocate. - * @param _maxRatio Max ratio to give on debt increases. - */ - function setStrategyDebtRatios( - address _strategy, - uint256 _targetRatio, - uint256 _maxRatio - ) external onlyGovernance { - // Make sure the strategy is added to the vault. - require(IVault(vault).strategies(_strategy).activation != 0, "!active"); - // Make sure a minimumChange has been set. - require(minimumChange != 0, "!minimum"); - // Max can not be lower than the target. - require(_maxRatio >= _targetRatio, "max ratio"); - - // Get what will be the new total debt ratio. - uint256 newDebtRatio = debtRatio - - configs[_strategy].targetRatio + - _targetRatio; - - // Make sure it is under 100% allocated - require(newDebtRatio <= MAX_BPS, "ratio too high"); - - // Write to storage. - configs[_strategy].targetRatio = _targetRatio; - configs[_strategy].maxRatio = _maxRatio; - debtRatio = newDebtRatio; - - emit SetTargetDebtRatio(_strategy, _targetRatio, newDebtRatio); - } - - /** - * @notice Set the minimum change variable for a strategy. - * @dev This is the amount of debt that will needed to be - * added or pulled for it to trigger an update. - * - * @param _minimumChange The new minimum to set for the strategy. - */ - function setMinimumChange(uint256 _minimumChange) external onlyGovernance { - require(minimumChange > 0, "zero"); - // Set the new minimum. - minimumChange = _minimumChange; - - emit SetMinimumChange(_minimumChange); - } - - function setKeeper(address _keeper) external onlyGovernance { - keeper = _keeper; - } - - /** - * @notice Set the max acceptable base fee. - * @dev This defaults to max uint256 and will need to - * be set for it to be used. - * - * Is denominated in gwei. So 50gwei would be set as 50e9. - * - * @param _maxAcceptableBaseFee The new max base fee. - */ - function setMaxAcceptableBaseFee( - uint256 _maxAcceptableBaseFee - ) external onlyGovernance { - maxAcceptableBaseFee = _maxAcceptableBaseFee; - - emit SetMaxAcceptableBaseFee(_maxAcceptableBaseFee); - } - - /** - * @notice Set the max acceptable base fee. - * @dev This defaults to max uint256 and will need to - * be set for it to be used. - * - * Is denominated in gwei. So 50gwei would be set as 50e9. - * - * @param _minWait The new max base fee. - */ - function setMinTime(uint256 _minWait) external onlyGovernance { - minWait = _minWait; - - emit SetMinWait(_minWait); - } -} diff --git a/contracts/debtAllocators/GenericDebtAllocator.sol b/contracts/debtAllocators/GenericDebtAllocator.sol index 741c20d..b19cef7 100644 --- a/contracts/debtAllocators/GenericDebtAllocator.sol +++ b/contracts/debtAllocators/GenericDebtAllocator.sol @@ -16,35 +16,49 @@ import {IVault} from "@yearn-vaults/interfaces/IVault.sol"; * * Each allocator contract will serve one Vault and each strategy * that should be managed by this allocator will need to be added - * manually by setting a `minimumChange` and a `targetRatio`. + * manually by setting a `targetRatio` and `maxRatio`. * * The allocator aims to allocate debt between the strategies * based on their set target ratios. Which are denominated in basis * points and represent the percent of total assets that specific * strategy should hold. + * + * The trigger will attempt to allocate up to the `maxRatio` when + * the strategy has `minimumChange` amount less than the `targetRatio`. + * And will pull funds from the strategy when it has `minimumChange` + * more than its `maxRatio`. */ contract GenericDebtAllocator is Governance { - event SetTargetDebtRatio( + event UpdatedStrategyDebtRatios( address indexed strategy, uint256 targetRatio, + uint256 maxRatio, uint256 totalDebtRatio ); - event SetMinimumChange(address indexed strategy, uint256 minimumChange); + event UpdatedMinimumChange(uint256 minimumChange); + + event UpdatedMaxAcceptableBaseFee(uint256 maxAcceptableBaseFee); - event SetMaxAcceptableBaseFee(uint256 maxAcceptableBaseFee); + event UpdatedMinimumWait(uint256 newMinimumWait); // Struct for each strategies info. struct Config { - // The percent in Basis Points the strategy should have. + // The ideal percent in Basis Points the strategy should have. uint256 targetRatio; - // The minimum amount denominated in asset that will - // need to be moved to trigger a debt update. - uint256 minimumChange; + // The max percent of assets the strategy should hold. + uint256 maxRatio; + // Timestamp of the last time debt was updated. + // The debt updates must be done through this allocator + // for this to be used. + uint256 lastUpdate; } uint256 internal constant MAX_BPS = 10_000; + // Vaults DEBT_MANAGER enumerator. + uint256 internal constant DEBT_MANAGER = 64; + // Mapping of strategy => its config. mapping(address => Config) public configs; @@ -55,12 +69,23 @@ contract GenericDebtAllocator is Governance { // Can't be more than 10_000. uint256 public debtRatio; + // The minimum amount denominated in asset that will + // need to be moved to trigger a debt update. + uint256 public minimumChange; + + // Time to wait between debt updates. + uint256 public minimumWait; + // Max the chains base fee can be during debt update. // Will default to max uint256 and need to be set to be used. uint256 public maxAcceptableBaseFee; - constructor(address _vault, address _governance) Governance(_governance) { - initialize(_vault, _governance); + constructor( + address _vault, + address _governance, + uint256 _minimumChange + ) Governance(_governance) { + initialize(_vault, _governance, _minimumChange); } /** @@ -68,21 +93,48 @@ contract GenericDebtAllocator is Governance { * @dev Should be called atomically after cloning. * @param _vault Address of the vault this allocates debt for. * @param _governance Address to govern this contract. + * @param _minimumChange The minimum in asset that must be moved. */ - function initialize(address _vault, address _governance) public { + function initialize( + address _vault, + address _governance, + uint256 _minimumChange + ) public { require(address(vault) == address(0), "!initialized"); vault = _vault; governance = _governance; + minimumChange = _minimumChange; // Default max base fee to uint256 max maxAcceptableBaseFee = type(uint256).max; } + /** + * @notice Debt update wrapper for the vault. + * @dev This can be used if a minimum time between debt updates + * is desired to be enforced. + * + * This contract and the msg.sender must have the DEBT_MANAGER + * role assigned to them. + * + * The function signature matches the vault so no update to the + * call data is required. + */ + function update_debt(address _strategy, uint256 _targetDebt) external { + IVault _vault = IVault(vault); + require( + (IVault(_vault).roles(msg.sender) & DEBT_MANAGER) == DEBT_MANAGER, + "!authorized" + ); + IVault(_vault).update_debt(_strategy, _targetDebt); + configs[_strategy].lastUpdate = block.timestamp; + } + /** * @notice Check if a strategy's debt should be updated. * @dev This should be called by a keeper to decide if a strategies * debt should be updated and if so by how much. * - * This cannot be used to withdraw down to 0 debt. + * NOTE: This cannot be used to withdraw down to 0 debt. * * @param _strategy Address of the strategy to check. * @return . Bool representing if the debt should be updated. @@ -108,9 +160,22 @@ contract GenericDebtAllocator is Governance { // Make sure we have a target debt. require(config.targetRatio != 0, "no targetRatio"); + if (block.timestamp - config.lastUpdate <= minimumWait) { + return (false, "min wait"); + } + + uint256 vaultAssets = _vault.totalAssets(); + // Get the target debt for the strategy based on vault assets. uint256 targetDebt = Math.min( - (_vault.totalAssets() * config.targetRatio) / MAX_BPS, + (vaultAssets * config.targetRatio) / MAX_BPS, + // Make sure it is not more than the max allowed. + params.max_debt + ); + + // Get the max debt we would want the strategy to have. + uint256 maxDebt = Math.min( + (vaultAssets * config.maxRatio) / MAX_BPS, // Make sure it is not more than the max allowed. params.max_debt ); @@ -125,8 +190,9 @@ contract GenericDebtAllocator is Governance { return (false, bytes("No Idle")); } + // Add up to the max if possible uint256 toAdd = Math.min( - targetDebt - params.current_debt, + maxDebt - params.current_debt, // Can't take more than is available. Math.min( currentIdle - minIdle, @@ -135,7 +201,7 @@ contract GenericDebtAllocator is Governance { ); // If the amount to add is over our threshold. - if (toAdd > config.minimumChange) { + if (toAdd > minimumChange) { // Return true and the calldata. return ( true, @@ -146,7 +212,7 @@ contract GenericDebtAllocator is Governance { ); } // If target debt is lower than the current. - } else if (targetDebt < params.current_debt) { + } else if (maxDebt < params.current_debt) { // Find out by how much. uint256 toPull = Math.min( params.current_debt - targetDebt, @@ -155,7 +221,7 @@ contract GenericDebtAllocator is Governance { ); // Check if it's over the threshold. - if (toPull > config.minimumChange) { + if (toPull > minimumChange) { // Can't lower debt if there is unrealised losses. if ( _vault.assess_share_of_unrealised_losses( @@ -188,15 +254,21 @@ contract GenericDebtAllocator is Governance { * * @param _strategy Address of the strategy to set. * @param _targetRatio Amount in Basis points to allocate. + * @param _maxRatio Max ratio to give on debt increases. */ - function setTargetDebtRatio( + function setStrategyDebtRatios( address _strategy, - uint256 _targetRatio + uint256 _targetRatio, + uint256 _maxRatio ) external onlyGovernance { // Make sure the strategy is added to the vault. require(IVault(vault).strategies(_strategy).activation != 0, "!active"); // Make sure a minimumChange has been set. - require(configs[_strategy].minimumChange != 0, "!minimum"); + require(minimumChange != 0, "!minimum"); + // Cannot be more than 100%. + require(_maxRatio <= MAX_BPS, "max too high"); + // Max cannot be lower than the target. + require(_maxRatio >= _targetRatio, "max ratio"); // Get what will be the new total debt ratio. uint256 newDebtRatio = debtRatio - @@ -208,9 +280,16 @@ contract GenericDebtAllocator is Governance { // Write to storage. configs[_strategy].targetRatio = _targetRatio; + configs[_strategy].maxRatio = _maxRatio; + debtRatio = newDebtRatio; - emit SetTargetDebtRatio(_strategy, _targetRatio, newDebtRatio); + emit UpdatedStrategyDebtRatios( + _strategy, + _targetRatio, + _maxRatio, + newDebtRatio + ); } /** @@ -218,20 +297,25 @@ contract GenericDebtAllocator is Governance { * @dev This is the amount of debt that will needed to be * added or pulled for it to trigger an update. * - * @param _strategy The address of the strategy to update. * @param _minimumChange The new minimum to set for the strategy. */ - function setMinimumChange( - address _strategy, - uint256 _minimumChange - ) external onlyGovernance { - // Make sure the strategy is added to the vault. - require(IVault(vault).strategies(_strategy).activation != 0, "!active"); - + function setMinimumChange(uint256 _minimumChange) external onlyGovernance { + require(_minimumChange > 0, "zero"); // Set the new minimum. - configs[_strategy].minimumChange = _minimumChange; + minimumChange = _minimumChange; + + emit UpdatedMinimumChange(_minimumChange); + } + + /** + * @notice Set the minimum time to wait before re-updating a strategies debt. + * @dev This is only enforced per strategy. + * @param _minimumWait The minimum time in seconds to wait. + */ + function setMinimumTime(uint256 _minimumWait) external onlyGovernance { + minimumWait = _minimumWait; - emit SetMinimumChange(_strategy, _minimumChange); + emit UpdatedMinimumWait(_minimumWait); } /** @@ -248,6 +332,6 @@ contract GenericDebtAllocator is Governance { ) external onlyGovernance { maxAcceptableBaseFee = _maxAcceptableBaseFee; - emit SetMaxAcceptableBaseFee(_maxAcceptableBaseFee); + emit UpdatedMaxAcceptableBaseFee(_maxAcceptableBaseFee); } } diff --git a/contracts/debtAllocators/GenericDebtAllocatorFactory.sol b/contracts/debtAllocators/GenericDebtAllocatorFactory.sol index 0727128..aa26a77 100644 --- a/contracts/debtAllocators/GenericDebtAllocatorFactory.sol +++ b/contracts/debtAllocators/GenericDebtAllocatorFactory.sol @@ -17,12 +17,13 @@ contract GenericDebtAllocatorFactory { address public immutable original; constructor() { - original = address(new GenericDebtAllocator(address(1), address(2))); + original = address(new GenericDebtAllocator(address(1), address(2), 0)); } /** * @notice Clones a new debt allocator. - * @dev defaults to msg.sender as the governance role. + * @dev defaults to msg.sender as the governance role and 0 + * for the `minimumChange`. * * @param _vault The vault for the allocator to be hooked to. * @return Address of the new generic debt allocator @@ -30,18 +31,34 @@ contract GenericDebtAllocatorFactory { function newGenericDebtAllocator( address _vault ) external returns (address) { - return newGenericDebtAllocator(_vault, msg.sender); + return newGenericDebtAllocator(_vault, msg.sender, 0); } /** * @notice Clones a new debt allocator. + * @dev defaults to 0 for the `minimumChange`. + * * @param _vault The vault for the allocator to be hooked to. * @param _governance Address to serve as governance. - * @return newAllocator Address of the new generic debt allocator + * @return Address of the new generic debt allocator */ function newGenericDebtAllocator( address _vault, address _governance + ) external returns (address) { + return newGenericDebtAllocator(_vault, _governance, 0); + } + + /** + * @notice Clones a new debt allocator. + * @param _vault The vault for the allocator to be hooked to. + * @param _governance Address to serve as governance. + * @return newAllocator Address of the new generic debt allocator + */ + function newGenericDebtAllocator( + address _vault, + address _governance, + uint256 _minimumChange ) public returns (address newAllocator) { // Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol bytes20 addressBytes = bytes20(original); @@ -60,7 +77,11 @@ contract GenericDebtAllocatorFactory { newAllocator := create(0, clone_code, 0x37) } - GenericDebtAllocator(newAllocator).initialize(_vault, _governance); + GenericDebtAllocator(newAllocator).initialize( + _vault, + _governance, + _minimumChange + ); emit NewDebtAllocator(newAllocator, _vault); } diff --git a/tests/test_generic_debt_allocator.py b/tests/test_generic_debt_allocator.py index 912348b..bb39bc0 100644 --- a/tests/test_generic_debt_allocator.py +++ b/tests/test_generic_debt_allocator.py @@ -5,7 +5,7 @@ def test_setup(generic_debt_allocator_factory, user, strategy, vault): tx = generic_debt_allocator_factory.newGenericDebtAllocator( - vault, user, sender=user + vault, user, 0, sender=user ) events = list(tx.decode_logs(generic_debt_allocator_factory.NewDebtAllocator)) @@ -17,71 +17,92 @@ def test_setup(generic_debt_allocator_factory, user, strategy, vault): assert generic_debt_allocator.governance() == user assert generic_debt_allocator.vault() == vault.address - assert generic_debt_allocator.configs(strategy) == (0, 0) + assert generic_debt_allocator.configs(strategy) == (0, 0, 0) assert generic_debt_allocator.debtRatio() == 0 with ape.reverts("!active"): generic_debt_allocator.shouldUpdateDebt(strategy) def test_set_minimum(generic_debt_allocator, daddy, vault, strategy, user): - assert generic_debt_allocator.configs(strategy) == (0, 0) + assert generic_debt_allocator.configs(strategy) == (0, 0, 0) + assert generic_debt_allocator.minimumChange() == 0 minimum = int(1e17) with ape.reverts("!governance"): - generic_debt_allocator.setMinimumChange(strategy, minimum, sender=user) + generic_debt_allocator.setMinimumChange(minimum, sender=user) - with ape.reverts("!active"): - generic_debt_allocator.setMinimumChange(strategy, minimum, sender=daddy) - - vault.add_strategy(strategy.address, sender=daddy) + with ape.reverts("zero"): + generic_debt_allocator.setMinimumChange(0, sender=daddy) - tx = generic_debt_allocator.setMinimumChange(strategy, minimum, sender=daddy) + tx = generic_debt_allocator.setMinimumChange(minimum, sender=daddy) - event = list(tx.decode_logs(generic_debt_allocator.SetMinimumChange))[0] + event = list(tx.decode_logs(generic_debt_allocator.UpdatedMinimumChange))[0] - assert event.strategy == strategy.address assert event.minimumChange == minimum - assert generic_debt_allocator.configs(strategy) == (0, minimum) + assert generic_debt_allocator.minimumChange() == minimum -def test_set_minimum(generic_debt_allocator, daddy, vault, strategy, user): - assert generic_debt_allocator.configs(strategy) == (0, 0) +def test_set_ratios( + generic_debt_allocator, daddy, vault, strategy, create_strategy, user +): + assert generic_debt_allocator.configs(strategy) == (0, 0, 0) minimum = int(1e17) + max = int(6_000) target = int(5_000) with ape.reverts("!governance"): - generic_debt_allocator.setTargetDebtRatio(strategy, target, sender=user) + generic_debt_allocator.setStrategyDebtRatios(strategy, target, max, sender=user) with ape.reverts("!active"): - generic_debt_allocator.setTargetDebtRatio(strategy, target, sender=daddy) + generic_debt_allocator.setStrategyDebtRatios( + strategy, target, max, sender=daddy + ) vault.add_strategy(strategy.address, sender=daddy) with ape.reverts("!minimum"): - generic_debt_allocator.setTargetDebtRatio(strategy, target, sender=daddy) + generic_debt_allocator.setStrategyDebtRatios( + strategy, target, max, sender=daddy + ) - generic_debt_allocator.setMinimumChange(strategy, minimum, sender=daddy) + generic_debt_allocator.setMinimumChange(minimum, sender=daddy) - with ape.reverts("ratio too high"): - generic_debt_allocator.setTargetDebtRatio(strategy, int(10_001), sender=daddy) + with ape.reverts("max too high"): + generic_debt_allocator.setStrategyDebtRatios( + strategy, target, int(10_001), sender=daddy + ) - tx = generic_debt_allocator.setTargetDebtRatio(strategy, target, sender=daddy) + with ape.reverts("max ratio"): + generic_debt_allocator.setStrategyDebtRatios( + strategy, int(max + 1), max, sender=daddy + ) - event = list(tx.decode_logs(generic_debt_allocator.SetTargetDebtRatio))[0] + tx = generic_debt_allocator.setStrategyDebtRatios( + strategy, target, max, sender=daddy + ) + + event = list(tx.decode_logs(generic_debt_allocator.UpdatedStrategyDebtRatios))[0] - assert event.strategy == strategy.address assert event.targetRatio == target + assert event.maxRatio == max assert event.totalDebtRatio == target assert generic_debt_allocator.debtRatio() == target - assert generic_debt_allocator.configs(strategy) == (target, minimum) + assert generic_debt_allocator.configs(strategy) == (target, max, 0) + + new_strategy = create_strategy() + vault.add_strategy(new_strategy, sender=daddy) + with ape.reverts("ratio too high"): + generic_debt_allocator.setStrategyDebtRatios( + new_strategy, int(10_000), int(10_000), sender=daddy + ) def test_should_update_debt( generic_debt_allocator, vault, strategy, daddy, deposit_into_vault, amount ): - assert generic_debt_allocator.configs(strategy.address) == (0, 0) + assert generic_debt_allocator.configs(strategy.address) == (0, 0, 0) with ape.reverts("!active"): generic_debt_allocator.shouldUpdateDebt(strategy.address) @@ -93,9 +114,10 @@ def test_should_update_debt( minimum = int(1) target = int(5_000) + max = int(5_000) - generic_debt_allocator.setMinimumChange(strategy, minimum, sender=daddy) - generic_debt_allocator.setTargetDebtRatio(strategy, target, sender=daddy) + generic_debt_allocator.setMinimumChange(minimum, sender=daddy) + generic_debt_allocator.setStrategyDebtRatios(strategy, target, max, sender=daddy) # Vault has no assets so should be false. (bool, bytes) = generic_debt_allocator.shouldUpdateDebt(strategy.address) @@ -126,7 +148,9 @@ def test_should_update_debt( assert bytes == ("Below Min").encode("utf-8") # Update the ratio to make true - generic_debt_allocator.setTargetDebtRatio(strategy, int(target + 1), sender=daddy) + generic_debt_allocator.setStrategyDebtRatios( + strategy, int(target + 1), int(target + 1), sender=daddy + ) (bool, bytes) = generic_debt_allocator.shouldUpdateDebt(strategy.address) assert bool == True @@ -160,15 +184,17 @@ def test_should_update_debt( vault.set_minimum_total_idle(0, sender=daddy) # increase the minimum so its false again - generic_debt_allocator.setMinimumChange(strategy, int(1e30), sender=daddy) + generic_debt_allocator.setMinimumChange(int(1e30), sender=daddy) (bool, bytes) = generic_debt_allocator.shouldUpdateDebt(strategy.address) assert bool == False assert bytes == ("Below Min").encode("utf-8") # Lower the target and minimum - generic_debt_allocator.setMinimumChange(strategy, int(1), sender=daddy) - generic_debt_allocator.setTargetDebtRatio(strategy, int(target // 2), sender=daddy) + generic_debt_allocator.setMinimumChange(int(1), sender=daddy) + generic_debt_allocator.setStrategyDebtRatios( + strategy, int(target // 2), int(target // 2), sender=daddy + ) (bool, bytes) = generic_debt_allocator.shouldUpdateDebt(strategy.address) assert bool == True