Skip to content

Commit

Permalink
feat: use allocator
Browse files Browse the repository at this point in the history
  • Loading branch information
Schlagonia committed Dec 18, 2023
1 parent 16c0acf commit 637886a
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 174 deletions.
16 changes: 15 additions & 1 deletion contracts/debtAllocators/GenericDebtAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Governance} from "@periphery/utils/Governance.sol";
import {IVault} from "@yearn-vaults/interfaces/IVault.sol";

// TODO: Getters

/**
* @title YearnV3 Generic Debt Allocator
* @author yearn.finance
Expand Down Expand Up @@ -358,7 +360,7 @@ contract GenericDebtAllocator is Governance {
* @param _address The address to set mapping for.
* @param _allowed If the address can call {update_debt}.
*/
function setKeepers(
function setKeeper(
address _address,
bool _allowed
) external virtual onlyGovernance {
Expand Down Expand Up @@ -428,4 +430,16 @@ contract GenericDebtAllocator is Governance {

emit UpdateMaxAcceptableBaseFee(_maxAcceptableBaseFee);
}

function getStrategyTargetRatio(
address _strategy
) external view virtual returns (uint256) {
return configs[_strategy].targetRatio;
}

function getStrategyMaxRatio(
address _strategy
) external view virtual returns (uint256) {
return configs[_strategy].maxRatio;
}
}
6 changes: 3 additions & 3 deletions contracts/debtAllocators/YieldManager/StrategyManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,15 @@ contract StrategyManager is Governance {
// Get the current unlock rate.
uint256 profitUnlock = IStrategy(_strategy).profitMaxUnlockTime();

if (profitUnlock != 0) {
if (profitUnlock != 1) {
// Set profit unlock to 0.
IStrategy(_strategy).setProfitMaxUnlockTime(0);
IStrategy(_strategy).setProfitMaxUnlockTime(1);
}

// Report profits.
IStrategy(_strategy).report();

if (profitUnlock != 0) {
if (profitUnlock != 1) {
// Set profit unlock back to original.
IStrategy(_strategy).setProfitMaxUnlockTime(profitUnlock);
}
Expand Down
164 changes: 59 additions & 105 deletions contracts/debtAllocators/YieldManager/YieldManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {AprOracle} from "@periphery/AprOracle/AprOracle.sol";

import {StrategyManager, Governance} from "./StrategyManager.sol";

import {GenericDebtAllocator} from "../GenericDebtAllocator.sol";

/**
* @title YearnV3 Yield Yield Based Debt Allocator
* @author yearn.finance
Expand All @@ -18,10 +20,10 @@ contract YieldManager is Governance {
event UpdateOpen(bool status);

/// @notice Emitted when a vaults status is updated.
event UpdateVault(address indexed vault, bool status);
event UpdateVaultAllocator(address indexed vault, address allocator);

/// @notice Emitted when a allocators status is updated.
event UpdateAllocator(address indexed allocator, bool status);
/// @notice Emitted when a proposers status is updated.
event UpdateProposer(address indexed proposer, bool status);

/// @notice An event emitted when the max debt update loss is updated.
event UpdateMaxDebtUpdateLoss(uint256 newMaxDebtUpdateLoss);
Expand All @@ -36,14 +38,14 @@ contract YieldManager is Governance {
}

/// @notice Only allow the sender to be an allocator if not opened.
modifier onlyAllocatorsOrOpen() {
_isAllocatorOrOpen();
modifier onlyProposersOrOpen() {
_isProposerOrOpen();
_;
}

/// @notice Check if it has been opened or is an allocator.
function _isAllocatorOrOpen() internal view {
require(open || allocators[msg.sender], "!allocator or open");
function _isProposerOrOpen() internal view {
require(open || proposers[msg.sender], "!allocator or open");
}

uint256 internal constant MAX_BPS = 10_000;
Expand All @@ -61,11 +63,11 @@ contract YieldManager is Governance {
/// @notice Address that should hold the strategies `management` role.
address public immutable strategyManager;

/// @notice Mapping for vaults that can be allocated for.
mapping(address => bool) public vaults;
/// @notice Mapping for vaults that can be allocated for => its debt allocator.
mapping(address => address) public vaultAllocator;

/// @notice Addresses that are allowed to propose allocations.
mapping(address => bool) public allocators;
mapping(address => bool) public proposers;

constructor(
address _governance,
Expand Down Expand Up @@ -124,10 +126,12 @@ contract YieldManager is Governance {
Allocation[] memory _newAllocations
)
external
onlyAllocatorsOrOpen
onlyProposersOrOpen
returns (uint256 _currentYield, uint256 _afterYield)
{
require(vaults[_vault], "vault not added");
address allocator = vaultAllocator[_vault];
require(allocator != address(0), "vault not added");

// Get the total assets the vault has.
uint256 _totalAssets = IVault(_vault).totalAssets();

Expand All @@ -140,8 +144,6 @@ contract YieldManager is Governance {
address _strategy;
uint256 _currentDebt;
uint256 _newDebt;
uint256 _loss;
uint256 _gain;
for (uint256 i = 0; i < _newAllocations.length; ++i) {
_strategy = _newAllocations[i].strategy;
_newDebt = uint256(_newAllocations[i].newDebt);
Expand Down Expand Up @@ -172,15 +174,6 @@ contract YieldManager is Governance {
StrategyManager(strategyManager).reportFullProfit(
_strategy
);

// Report profits on the vault.
(uint256 reportedProfit, uint256 reportedLoss) = IVault(
_vault
).process_report(_strategy);

// Track for debt reduction loss checks.
_loss += reportedLoss;
_gain += reportedProfit;
} else if (
// We cannot decrease debt if the strategy has any unrealised losses.
IVault(_vault).assess_share_of_unrealised_losses(
Expand All @@ -189,39 +182,23 @@ contract YieldManager is Governance {
) != 0
) {
// Realize the loss.
(, uint256 reportedLoss) = IVault(_vault).process_report(
_strategy
);
// Track for debt reduction loss checks.
_loss += reportedLoss;
}

// Allocate the new debt.
IVault(_vault).update_debt(_strategy, _newDebt);

// Validate losses based on ending totalAssets adjusted for any realized loss or gain.
uint256 afterAssets = IVault(_vault).totalAssets() +
_loss -
_gain;

// If a loss was realized on just the debt update.
if (afterAssets < _totalAssets) {
// Make sure its within the range.
require(
_totalAssets - afterAssets <=
(_currentDebt * maxDebtUpdateLoss) / MAX_BPS,
"too much loss"
);
IVault(_vault).process_report(_strategy);
}
} else {
// If adding just Allocate the new debt.
IVault(_vault).update_debt(_strategy, _newDebt);
}

uint256 _targetRatio = (_newDebt * MAX_BPS) / _totalAssets;
// Update allocation.
GenericDebtAllocator(allocator).setStrategyDebtRatios(
_strategy,
_targetRatio
);

// Get the new APR
if (_newDebt != 0) {
_afterYield += (aprOracle.getStrategyApr(_strategy, 0) *
_newDebt);
_afterYield += (aprOracle.getStrategyApr(
_strategy,
int256(_newDebt) - int256(_currentDebt)
) * _newDebt);
}
}

Expand Down Expand Up @@ -320,39 +297,31 @@ contract YieldManager is Governance {
address _vault,
Allocation[] memory _newAllocations
) internal {
address allocator = vaultAllocator[_vault];
require(allocator != address(0), "vault not added");
address _strategy;
uint256 _newDebt;
uint256 _loss;
uint256 _gain;
uint256 _currentDebt;
uint256 _totalAssets = IVault(_vault).totalAssets();
for (uint256 i = 0; i < _newAllocations.length; ++i) {
_strategy = _newAllocations[i].strategy;
_newDebt = uint256(_newAllocations[i].newDebt);

// Get the current amount the strategy holds.
uint256 _currentDebt = IVault(_vault)
.strategies(_strategy)
.current_debt;
// Get the debt the strategy current has.
_currentDebt = IVault(_vault).strategies(_strategy).current_debt;

// If no change move to the next strategy.
if (_newDebt == _currentDebt) continue;
if (_newDebt == _currentDebt) {
continue;
}

if (_newDebt < _currentDebt) {
// 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.
StrategyManager(strategyManager).reportFullProfit(
_strategy
);

// Report on the vault.
(uint256 reportedProfit, uint256 reportedLoss) = IVault(
_vault
).process_report(_strategy);

// Track for debt reduction loss checks.
_loss += reportedLoss;
_gain += reportedProfit;
} else if (
// We cannot decrease debt if the strategy has any unrealised losses.
IVault(_vault).assess_share_of_unrealised_losses(
Expand All @@ -361,60 +330,45 @@ contract YieldManager is Governance {
) != 0
) {
// Realize the loss.
(, uint256 reportedLoss) = IVault(_vault).process_report(
_strategy
);
// Track for debt reduction loss checks.
_loss += reportedLoss;
}

// Allocate the new debt.
IVault(_vault).update_debt(_strategy, _newDebt);

// Validate losses based on ending totalAssets adjusted for any realized loss or gain.
uint256 afterAssets = IVault(_vault).totalAssets() +
_loss -
_gain;

// If a loss was realized on just the debt update.
if (afterAssets < _totalAssets) {
// Make sure its within the range.
require(
_totalAssets - afterAssets <=
(_currentDebt * maxDebtUpdateLoss) / MAX_BPS,
"too much loss"
);
IVault(_vault).process_report(_strategy);
}
} else {
// If adding just Allocate the new debt.
IVault(_vault).update_debt(_strategy, _newDebt);
}

uint256 _targetRatio = (_newDebt * MAX_BPS) / _totalAssets;
// Update allocation.
GenericDebtAllocator(allocator).setStrategyDebtRatios(
_strategy,
_targetRatio
);
}
}

/**
* @notice Sets the permission for an allocator.
* @param _address The address of the allocator.
* @param _allowed The permission to set for the allocator.
* @notice Sets the permission for a proposer.
* @param _address The address of the proposer.
* @param _allowed The permission to set for the proposer.
*/
function setAllocator(
function setProposer(
address _address,
bool _allowed
) external onlyGovernance {
allocators[_address] = _allowed;
proposers[_address] = _allowed;

emit UpdateAllocator(_address, _allowed);
emit UpdateProposer(_address, _allowed);
}

/**
* @notice Sets the mapping of vaults allowed.
* @param _vault The address of the _vault.
* @param _allowed The permission to set for the _vault.
* @param _allocator The vault specific debt allocator.
*/
function setVault(address _vault, bool _allowed) external onlyGovernance {
vaults[_vault] = _allowed;
function setVaultAllocator(
address _vault,
address _allocator
) external onlyGovernance {
vaultAllocator[_vault] = _allocator;

emit UpdateVault(_vault, _allowed);
emit UpdateVaultAllocator(_vault, _allocator);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions tests/debtAllocators/test_generic_debt_allocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ def test_set_keepers(generic_debt_allocator, daddy, vault, strategy, user):
assert generic_debt_allocator.keepers(user) == False

with ape.reverts("!governance"):
generic_debt_allocator.setKeepers(user, True, sender=user)
generic_debt_allocator.setKeeper(user, True, sender=user)

tx = generic_debt_allocator.setKeepers(user, True, sender=daddy)
tx = generic_debt_allocator.setKeeper(user, True, sender=daddy)

event = list(tx.decode_logs(generic_debt_allocator.UpdateKeeper))[0]

assert event.keeper == user
assert event.allowed == True
assert generic_debt_allocator.keepers(user) == True

tx = generic_debt_allocator.setKeepers(daddy, False, sender=daddy)
tx = generic_debt_allocator.setKeeper(daddy, False, sender=daddy)

event = list(tx.decode_logs(generic_debt_allocator.UpdateKeeper))[0]

Expand Down Expand Up @@ -306,7 +306,7 @@ def test_update_debt(
assert vault.totalIdle() == 0
assert vault.totalDebt() == amount

generic_debt_allocator.setKeepers(user, True, sender=daddy)
generic_debt_allocator.setKeeper(user, True, sender=daddy)

generic_debt_allocator.update_debt(strategy, 0, sender=user)

Expand Down
Loading

0 comments on commit 637886a

Please sign in to comment.