Skip to content

Commit

Permalink
feat: check debt loss
Browse files Browse the repository at this point in the history
  • Loading branch information
Schlagonia committed Nov 14, 2023
1 parent 1a15619 commit 934d375
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 24 deletions.
73 changes: 56 additions & 17 deletions contracts/debtAllocators/GenericDebtAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,25 @@ import {IVault} from "@yearn-vaults/interfaces/IVault.sol";
* more than its `maxRatio`.
*/
contract GenericDebtAllocator is Governance {
/// @notice An event emitted when a strategies debt ratios are updated.
event UpdatedStrategyDebtRatios(
/// @notice An event emitted when a strategies debt ratios are Updated.
event UpdateStrategyDebtRatios(
address indexed strategy,
uint256 targetRatio,
uint256 maxRatio,
uint256 totalDebtRatio
uint256 newTargetRatio,
uint256 newMaxRatio,
uint256 newTotalDebtRatio
);

/// @notice An event emitted when the minimum change is updated.
event UpdatedMinimumChange(uint256 minimumChange);
/// @notice An event emitted when the minimum change is Updated.
event UpdateMinimumChange(uint256 newMinimumChange);

/// @notice An event emitted when the max base fee is updated.
event UpdatedMaxAcceptableBaseFee(uint256 maxAcceptableBaseFee);
/// @notice An event emitted when the max base fee is Updated.
event UpdateMaxAcceptableBaseFee(uint256 newMaxAcceptableBaseFee);

/// @notice An event emitted when the minimum time to wait is updated.
event UpdatedMinimumWait(uint256 newMinimumWait);
/// @notice An event emitted when the max debt update loss is Updated.
event UpdateMaxDebtUpdateLoss(uint256 newMaxDebtUpdateLoss);

/// @notice An event emitted when the minimum time to wait is Updated.
event UpdateMinimumWait(uint256 newMinimumWait);

/// @notice Struct for each strategies info.
struct Config {
Expand Down Expand Up @@ -80,6 +83,9 @@ contract GenericDebtAllocator is Governance {
/// @notice Time to wait between debt updates.
uint256 public minimumWait;

/// @notice Max loss to accept on debt updates in basis points.
uint256 public maxDebtUpdateLoss;

/// @notice 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;
Expand Down Expand Up @@ -110,6 +116,8 @@ contract GenericDebtAllocator is Governance {
minimumChange = _minimumChange;
// Default max base fee to uint256 max
maxAcceptableBaseFee = type(uint256).max;
// Default max loss on debt updates to 1 BP.
maxDebtUpdateLoss = 1;
}

/**
Expand All @@ -122,17 +130,33 @@ contract GenericDebtAllocator is Governance {
*
* The function signature matches the vault so no update to the
* call data is required.
*
* This will also run checks on losses realized during debt
* updates to assure decreases did not realize profits outside
* of the allowed range.
*/
function update_debt(
address _strategy,
uint256 _targetDebt
) external virtual {
IVault _vault = IVault(vault);
require(
(IVault(_vault).roles(msg.sender) & DEBT_MANAGER) == DEBT_MANAGER,
(_vault.roles(msg.sender) & DEBT_MANAGER) == DEBT_MANAGER,
"not allowed"
);
IVault(_vault).update_debt(_strategy, _targetDebt);
uint256 initialAssets = _vault.totalAssets();
_vault.update_debt(_strategy, _targetDebt);
uint256 afterAssets = _vault.totalAssets();

// If a loss was realized.
if (afterAssets < initialAssets) {
// Make sure its within the range.
require(
initialAssets - afterAssets <=
(initialAssets * maxDebtUpdateLoss) / MAX_BPS,
"too much loss"
);
}
configs[_strategy].lastUpdate = block.timestamp;
}

Expand Down Expand Up @@ -291,7 +315,7 @@ contract GenericDebtAllocator is Governance {

debtRatio = newDebtRatio;

emit UpdatedStrategyDebtRatios(
emit UpdateStrategyDebtRatios(
_strategy,
_targetRatio,
_maxRatio,
Expand All @@ -313,7 +337,22 @@ contract GenericDebtAllocator is Governance {
// Set the new minimum.
minimumChange = _minimumChange;

emit UpdatedMinimumChange(_minimumChange);
emit UpdateMinimumChange(_minimumChange);
}

/**
* @notice Set the max loss in Basis points to allow on debt updates.
* @dev Withdrawing during debt updates use {redeem} which allows for 100% loss.
* This can be used to assure a loss is not realized on redeem outside the tolerance.
* @param _maxDebtUpdateLoss The max loss to accept on debt updates.
*/
function setMaxDebtUpdateLoss(
uint256 _maxDebtUpdateLoss
) external virtual onlyGovernance {
require(_maxDebtUpdateLoss <= MAX_BPS, "higher than max");
maxDebtUpdateLoss = _maxDebtUpdateLoss;

emit UpdateMaxDebtUpdateLoss(_maxDebtUpdateLoss);
}

/**
Expand All @@ -326,7 +365,7 @@ contract GenericDebtAllocator is Governance {
) external virtual onlyGovernance {
minimumWait = _minimumWait;

emit UpdatedMinimumWait(_minimumWait);
emit UpdateMinimumWait(_minimumWait);
}

/**
Expand All @@ -343,6 +382,6 @@ contract GenericDebtAllocator is Governance {
) external virtual onlyGovernance {
maxAcceptableBaseFee = _maxAcceptableBaseFee;

emit UpdatedMaxAcceptableBaseFee(_maxAcceptableBaseFee);
emit UpdateMaxAcceptableBaseFee(_maxAcceptableBaseFee);
}
}
51 changes: 44 additions & 7 deletions tests/test_generic_debt_allocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_setup(generic_debt_allocator_factory, user, strategy, vault):
generic_debt_allocator.shouldUpdateDebt(strategy)


def test_set_minimum(generic_debt_allocator, daddy, vault, strategy, user):
def test_set_minimum_change(generic_debt_allocator, daddy, vault, strategy, user):
assert generic_debt_allocator.configs(strategy) == (0, 0, 0)
assert generic_debt_allocator.minimumChange() == 0

Expand All @@ -37,12 +37,49 @@ def test_set_minimum(generic_debt_allocator, daddy, vault, strategy, user):

tx = generic_debt_allocator.setMinimumChange(minimum, sender=daddy)

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

assert event.minimumChange == minimum
assert event.newMinimumChange == minimum
assert generic_debt_allocator.minimumChange() == minimum


def test_set_minimum_wait(generic_debt_allocator, daddy, vault, strategy, user):
assert generic_debt_allocator.configs(strategy) == (0, 0, 0)
assert generic_debt_allocator.minimumWait() == 0

minimum = int(1e17)

with ape.reverts("!governance"):
generic_debt_allocator.setMinimumWait(minimum, sender=user)

tx = generic_debt_allocator.setMinimumWait(minimum, sender=daddy)

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

assert event.newMinimumWait == minimum
assert generic_debt_allocator.minimumWait() == minimum


def test_set_max_debt_update_loss(generic_debt_allocator, daddy, vault, strategy, user):
assert generic_debt_allocator.configs(strategy) == (0, 0, 0)
assert generic_debt_allocator.maxDebtUpdateLoss() == 1

max = int(69)

with ape.reverts("!governance"):
generic_debt_allocator.setMaxDebtUpdateLoss(max, sender=user)

with ape.reverts("higher than max"):
generic_debt_allocator.setMaxDebtUpdateLoss(10_001, sender=daddy)

tx = generic_debt_allocator.setMaxDebtUpdateLoss(max, sender=daddy)

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

assert event.newMaxDebtUpdateLoss == max
assert generic_debt_allocator.maxDebtUpdateLoss() == max


def test_set_ratios(
generic_debt_allocator, daddy, vault, strategy, create_strategy, user
):
Expand Down Expand Up @@ -83,11 +120,11 @@ def test_set_ratios(
strategy, target, max, sender=daddy
)

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

assert event.targetRatio == target
assert event.maxRatio == max
assert event.totalDebtRatio == target
assert event.newTargetRatio == target
assert event.newMaxRatio == max
assert event.newTotalDebtRatio == target
assert generic_debt_allocator.debtRatio() == target
assert generic_debt_allocator.configs(strategy) == (target, max, 0)

Expand Down

0 comments on commit 934d375

Please sign in to comment.