-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: debt optimizer applicator (#46)
* feat: debt optimizer applicator * fix: emit * fix: bug * chore: test optimizer applicator * fix: use calldata
- Loading branch information
Showing
3 changed files
with
210 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// SPDX-License-Identifier: GNU AGPLv3 | ||
pragma solidity >=0.8.18; | ||
|
||
import {DebtAllocator, DebtAllocatorFactory} from "./DebtAllocator.sol"; | ||
|
||
contract DebtOptimizerApplicator { | ||
/// @notice An event emitted when a keeper is added or removed. | ||
event UpdateManager(address indexed manager, bool allowed); | ||
|
||
/// @notice struct for debt ratio changes | ||
struct StrategyDebtRatio { | ||
address strategy; | ||
uint256 targetRatio; | ||
uint256 maxRatio; | ||
} | ||
|
||
/// @notice Make sure the caller is governance. | ||
modifier onlyGovernance() { | ||
_isGovernance(); | ||
_; | ||
} | ||
|
||
/// @notice Make sure the caller is governance or a manager. | ||
modifier onlyManagers() { | ||
_isManager(); | ||
_; | ||
} | ||
|
||
/// @notice Check the Factories governance address. | ||
function _isGovernance() internal view virtual { | ||
require( | ||
msg.sender == | ||
DebtAllocatorFactory(debtAllocatorFactory).governance(), | ||
"!governance" | ||
); | ||
} | ||
|
||
/// @notice Check is either factories governance or local manager. | ||
function _isManager() internal view virtual { | ||
require( | ||
managers[msg.sender] || | ||
msg.sender == | ||
DebtAllocatorFactory(debtAllocatorFactory).governance(), | ||
"!manager" | ||
); | ||
} | ||
|
||
/// @notice The address of the debt allocator factory to use for some role checks. | ||
address public immutable debtAllocatorFactory; | ||
|
||
/// @notice Mapping of addresses that are allowed to update debt ratios. | ||
mapping(address => bool) public managers; | ||
|
||
constructor(address _debtAllocatorFactory) { | ||
debtAllocatorFactory = _debtAllocatorFactory; | ||
} | ||
|
||
/** | ||
* @notice Set if a manager can update ratios. | ||
* @param _address The address to set mapping for. | ||
* @param _allowed If the address can call {update_debt}. | ||
*/ | ||
function setManager( | ||
address _address, | ||
bool _allowed | ||
) external virtual onlyGovernance { | ||
managers[_address] = _allowed; | ||
|
||
emit UpdateManager(_address, _allowed); | ||
} | ||
|
||
function setStrategyDebtRatios( | ||
address _debtAllocator, | ||
StrategyDebtRatio[] calldata _strategyDebtRatios | ||
) public onlyManagers { | ||
for (uint8 i; i < _strategyDebtRatios.length; ++i) { | ||
if (_strategyDebtRatios[i].maxRatio == 0) { | ||
DebtAllocator(_debtAllocator).setStrategyDebtRatio( | ||
_strategyDebtRatios[i].strategy, | ||
_strategyDebtRatios[i].targetRatio | ||
); | ||
} else { | ||
DebtAllocator(_debtAllocator).setStrategyDebtRatio( | ||
_strategyDebtRatios[i].strategy, | ||
_strategyDebtRatios[i].targetRatio, | ||
_strategyDebtRatios[i].maxRatio | ||
); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import ape | ||
from ape import chain, project | ||
from utils.constants import MAX_INT, ROLES | ||
|
||
|
||
def test_setup(debt_optimizer_applicator, debt_allocator_factory, brain): | ||
|
||
assert debt_optimizer_applicator.managers(brain) == False | ||
assert ( | ||
debt_optimizer_applicator.debtAllocatorFactory() | ||
== debt_allocator_factory.address | ||
) | ||
|
||
|
||
def test_set_managers(debt_optimizer_applicator, brain, user): | ||
assert debt_optimizer_applicator.managers(brain) == False | ||
assert debt_optimizer_applicator.managers(user) == False | ||
|
||
with ape.reverts("!governance"): | ||
debt_optimizer_applicator.setManager(user, True, sender=user) | ||
|
||
tx = debt_optimizer_applicator.setManager(user, True, sender=brain) | ||
|
||
event = list(tx.decode_logs(debt_optimizer_applicator.UpdateManager))[0] | ||
|
||
assert event.manager == user | ||
assert event.allowed == True | ||
assert debt_optimizer_applicator.managers(user) == True | ||
|
||
tx = debt_optimizer_applicator.setManager(user, False, sender=brain) | ||
|
||
event = list(tx.decode_logs(debt_optimizer_applicator.UpdateManager))[0] | ||
|
||
assert event.manager == user | ||
assert event.allowed == False | ||
assert debt_optimizer_applicator.managers(user) == False | ||
|
||
|
||
def test_set_ratios( | ||
debt_optimizer_applicator, | ||
debt_allocator, | ||
brain, | ||
daddy, | ||
vault, | ||
strategy, | ||
create_strategy, | ||
user, | ||
): | ||
max = int(6_000) | ||
target = int(5_000) | ||
strategy_debt_ratio = (strategy.address, target, max) | ||
|
||
debt_allocator.setManager(debt_optimizer_applicator, True, sender=brain) | ||
debt_allocator.setMinimumChange(1, sender=brain) | ||
|
||
with ape.reverts("!manager"): | ||
debt_optimizer_applicator.setStrategyDebtRatios( | ||
debt_allocator, [strategy_debt_ratio], sender=user | ||
) | ||
|
||
vault.add_strategy(strategy.address, sender=daddy) | ||
|
||
tx = debt_optimizer_applicator.setStrategyDebtRatios( | ||
debt_allocator, [strategy_debt_ratio], sender=brain | ||
) | ||
|
||
event = list(tx.decode_logs(debt_allocator.StrategyChanged))[0] | ||
|
||
assert event.strategy == strategy | ||
assert event.status == 1 | ||
|
||
event = list(tx.decode_logs(debt_allocator.UpdateStrategyDebtRatio))[0] | ||
|
||
assert event.newTargetRatio == target | ||
assert event.newMaxRatio == max | ||
assert event.newTotalDebtRatio == target | ||
assert debt_allocator.totalDebtRatio() == target | ||
assert debt_allocator.getConfig(strategy) == (True, target, max, 0, 0) | ||
|
||
new_strategy = create_strategy() | ||
vault.add_strategy(new_strategy, sender=daddy) | ||
|
||
with ape.reverts("ratio too high"): | ||
debt_optimizer_applicator.setStrategyDebtRatios( | ||
debt_allocator, | ||
[(new_strategy.address, int(10_000), int(10_000))], | ||
sender=brain, | ||
) | ||
|
||
tx = debt_optimizer_applicator.setStrategyDebtRatios( | ||
debt_allocator, | ||
[ | ||
(strategy.address, int(8_000), int(9_000)), | ||
(new_strategy.address, int(2_000), int(0)), | ||
], | ||
sender=brain, | ||
) | ||
|
||
events = list(tx.decode_logs(debt_allocator.UpdateStrategyDebtRatio)) | ||
|
||
assert len(events) == 2 | ||
for event in events: | ||
assert event.strategy in [strategy, new_strategy] | ||
if event.strategy == strategy: | ||
assert event.newTargetRatio == 8_000 | ||
assert event.newMaxRatio == 9_000 | ||
else: | ||
assert event.newTargetRatio == 2_000 | ||
assert event.newMaxRatio == 2_000 * 1.2 | ||
|
||
assert debt_allocator.totalDebtRatio() == 10_000 | ||
assert debt_allocator.getConfig(strategy) == (True, 8_000, 9_000, 0, 0) | ||
assert debt_allocator.getConfig(new_strategy) == (True, 2_000, 2_000 * 1.2, 0, 0) |