From e50974368fd0963e24c5ccc754567c50cfaa33cc Mon Sep 17 00:00:00 2001 From: FP Date: Thu, 20 Jun 2024 10:21:38 -0700 Subject: [PATCH] feat: doa multicall support --- .../DebtOptimizerApplicator.sol | 25 ++++++-- tests/conftest.py | 14 +++-- .../test_debt_optimizer_applicator.py | 59 +++++++++++++++++++ 3 files changed, 88 insertions(+), 10 deletions(-) diff --git a/contracts/debtAllocators/DebtOptimizerApplicator.sol b/contracts/debtAllocators/DebtOptimizerApplicator.sol index 85eccff..e550390 100644 --- a/contracts/debtAllocators/DebtOptimizerApplicator.sol +++ b/contracts/debtAllocators/DebtOptimizerApplicator.sol @@ -1,9 +1,22 @@ // SPDX-License-Identifier: GNU AGPLv3 pragma solidity >=0.8.18; -import {DebtAllocator, DebtAllocatorFactory} from "./DebtAllocator.sol"; +import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol"; +import {IDebtAllocatorFactory} from "./DebtAllocator.sol"; -contract DebtOptimizerApplicator { +interface IDebtAllocator { + function setStrategyDebtRatio( + address _strategy, + uint256 _targetRatio + ) external; + function setStrategyDebtRatio( + address _strategy, + uint256 _targetRatio, + uint256 _maxRatio + ) external; +} + +contract DebtOptimizerApplicator is Multicall { /// @notice An event emitted when a keeper is added or removed. event UpdateManager(address indexed manager, bool allowed); @@ -30,7 +43,7 @@ contract DebtOptimizerApplicator { function _isGovernance() internal view virtual { require( msg.sender == - DebtAllocatorFactory(debtAllocatorFactory).governance(), + IDebtAllocatorFactory(debtAllocatorFactory).governance(), "!governance" ); } @@ -40,7 +53,7 @@ contract DebtOptimizerApplicator { require( managers[msg.sender] || msg.sender == - DebtAllocatorFactory(debtAllocatorFactory).governance(), + IDebtAllocatorFactory(debtAllocatorFactory).governance(), "!manager" ); } @@ -75,12 +88,12 @@ contract DebtOptimizerApplicator { ) public onlyManagers { for (uint8 i; i < _strategyDebtRatios.length; ++i) { if (_strategyDebtRatios[i].maxRatio == 0) { - DebtAllocator(_debtAllocator).setStrategyDebtRatio( + IDebtAllocator(_debtAllocator).setStrategyDebtRatio( _strategyDebtRatios[i].strategy, _strategyDebtRatios[i].targetRatio ); } else { - DebtAllocator(_debtAllocator).setStrategyDebtRatio( + IDebtAllocator(_debtAllocator).setStrategyDebtRatio( _strategyDebtRatios[i].strategy, _strategyDebtRatios[i].targetRatio, _strategyDebtRatios[i].maxRatio diff --git a/tests/conftest.py b/tests/conftest.py index 7a190ec..f81e591 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -404,13 +404,19 @@ def debt_allocator_factory(deploy_debt_allocator_factory): @pytest.fixture(scope="session") -def debt_allocator(debt_allocator_factory, project, vault, daddy): - tx = debt_allocator_factory.newDebtAllocator(vault, sender=daddy) +def create_debt_allocator(debt_allocator_factory, daddy): + def create_debt_allocator(vault): + tx = debt_allocator_factory.newDebtAllocator(vault, sender=daddy) + event = list(tx.decode_logs(debt_allocator_factory.NewDebtAllocator))[0] + debt_allocator = project.DebtAllocator.at(event.allocator) + return debt_allocator - event = list(tx.decode_logs(debt_allocator_factory.NewDebtAllocator))[0] + yield create_debt_allocator - debt_allocator = project.DebtAllocator.at(event.allocator) +@pytest.fixture(scope="session") +def debt_allocator(create_debt_allocator, vault): + debt_allocator = create_debt_allocator(vault) yield debt_allocator diff --git a/tests/debtAllocators/test_debt_optimizer_applicator.py b/tests/debtAllocators/test_debt_optimizer_applicator.py index 440d39c..565808b 100644 --- a/tests/debtAllocators/test_debt_optimizer_applicator.py +++ b/tests/debtAllocators/test_debt_optimizer_applicator.py @@ -1,6 +1,7 @@ import ape from ape import chain, project from utils.constants import MAX_INT, ROLES +import itertools def test_setup(debt_optimizer_applicator, debt_allocator_factory, brain): @@ -111,3 +112,61 @@ def test_set_ratios( 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) + + +def test_set_ratios_multicall( + debt_optimizer_applicator, + debt_allocator, + brain, + daddy, + asset, + create_vault, + create_strategy, + create_debt_allocator, + user, +): + debt_allocators: dict[str, list[str]] = {} + for _ in range(2): + vault = create_vault(asset) + debt_allocator = create_debt_allocator(vault) + debt_allocator.setManager(debt_optimizer_applicator, True, sender=brain) + debt_allocator.setMinimumChange(1, sender=brain) + debt_allocators[debt_allocator.address] = [] + for _ in range(2): + debt_allocators[debt_allocator.address].append(create_strategy(asset)) + + calldata = [ + debt_optimizer_applicator.setStrategyDebtRatios.encode_input( + allocator, [(strategy, int(5_000), int(0)) for strategy in strategies] + ) + for allocator, strategies in debt_allocators.items() + ] + + with ape.reverts("!manager"): + debt_optimizer_applicator.multicall(calldata, sender=user) + + tx = debt_optimizer_applicator.multicall( + calldata, + sender=brain, + ) + + events = list(tx.decode_logs(debt_allocator.UpdateStrategyDebtRatio)) + strategies = list(itertools.chain(*debt_allocators.values())) + + assert len(events) == 4 + for event in events: + assert event.strategy in strategies + assert event.newTargetRatio == 5_000 + assert event.newMaxRatio == int(5_000 * 1.2) + + for debt_allocator_addr, strategies in debt_allocators.items(): + debt_allocator = project.DebtAllocator.at(debt_allocator_addr) + assert debt_allocator.totalDebtRatio() == 10_000 + for strategy in strategies: + assert debt_allocator.getConfig(strategy) == ( + True, + 5_000, + 5_000 * 1.2, + 0, + 0, + )