Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: risk stewards #1

Merged
merged 32 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ffb7215
feat: initial risk steward
brotherlymite Jan 10, 2024
868920e
refactor: some cleanup
brotherlymite Jan 19, 2024
7bb463b
docs: add natspec and some refactoring
brotherlymite Jan 22, 2024
2446bd2
test: caps update
brotherlymite Jan 22, 2024
bfc666e
test: rates test
brotherlymite Jan 22, 2024
a22dbd7
feat: allow relative and absolute percent change
brotherlymite Feb 27, 2024
de28d1a
docs: add readme
brotherlymite Feb 27, 2024
153a9f4
feat: adjust to v3.1.0 changes
brotherlymite Mar 27, 2024
8114f25
refactor: validation
brotherlymite Mar 28, 2024
463ea1f
fix: tests
brotherlymite Mar 28, 2024
f0c87f7
fix: lint
brotherlymite Mar 28, 2024
9385282
feat: add some events
brotherlymite Mar 28, 2024
8b8978f
fix typo
kyzia551 Apr 3, 2024
7daef2b
fix typo - 2
kyzia551 Apr 3, 2024
b2eded8
feat: use constants
brotherlymite Apr 3, 2024
5665499
feat: use custom errors
brotherlymite Apr 3, 2024
2a085e1
fix: events and add some comments
brotherlymite Apr 4, 2024
b6ff254
fix: typo
brotherlymite Apr 4, 2024
11594ca
test: some getters
brotherlymite Apr 15, 2024
5ed1bf5
chore: fix remappings
brotherlymite May 6, 2024
e414a43
fix: remove extra events
brotherlymite May 6, 2024
3875c38
refactor: _validateParamUpdate params to struct
brotherlymite May 6, 2024
a8fbacd
fix: imports
brotherlymite May 6, 2024
9f2a32b
fix: imports
brotherlymite May 6, 2024
cd5f2a2
fix: remappings
brotherlymite May 6, 2024
1646d8f
fix: remapping
brotherlymite May 7, 2024
88578c3
fix: submodule
brotherlymite May 7, 2024
4b59c36
fix: rm extra submodule
brotherlymite May 7, 2024
9bec351
fix: remove forge-std
brotherlymite May 7, 2024
10b82b8
fix: _updateWithinAllowedRange
brotherlymite May 7, 2024
cc6c921
refactor: naming
brotherlymite May 7, 2024
2e0773c
chore: fix license and improve docs
brotherlymite May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 90 additions & 53 deletions src/contracts/RiskSteward.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import {IPoolDataProvider} from 'aave-address-book/AaveV3.sol';
import {Address} from 'solidity-utils/contracts/oz-common/Address.sol';
import {EngineFlags} from 'aave-helpers/v3-config-engine/EngineFlags.sol';
import {RiskStewardErrors} from './libraries/RiskStewardErrors.sol';
import {ConfigConstants} from './libraries/ConfigConstants.sol';
import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol';
import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IRiskSteward} from '../interfaces/IRiskSteward.sol';
Expand Down Expand Up @@ -40,7 +40,7 @@ contract RiskSteward is Ownable, IRiskSteward {
* @dev Modifier preventing anyone, but the council to update risk params.
*/
modifier onlyRiskCouncil() {
require(RISK_COUNCIL == msg.sender, RiskStewardErrors.INVALID_CALLER);
if (RISK_COUNCIL != msg.sender) revert InvalidCaller();
_;
}

Expand All @@ -57,8 +57,8 @@ contract RiskSteward is Ownable, IRiskSteward {
Config memory riskConfig
) {
POOL_DATA_PROVIDER = poolDataProvider;
RISK_COUNCIL = riskCouncil;
CONFIG_ENGINE = engine;
RISK_COUNCIL = riskCouncil;
_riskConfig = riskConfig;
}

Expand Down Expand Up @@ -114,16 +114,13 @@ contract RiskSteward is Ownable, IRiskSteward {
* @param capsUpdate list containing the new supply, borrow caps of the assets
*/
function _validateCapsUpdate(IEngine.CapsUpdate[] calldata capsUpdate) internal view {
require(capsUpdate.length > 0, RiskStewardErrors.NO_ZERO_UPDATES);
if (capsUpdate.length == 0) revert NoZeroUpdates();

for (uint256 i = 0; i < capsUpdate.length; i++) {
address asset = capsUpdate[i].asset;

require(!_restrictedAssets[asset], RiskStewardErrors.ASSET_RESTRICTED);
require(
capsUpdate[i].supplyCap != 0 && capsUpdate[i].borrowCap != 0,
RiskStewardErrors.INVALID_UPDATE_TO_ZERO
);
if (_restrictedAssets[asset]) revert AssetIsRestricted();
if (capsUpdate[i].supplyCap == 0 || capsUpdate[i].borrowCap == 0) revert InvalidUpdateToZero();

(uint256 currentBorrowCap, uint256 currentSupplyCap) = POOL_DATA_PROVIDER.getReserveCaps(
capsUpdate[i].asset
Expand All @@ -134,14 +131,14 @@ contract RiskSteward is Ownable, IRiskSteward {
capsUpdate[i].supplyCap,
_timelocks[asset].supplyCapLastUpdated,
_riskConfig.supplyCap,
true
ConfigConstants.IS_SUPPLY_CAP_CHANGE_RELATIVE
);
_validateParamUpdate(
kyzia551 marked this conversation as resolved.
Show resolved Hide resolved
currentBorrowCap,
capsUpdate[i].borrowCap,
_timelocks[asset].borrowCapLastUpdated,
_riskConfig.borrowCap,
true
ConfigConstants.IS_BORROW_CAP_CHANGE_RELATIVE
);
}
}
Expand All @@ -151,11 +148,11 @@ contract RiskSteward is Ownable, IRiskSteward {
* @param ratesUpdate list containing the new interest rates params of the assets
*/
function _validateRatesUpdate(IEngine.RateStrategyUpdate[] calldata ratesUpdate) internal view {
require(ratesUpdate.length > 0, RiskStewardErrors.NO_ZERO_UPDATES);
if (ratesUpdate.length == 0) revert NoZeroUpdates();

for (uint256 i = 0; i < ratesUpdate.length; i++) {
address asset = ratesUpdate[i].asset;
require(!_restrictedAssets[asset], RiskStewardErrors.ASSET_RESTRICTED);
if (_restrictedAssets[asset]) revert AssetIsRestricted();

(
uint256 currentOptimalUsageRatio,
Expand All @@ -169,28 +166,28 @@ contract RiskSteward is Ownable, IRiskSteward {
ratesUpdate[i].params.optimalUsageRatio,
_timelocks[asset].optimalUsageRatioLastUpdated,
_riskConfig.optimalUsageRatio,
false
ConfigConstants.IS_OPTIMAL_USAGE_CHANGE_RELATIVE
);
_validateParamUpdate(
currentBaseVariableBorrowRate,
ratesUpdate[i].params.baseVariableBorrowRate,
_timelocks[asset].baseVariableRateLastUpdated,
_riskConfig.baseVariableBorrowRate,
false
ConfigConstants.IS_BASE_VARIABLE_BORROW_RATE_CHANGE_RELATIVE
);
_validateParamUpdate(
currentVariableRateSlope1,
ratesUpdate[i].params.variableRateSlope1,
_timelocks[asset].variableRateSlope1LastUpdated,
_riskConfig.variableRateSlope1,
false
ConfigConstants.IS_VARIABLE_RATE_SLOPE_1_CHANGE_RELATIVE
);
_validateParamUpdate(
currentVariableRateSlope2,
ratesUpdate[i].params.variableRateSlope2,
_timelocks[asset].variableRateSlope2LastUpdated,
_riskConfig.variableRateSlope2,
false
ConfigConstants.IS_VARIABLE_RATE_SLOPE_2_CHANGE_RELATIVE
);
}
}
Expand All @@ -202,22 +199,19 @@ contract RiskSteward is Ownable, IRiskSteward {
function _validateCollateralsUpdate(
IEngine.CollateralUpdate[] calldata collateralUpdates
) internal view {
require(collateralUpdates.length > 0, RiskStewardErrors.NO_ZERO_UPDATES);
if (collateralUpdates.length == 0) revert NoZeroUpdates();

for (uint256 i = 0; i < collateralUpdates.length; i++) {
address asset = collateralUpdates[i].asset;
require(!_restrictedAssets[asset], RiskStewardErrors.ASSET_RESTRICTED);
require(
collateralUpdates[i].liqProtocolFee == EngineFlags.KEEP_CURRENT,
RiskStewardErrors.PARAM_CHANGE_NOT_ALLOWED
);
require(
collateralUpdates[i].ltv != 0 &&
collateralUpdates[i].liqThreshold != 0 &&
collateralUpdates[i].liqThreshold != 0 &&
collateralUpdates[i].debtCeiling != 0,
RiskStewardErrors.INVALID_UPDATE_TO_ZERO
);

if (_restrictedAssets[asset]) revert AssetIsRestricted();
if (collateralUpdates[i].liqProtocolFee != EngineFlags.KEEP_CURRENT) revert ParamChangeNotAllowed();
if (
collateralUpdates[i].ltv == 0 ||
collateralUpdates[i].liqThreshold == 0 ||
collateralUpdates[i].liqBonus == 0 ||
collateralUpdates[i].debtCeiling == 0
) revert InvalidUpdateToZero();

(
,
Expand All @@ -238,28 +232,28 @@ contract RiskSteward is Ownable, IRiskSteward {
collateralUpdates[i].ltv,
_timelocks[asset].ltvLastUpdated,
_riskConfig.ltv,
false
ConfigConstants.IS_LTV_CHANGE_RELATIVE
);
_validateParamUpdate(
currentLiquidationThreshold,
collateralUpdates[i].liqThreshold,
_timelocks[asset].liquidationThresholdLastUpdated,
_riskConfig.liquidationThreshold,
false
ConfigConstants.IS_LIQUDATION_THRESHOLD_CHANGE_RELATIVE
);
_validateParamUpdate(
currentLiquidationBonus - 100_00, // as the definition is 100% + x%, and config engine takes into account x% for simplicity.
collateralUpdates[i].liqBonus,
_timelocks[asset].liquidationBonusLastUpdated,
_riskConfig.liquidationBonus,
false
ConfigConstants.IS_LIQUIDATION_BONUS_CHANGE_RELATIVE
);
_validateParamUpdate(
currentDebtCeiling / 100, // as the definition is with 2 decimals, and config engine does not take the decimals into account.
collateralUpdates[i].debtCeiling,
_timelocks[asset].debtCeilingLastUpdated,
_riskConfig.debtCeiling,
true
ConfigConstants.IS_DEBT_CEILING_CHANGE_RELATIVE
);
}
}
Expand All @@ -280,19 +274,15 @@ contract RiskSteward is Ownable, IRiskSteward {
) internal view {
if (newParamValue == EngineFlags.KEEP_CURRENT) return;

require(
block.timestamp - lastUpdated > riskConfig.minDelay,
RiskStewardErrors.DEBOUNCE_NOT_RESPECTED
);
require(
_updateWithinAllowedRange(
if (block.timestamp - lastUpdated < riskConfig.minDelay) revert DebounceNotRespected();
if (
!_updateWithinAllowedRange(
currentParamValue,
newParamValue,
riskConfig.maxPercentChange,
isChangeRelative
),
RiskStewardErrors.UPDATE_NOT_IN_RANGE
);
)
) revert UpdateNotInRange();
}

/**
Expand All @@ -305,12 +295,20 @@ contract RiskSteward is Ownable, IRiskSteward {

if (capsUpdate[i].supplyCap != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].supplyCapLastUpdated = uint40(block.timestamp);
emit SupplyCapUpdated(asset, capsUpdate[i].supplyCap);
emit SupplyCapUpdated(
asset,
capsUpdate[i].supplyCap,
uint40(block.timestamp) + _riskConfig.supplyCap.minDelay
);
}

if (capsUpdate[i].borrowCap != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].borrowCapLastUpdated = uint40(block.timestamp);
emit BorrowCapUpdated(asset, capsUpdate[i].borrowCap);
emit BorrowCapUpdated(
asset,
capsUpdate[i].borrowCap,
uint40(block.timestamp) + _riskConfig.borrowCap.minDelay
);
}
}

Expand All @@ -329,22 +327,38 @@ contract RiskSteward is Ownable, IRiskSteward {

if (ratesUpdate[i].params.optimalUsageRatio != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].optimalUsageRatioLastUpdated = uint40(block.timestamp);
emit OptimalUsageRatioUpdated(asset, ratesUpdate[i].params.optimalUsageRatio);
emit OptimalUsageRatioUpdated(
asset,
ratesUpdate[i].params.optimalUsageRatio,
uint40(block.timestamp) + _riskConfig.optimalUsageRatio.minDelay
);
}

if (ratesUpdate[i].params.baseVariableBorrowRate != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].baseVariableRateLastUpdated = uint40(block.timestamp);
emit BaseVariableBorrowRateUpdated(asset, ratesUpdate[i].params.baseVariableBorrowRate);
emit BaseVariableBorrowRateUpdated(
asset,
ratesUpdate[i].params.baseVariableBorrowRate,
uint40(block.timestamp) + _riskConfig.baseVariableBorrowRate.minDelay
);
}

if (ratesUpdate[i].params.variableRateSlope1 != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].variableRateSlope1LastUpdated = uint40(block.timestamp);
emit VariableRateSlope1Updated(asset, ratesUpdate[i].params.variableRateSlope1);
emit VariableRateSlope1Updated(
asset,
ratesUpdate[i].params.variableRateSlope1,
uint40(block.timestamp) + _riskConfig.variableRateSlope1.minDelay
);
}

if (ratesUpdate[i].params.variableRateSlope2 != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].variableRateSlope2LastUpdated = uint40(block.timestamp);
emit VariableRateSlope2Updated(asset, ratesUpdate[i].params.variableRateSlope2);
emit VariableRateSlope2Updated(
asset,
ratesUpdate[i].params.variableRateSlope2,
uint40(block.timestamp) + _riskConfig.variableRateSlope2.minDelay
);
}
}

Expand All @@ -363,19 +377,38 @@ contract RiskSteward is Ownable, IRiskSteward {

if (collateralUpdates[i].ltv != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].ltvLastUpdated = uint40(block.timestamp);
emit LtvUpdated(asset, collateralUpdates[i].ltv);
emit LtvUpdated(
asset,
collateralUpdates[i].ltv,
uint40(block.timestamp) + _riskConfig.ltv.minDelay
);
}

if (collateralUpdates[i].liqThreshold != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].liquidationThresholdLastUpdated = uint40(block.timestamp);
emit LiquidationThresholdUpdated(asset, collateralUpdates[i].liqThreshold);
emit LiquidationThresholdUpdated(
asset,
collateralUpdates[i].liqThreshold,
uint40(block.timestamp) + _riskConfig.liquidationThreshold.minDelay
);
}

if (collateralUpdates[i].liqBonus != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].liquidationBonusLastUpdated = uint40(block.timestamp);
emit LiquidationBonusUpdated(asset, collateralUpdates[i].liqBonus);
emit LiquidationBonusUpdated(
asset,
collateralUpdates[i].liqBonus,
uint40(block.timestamp) + _riskConfig.liquidationBonus.minDelay
);
}

if (collateralUpdates[i].debtCeiling != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].debtCeilingLastUpdated = uint40(block.timestamp);
emit DebtCeilingUpdated(asset, collateralUpdates[i].debtCeiling);
emit DebtCeilingUpdated(
asset,
collateralUpdates[i].debtCeiling,
uint40(block.timestamp) + _riskConfig.debtCeiling.minDelay
);
}
}

Expand Down Expand Up @@ -431,9 +464,13 @@ contract RiskSteward is Ownable, IRiskSteward {
uint256 maxPercentChange,
bool isChangeRelative
) internal pure returns (bool) {
// diff denotes the difference between the from and to values, ensuring it is a positive value always
int256 diff = int256(from) - int256(to);
if (diff < 0) diff = -diff;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// diff denotes the difference between the from and to values, ensuring it is a positive value always
int256 diff = int256(from) - int256(to);
if (diff < 0) diff = -diff;
uint256 diff = from > to ? from - to : to - from;

that way we will sure that it's positive, because it's uint


// maxDiff denotes the max permitted difference on both the upper and lower bounds, if the maxPercentChange is relative in value
// we calculate the max permitted difference using the maxPercentChange and the from value, otherwise if the maxPercentChange is absolute in value
// the max permitted difference is the maxPercentChange itself
uint256 maxDiff = isChangeRelative ? (maxPercentChange * from) / BPS_MAX : maxPercentChange;
if (uint256(diff) > maxDiff) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (uint256(diff) > maxDiff) return false;
if (diff > maxDiff) return false;

following previous change

return true;
Expand Down
50 changes: 50 additions & 0 deletions src/contracts/libraries/ConfigConstants.sol
kyzia551 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title ConfigConstants
* @author BGD labs
* @notice Library storing the configuration constants for the risk params.
*/
library ConfigConstants {
/**
* @notice The permitted percentage change in supply cap is determined by the relative percentage value.
*/
bool public constant IS_SUPPLY_CAP_CHANGE_RELATIVE = true;
/**
* @notice The permitted percentage change in borrow cap is determined by the relative percentage value.
*/
bool public constant IS_BORROW_CAP_CHANGE_RELATIVE = true;
/**
* @notice The permitted percentage change in optimal usage ratio is determined by the absolute percentage value.
*/
bool public constant IS_OPTIMAL_USAGE_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in base variable borrow rate is determined by the absolute percentage value.
*/
bool public constant IS_BASE_VARIABLE_BORROW_RATE_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in variable rate slope 1 is determined by the absolute percentage value.
*/
bool public constant IS_VARIABLE_RATE_SLOPE_1_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in variable rate slope 2 is determined by the absolute percentage value.
*/
bool public constant IS_VARIABLE_RATE_SLOPE_2_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in ltv is determined by the absolute percentage value.
*/
bool public constant IS_LTV_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in liquidation threshold is determined by the absolute percentage value.
*/
bool public constant IS_LIQUDATION_THRESHOLD_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in liquidation bonus is determined by the absolute percentage value.
*/
bool public constant IS_LIQUIDATION_BONUS_CHANGE_RELATIVE = false;
/**
* @notice The permitted percentage change in debt ceiling is determined by the relative percentage value.
*/
bool public constant IS_DEBT_CEILING_CHANGE_RELATIVE = true;
}
Loading
Loading