Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/contracts/EdgeRiskStewardRates.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import './RiskSteward.sol';
/**
* @title EdgeRiskStewardRates
* @author BGD labs
* @notice Contract to manage the interest rates params within configured bound on aave v3 pool.
* To be triggered by the Aave Steward Injector Contract in a automated way via the Edge Risk Oracle.
*/
contract EdgeRiskStewardRates is RiskSteward {
/**
* @param pool the aave pool to be controlled by the steward
* @param engine the config engine to be used by the steward
* @param riskCouncil the safe address of the council being able to interact with the steward
* @param owner the owner of the risk steward being able to set configs and mark items as restricted
* @param riskConfig the risk configuration to setup for each individual risk param
*/
constructor(
address pool,
address engine,
address riskCouncil,
address owner,
Config memory riskConfig
) RiskSteward(pool, engine, riskCouncil, owner, riskConfig) {}
/// @inheritdoc IRiskSteward
function updateCaps(IEngine.CapsUpdate[] calldata) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
/// @inheritdoc IRiskSteward
function updateCollateralSide(
IEngine.CollateralUpdate[] calldata
) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
/// @inheritdoc IRiskSteward
function updateEModeCategories(
IEngine.EModeCategoryUpdate[] calldata
) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
/// @inheritdoc IRiskSteward
function updateLstPriceCaps(
PriceCapLstUpdate[] calldata
) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
/// @inheritdoc IRiskSteward
function updateStablePriceCaps(
PriceCapStableUpdate[] calldata
) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
/// @inheritdoc IRiskSteward
function updatePendleDiscountRates(
DiscountRatePendleUpdate[] calldata
) external virtual override onlyRiskCouncil {
revert UpdateNotAllowed();
}
}
"
},
"src/contracts/RiskSteward.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {ReserveConfiguration, DataTypes} from 'aave-v3-origin/src/contracts/protocol/libraries/configuration/ReserveConfiguration.sol';
import {IPool} from 'aave-address-book/AaveV3.sol';
import {Address} from 'openzeppelin-contracts/contracts/utils/Address.sol';
import {SafeCast} from 'openzeppelin-contracts/contracts/utils/math/SafeCast.sol';
import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {Ownable} from 'openzeppelin-contracts/contracts/access/Ownable.sol';
import {Strings} from 'openzeppelin-contracts/contracts/utils/Strings.sol';
import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IRiskSteward} from '../interfaces/IRiskSteward.sol';
import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/src/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
import {IPriceCapAdapter} from 'aave-capo/interfaces/IPriceCapAdapter.sol';
import {IPriceCapAdapterStable} from 'aave-capo/interfaces/IPriceCapAdapterStable.sol';
import {IPendlePriceCapAdapter} from 'aave-capo/interfaces/IPendlePriceCapAdapter.sol';
/**
* @title RiskSteward
* @author BGD labs
* @notice Contract to manage the risk params within configured bound on aave v3 pool:
* This contract can update the following risk params: caps, ltv, liqThreshold, liqBonus, debtCeiling, interest rates params.
*/
contract RiskSteward is Ownable, IRiskSteward {
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using Strings for string;
using Address for address;
using SafeCast for uint256;
using SafeCast for int256;
/// @inheritdoc IRiskSteward
IEngine public immutable CONFIG_ENGINE;
/// @inheritdoc IRiskSteward
IPool public immutable POOL;
/// @inheritdoc IRiskSteward
address public immutable RISK_COUNCIL;
uint256 internal constant BPS_MAX = 100_00;
Config internal _riskConfig;
mapping(address => Debounce) internal _timelocks;
mapping(uint8 => EModeDebounce) internal _eModeTimelocks;
mapping(address => bool) internal _restrictedAddresses;
mapping(uint8 => bool) internal _restrictedEModes;
/**
* @dev Modifier preventing anyone, but the council to update risk params.
*/
modifier onlyRiskCouncil() {
if (RISK_COUNCIL != msg.sender) revert InvalidCaller();
_;
}
/**
* @param pool The aave pool to be controlled by the steward
* @param engine the config engine to be used by the steward
* @param riskCouncil the safe address of the council being able to interact with the steward
* @param owner the owner of the risk steward being able to set configs and mark items as restricted
* @param riskConfig the risk configuration to setup for each individual risk param
*/
constructor(
address pool,
address engine,
address riskCouncil,
address owner,
Config memory riskConfig
) Ownable(owner) {
POOL = IPool(pool);
CONFIG_ENGINE = IEngine(engine);
RISK_COUNCIL = riskCouncil;
_riskConfig = riskConfig;
}
/// @inheritdoc IRiskSteward
function updateCaps(IEngine.CapsUpdate[] calldata capsUpdate) external virtual onlyRiskCouncil {
_validateCapsUpdate(capsUpdate);
_updateCaps(capsUpdate);
}
/// @inheritdoc IRiskSteward
function updateRates(
IEngine.RateStrategyUpdate[] calldata ratesUpdate
) external virtual onlyRiskCouncil {
_validateRatesUpdate(ratesUpdate);
_updateRates(ratesUpdate);
}
/// @inheritdoc IRiskSteward
function updateCollateralSide(
IEngine.CollateralUpdate[] calldata collateralUpdates
) external virtual onlyRiskCouncil {
_validateCollateralsUpdate(collateralUpdates);
_updateCollateralSide(collateralUpdates);
}
/// @inheritdoc IRiskSteward
function updateEModeCategories(
IEngine.EModeCategoryUpdate[] calldata eModeCategoryUpdates
) external virtual onlyRiskCouncil {
_validateEModeCategoryUpdate(eModeCategoryUpdates);
_updateEModeCategories(eModeCategoryUpdates);
}
/// @inheritdoc IRiskSteward
function updateLstPriceCaps(
PriceCapLstUpdate[] calldata priceCapUpdates
) external virtual onlyRiskCouncil {
_validatePriceCapUpdate(priceCapUpdates);
_updateLstPriceCaps(priceCapUpdates);
}
/// @inheritdoc IRiskSteward
function updateStablePriceCaps(
PriceCapStableUpdate[] calldata priceCapUpdates
) external virtual onlyRiskCouncil {
_validatePriceCapStableUpdate(priceCapUpdates);
_updateStablePriceCaps(priceCapUpdates);
}
/// @inheritdoc IRiskSteward
function updatePendleDiscountRates(
DiscountRatePendleUpdate[] calldata discountRateUpdates
) external virtual onlyRiskCouncil {
_validatePendleDiscountRateUpdate(discountRateUpdates);
_updatePendleDiscountRates(discountRateUpdates);
}
/// @inheritdoc IRiskSteward
function getTimelock(address asset) external view returns (Debounce memory) {
return _timelocks[asset];
}
/// @inheritdoc IRiskSteward
function getEModeTimelock(uint8 eModeCategoryId) external view returns (EModeDebounce memory) {
return _eModeTimelocks[eModeCategoryId];
}
/// @inheritdoc IRiskSteward
function setRiskConfig(Config calldata riskConfig) external onlyOwner {
_riskConfig = riskConfig;
emit RiskConfigSet(riskConfig);
}
/// @inheritdoc IRiskSteward
function getRiskConfig() external view returns (Config memory) {
return _riskConfig;
}
/// @inheritdoc IRiskSteward
function isAddressRestricted(address contractAddress) external view returns (bool) {
return _restrictedAddresses[contractAddress];
}
/// @inheritdoc IRiskSteward
function setAddressRestricted(address contractAddress, bool isRestricted) external onlyOwner {
_restrictedAddresses[contractAddress] = isRestricted;
emit AddressRestricted(contractAddress, isRestricted);
}
/// @inheritdoc IRiskSteward
function isEModeCategoryRestricted(uint8 eModeCategoryId) external view returns (bool) {
return _restrictedEModes[eModeCategoryId];
}
/// @inheritdoc IRiskSteward
function setEModeCategoryRestricted(uint8 eModeCategoryId, bool isRestricted) external onlyOwner {
_restrictedEModes[eModeCategoryId] = isRestricted;
emit EModeRestricted(eModeCategoryId, isRestricted);
}
/**
* @notice method to validate the caps update
* @param capsUpdate list containing the new supply, borrow caps of the assets
*/
function _validateCapsUpdate(IEngine.CapsUpdate[] calldata capsUpdate) internal view {
if (capsUpdate.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < capsUpdate.length; i++) {
address asset = capsUpdate[i].asset;
if (_restrictedAddresses[asset]) revert AssetIsRestricted();
if (capsUpdate[i].supplyCap == 0 || capsUpdate[i].borrowCap == 0)
revert InvalidUpdateToZero();
(uint256 currentBorrowCap, uint256 currentSupplyCap) = POOL.getConfiguration(asset).getCaps();
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentSupplyCap,
newValue: capsUpdate[i].supplyCap,
lastUpdated: _timelocks[asset].supplyCapLastUpdated,
riskConfig: _riskConfig.capConfig.supplyCap,
isChangeRelative: true
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentBorrowCap,
newValue: capsUpdate[i].borrowCap,
lastUpdated: _timelocks[asset].borrowCapLastUpdated,
riskConfig: _riskConfig.capConfig.borrowCap,
isChangeRelative: true
})
);
}
}
/**
* @notice method to validate the interest rates update
* @param ratesUpdate list containing the new interest rates params of the assets
*/
function _validateRatesUpdate(IEngine.RateStrategyUpdate[] calldata ratesUpdate) internal view {
if (ratesUpdate.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < ratesUpdate.length; i++) {
address asset = ratesUpdate[i].asset;
if (_restrictedAddresses[asset]) revert AssetIsRestricted();
(
uint256 currentOptimalUsageRatio,
uint256 currentBaseVariableBorrowRate,
uint256 currentVariableRateSlope1,
uint256 currentVariableRateSlope2
) = _getInterestRatesForAsset(asset);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentOptimalUsageRatio,
newValue: ratesUpdate[i].params.optimalUsageRatio,
lastUpdated: _timelocks[asset].optimalUsageRatioLastUpdated,
riskConfig: _riskConfig.rateConfig.optimalUsageRatio,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentBaseVariableBorrowRate,
newValue: ratesUpdate[i].params.baseVariableBorrowRate,
lastUpdated: _timelocks[asset].baseVariableRateLastUpdated,
riskConfig: _riskConfig.rateConfig.baseVariableBorrowRate,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentVariableRateSlope1,
newValue: ratesUpdate[i].params.variableRateSlope1,
lastUpdated: _timelocks[asset].variableRateSlope1LastUpdated,
riskConfig: _riskConfig.rateConfig.variableRateSlope1,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentVariableRateSlope2,
newValue: ratesUpdate[i].params.variableRateSlope2,
lastUpdated: _timelocks[asset].variableRateSlope2LastUpdated,
riskConfig: _riskConfig.rateConfig.variableRateSlope2,
isChangeRelative: false
})
);
}
}
/**
* @notice method to validate the collaterals update
* @param collateralUpdates list containing the new collateral updates of the assets
*/
function _validateCollateralsUpdate(
IEngine.CollateralUpdate[] calldata collateralUpdates
) internal view virtual {
if (collateralUpdates.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < collateralUpdates.length; i++) {
address asset = collateralUpdates[i].asset;
if (_restrictedAddresses[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();
DataTypes.ReserveConfigurationMap memory configuration = POOL.getConfiguration(asset);
(
uint256 currentLtv,
uint256 currentLiquidationThreshold,
uint256 currentLiquidationBonus,
,
) = configuration.getParams();
uint256 currentDebtCeiling = configuration.getDebtCeiling();
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentLtv,
newValue: collateralUpdates[i].ltv,
lastUpdated: _timelocks[asset].ltvLastUpdated,
riskConfig: _riskConfig.collateralConfig.ltv,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentLiquidationThreshold,
newValue: collateralUpdates[i].liqThreshold,
lastUpdated: _timelocks[asset].liquidationThresholdLastUpdated,
riskConfig: _riskConfig.collateralConfig.liquidationThreshold,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentLiquidationBonus - 100_00, // as the definition is 100% + x%, and config engine takes into account x% for simplicity.
newValue: collateralUpdates[i].liqBonus,
lastUpdated: _timelocks[asset].liquidationBonusLastUpdated,
riskConfig: _riskConfig.collateralConfig.liquidationBonus,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentDebtCeiling / 100, // as the definition is with 2 decimals, and config engine does not take the decimals into account.
newValue: collateralUpdates[i].debtCeiling,
lastUpdated: _timelocks[asset].debtCeilingLastUpdated,
riskConfig: _riskConfig.collateralConfig.debtCeiling,
isChangeRelative: true
})
);
}
}
/**
* @notice method to validate the eMode category update
* @param eModeCategoryUpdates list containing the new eMode category updates
*/
function _validateEModeCategoryUpdate(
IEngine.EModeCategoryUpdate[] calldata eModeCategoryUpdates
) internal view {
if (eModeCategoryUpdates.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < eModeCategoryUpdates.length; i++) {
uint8 eModeId = eModeCategoryUpdates[i].eModeCategory;
if (_restrictedEModes[eModeId]) revert EModeIsRestricted();
if (!eModeCategoryUpdates[i].label.equal(EngineFlags.KEEP_CURRENT_STRING))
revert ParamChangeNotAllowed();
if (
eModeCategoryUpdates[i].ltv == 0 ||
eModeCategoryUpdates[i].liqThreshold == 0 ||
eModeCategoryUpdates[i].liqBonus == 0
) revert InvalidUpdateToZero();
DataTypes.CollateralConfig memory currentEmodeConfig = POOL.getEModeCategoryCollateralConfig(eModeId);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentEmodeConfig.ltv,
newValue: eModeCategoryUpdates[i].ltv,
lastUpdated: _eModeTimelocks[eModeId].eModeLtvLastUpdated,
riskConfig: _riskConfig.eModeConfig.ltv,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentEmodeConfig.liquidationThreshold,
newValue: eModeCategoryUpdates[i].liqThreshold,
lastUpdated: _eModeTimelocks[eModeId].eModeLiquidationThresholdLastUpdated,
riskConfig: _riskConfig.eModeConfig.liquidationThreshold,
isChangeRelative: false
})
);
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentEmodeConfig.liquidationBonus - 100_00, // as the definition is 100% + x%, and config engine takes into account x% for simplicity.
newValue: eModeCategoryUpdates[i].liqBonus,
lastUpdated: _eModeTimelocks[eModeId].eModeLiquidationBonusLastUpdated,
riskConfig: _riskConfig.eModeConfig.liquidationBonus,
isChangeRelative: false
})
);
}
}
/**
* @notice method to validate the oracle price caps update
* @param priceCapsUpdate list containing the new price cap params for the oracles
*/
function _validatePriceCapUpdate(PriceCapLstUpdate[] calldata priceCapsUpdate) internal view {
if (priceCapsUpdate.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < priceCapsUpdate.length; i++) {
address oracle = priceCapsUpdate[i].oracle;
if (_restrictedAddresses[oracle]) revert OracleIsRestricted();
if (
priceCapsUpdate[i].priceCapUpdateParams.snapshotRatio == 0 ||
priceCapsUpdate[i].priceCapUpdateParams.snapshotTimestamp == 0 ||
priceCapsUpdate[i].priceCapUpdateParams.maxYearlyRatioGrowthPercent == 0
) revert InvalidUpdateToZero();
// get current rate
uint256 currentMaxYearlyGrowthPercent = IPriceCapAdapter(oracle)
.getMaxYearlyGrowthRatePercent();
uint104 currentRatio = IPriceCapAdapter(oracle).getRatio().toUint256().toUint104();
// check that snapshotRatio is less or equal than current one
if (priceCapsUpdate[i].priceCapUpdateParams.snapshotRatio > currentRatio)
revert UpdateNotInRange();
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentMaxYearlyGrowthPercent,
newValue: priceCapsUpdate[i].priceCapUpdateParams.maxYearlyRatioGrowthPercent,
lastUpdated: _timelocks[oracle].priceCapLastUpdated,
riskConfig: _riskConfig.priceCapConfig.priceCapLst,
isChangeRelative: true
})
);
}
}
/**
* @notice method to validate the oracle stable price caps update
* @param priceCapsUpdate list containing the new price cap values for the oracles
*/
function _validatePriceCapStableUpdate(
PriceCapStableUpdate[] calldata priceCapsUpdate
) internal view {
if (priceCapsUpdate.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < priceCapsUpdate.length; i++) {
address oracle = priceCapsUpdate[i].oracle;
if (_restrictedAddresses[oracle]) revert OracleIsRestricted();
if (priceCapsUpdate[i].priceCap == 0) revert InvalidUpdateToZero();
// get current rate
int256 currentPriceCap = IPriceCapAdapterStable(oracle).getPriceCap();
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentPriceCap.toUint256(),
newValue: priceCapsUpdate[i].priceCap,
lastUpdated: _timelocks[oracle].priceCapLastUpdated,
riskConfig: _riskConfig.priceCapConfig.priceCapStable,
isChangeRelative: true
})
);
}
}
/**
* @notice method to validate the pendle oracle discount rate update
* @param discountRateUpdate list containing the new discount rate values for the pendle oracles
*/
function _validatePendleDiscountRateUpdate(
DiscountRatePendleUpdate[] calldata discountRateUpdate
) internal view {
if (discountRateUpdate.length == 0) revert NoZeroUpdates();
for (uint256 i = 0; i < discountRateUpdate.length; i++) {
address oracle = discountRateUpdate[i].oracle;
if (_restrictedAddresses[oracle]) revert OracleIsRestricted();
if (discountRateUpdate[i].discountRate == 0) revert InvalidUpdateToZero();
// get current rate
uint256 currentDiscount = IPendlePriceCapAdapter(oracle).discountRatePerYear();
_validateParamUpdate(
ParamUpdateValidationInput({
currentValue: currentDiscount,
newValue: discountRateUpdate[i].discountRate,
lastUpdated: _timelocks[oracle].priceCapLastUpdated,
riskConfig: _riskConfig.priceCapConfig.discountRatePendle,
isChangeRelative: false
})
);
}
}
/**
* @notice method to validate the risk param update is within the allowed bound and the debounce is respected
* @param validationParam struct containing values used for validation of the risk param update
*/
function _validateParamUpdate(ParamUpdateValidationInput memory validationParam) internal view {
if (validationParam.newValue == EngineFlags.KEEP_CURRENT) return;
if (block.timestamp - validationParam.lastUpdated < validationParam.riskConfig.minDelay)
revert DebounceNotRespected();
if (
!_updateWithinAllowedRange(
validationParam.currentValue,
validationParam.newValue,
validationParam.riskConfig.maxPercentChange,
validationParam.isChangeRelative
)
) revert UpdateNotInRange();
}
/**
* @notice method to update the borrow / supply caps using the config engine and updates the debounce
* @param capsUpdate list containing the new supply, borrow caps of the assets
*/
function _updateCaps(IEngine.CapsUpdate[] calldata capsUpdate) internal {
for (uint256 i = 0; i < capsUpdate.length; i++) {
address asset = capsUpdate[i].asset;
if (capsUpdate[i].supplyCap != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].supplyCapLastUpdated = uint40(block.timestamp);
}
if (capsUpdate[i].borrowCap != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].borrowCapLastUpdated = uint40(block.timestamp);
}
}
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateCaps.selector, capsUpdate)
);
}
/**
* @notice method to update the interest rates params using the config engine and updates the debounce
* @param ratesUpdate list containing the new interest rates params of the assets
*/
function _updateRates(IEngine.RateStrategyUpdate[] calldata ratesUpdate) internal {
for (uint256 i = 0; i < ratesUpdate.length; i++) {
address asset = ratesUpdate[i].asset;
if (ratesUpdate[i].params.optimalUsageRatio != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].optimalUsageRatioLastUpdated = uint40(block.timestamp);
}
if (ratesUpdate[i].params.baseVariableBorrowRate != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].baseVariableRateLastUpdated = uint40(block.timestamp);
}
if (ratesUpdate[i].params.variableRateSlope1 != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].variableRateSlope1LastUpdated = uint40(block.timestamp);
}
if (ratesUpdate[i].params.variableRateSlope2 != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].variableRateSlope2LastUpdated = uint40(block.timestamp);
}
}
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateRateStrategies.selector, ratesUpdate)
);
}
/**
* @notice method to update the collateral side params using the config engine and updates the debounce
* @param collateralUpdates list containing the new collateral updates of the assets
*/
function _updateCollateralSide(IEngine.CollateralUpdate[] calldata collateralUpdates) internal {
for (uint256 i = 0; i < collateralUpdates.length; i++) {
address asset = collateralUpdates[i].asset;
if (collateralUpdates[i].ltv != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].ltvLastUpdated = uint40(block.timestamp);
}
if (collateralUpdates[i].liqThreshold != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].liquidationThresholdLastUpdated = uint40(block.timestamp);
}
if (collateralUpdates[i].liqBonus != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].liquidationBonusLastUpdated = uint40(block.timestamp);
}
if (collateralUpdates[i].debtCeiling != EngineFlags.KEEP_CURRENT) {
_timelocks[asset].debtCeilingLastUpdated = uint40(block.timestamp);
}
}
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateCollateralSide.selector, collateralUpdates)
);
}
/**
* @notice method to update the eMode category params using the config engine and updates the debounce
* @param eModeCategoryUpdates list containing the new eMode category params of the eMode category id
*/
function _updateEModeCategories(IEngine.EModeCategoryUpdate[] calldata eModeCategoryUpdates) internal {
for (uint256 i = 0; i < eModeCategoryUpdates.length; i++) {
uint8 eModeId = eModeCategoryUpdates[i].eModeCategory;
if (eModeCategoryUpdates[i].ltv != EngineFlags.KEEP_CURRENT) {
_eModeTimelocks[eModeId].eModeLtvLastUpdated = uint40(block.timestamp);
}
if (eModeCategoryUpdates[i].liqThreshold != EngineFlags.KEEP_CURRENT) {
_eModeTimelocks[eModeId].eModeLiquidationThresholdLastUpdated = uint40(block.timestamp);
}
if (eModeCategoryUpdates[i].liqBonus != EngineFlags.KEEP_CURRENT) {
_eModeTimelocks[eModeId].eModeLiquidationBonusLastUpdated = uint40(block.timestamp);
}
}
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateEModeCategories.selector, eModeCategoryUpdates)
);
}
/**
* @notice method to update the oracle price caps update
* @param priceCapsUpdate list containing the new price cap params for the oracles
*/
function _updateLstPriceCaps(PriceCapLstUpdate[] calldata priceCapsUpdate) internal {
for (uint256 i = 0; i < priceCapsUpdate.length; i++) {
address oracle = priceCapsUpdate[i].oracle;
_timelocks[oracle].priceCapLastUpdated = uint40(block.timestamp);
IPriceCapAdapter(oracle).setCapParameters(priceCapsUpdate[i].priceCapUpdateParams);
if (IPriceCapAdapter(oracle).isCapped()) revert InvalidPriceCapUpdate();
}
}
/**
* @notice method to update the oracle stable price caps update
* @param priceCapsUpdate list containing the new price cap values for the oracles
*/
function _updateStablePriceCaps(PriceCapStableUpdate[] calldata priceCapsUpdate) internal {
for (uint256 i = 0; i < priceCapsUpdate.length; i++) {
address oracle = priceCapsUpdate[i].oracle;
_timelocks[oracle].priceCapLastUpdated = uint40(block.timestamp);
IPriceCapAdapterStable(oracle).setPriceCap(priceCapsUpdate[i].priceCap.toInt256());
}
}
/**
* @notice method to update the pendle oracle discount rate
* @param discountRateUpdate list containing the new discount rate values for the pendle oracles
*/
function _updatePendleDiscountRates(DiscountRatePendleUpdate[] calldata discountRateUpdate) internal {
for (uint256 i = 0; i < discountRateUpdate.length; i++) {
address oracle = discountRateUpdate[i].oracle;
_timelocks[oracle].priceCapLastUpdated = uint40(block.timestamp);
IPendlePriceCapAdapter(oracle).setDiscountRatePerYear(discountRateUpdate[i].discountRate.toUint64());
}
}
/**
* @notice method to fetch the current interest rate params of the asset
* @param asset the address of the underlying asset
* @return optimalUsageRatio the current optimal usage ratio of the asset
* @return baseVariableBorrowRate the current base variable borrow rate of the asset
* @return variableRateSlope1 the current variable rate slope 1 of the asset
* @return variableRateSlope2 the current variable rate slope 2 of the asset
*/
function _getInterestRatesForAsset(
address asset
)
internal
view
returns (
uint256 optimalUsageRatio,
uint256 baseVariableBorrowRate,
uint256 variableRateSlope1,
uint256 variableRateSlope2
)
{
address rateStrategyAddress = POOL.getReserveData(asset).interestRateStrategyAddress;
IDefaultInterestRateStrategyV2.InterestRateData
memory interestRateData = IDefaultInterestRateStrategyV2(rateStrategyAddress)
.getInterestRateDataBps(asset);
return (
interestRateData.optimalUsageRatio,
interestRateData.baseVariableBorrowRate,
interestRateData.variableRateSlope1,
interestRateData.variableRateSlope2
);
}
/**
* @notice Ensures the risk param update is within the allowed range
* @param from current risk param value
* @param to new updated risk param value
* @param maxPercentChange the max percent change allowed
* @param isChangeRelative true, if maxPercentChange is relative in value, false if maxPercentChange
* is absolute in value.
* @return bool true, if difference is within the maxPercentChange
*/
function _updateWithinAllowedRange(
uint256 from,
uint256 to,
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
uint256 diff = from > to ? from - to : to - from;
// 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 (diff > maxDiff) return false;
return true;
}
}
"
},
"lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/contracts/protocol/libraries/configuration/ReserveConfiguration.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Errors} from '../helpers/Errors.sol';
import {DataTypes} from '../types/DataTypes.sol';
/**
* @title ReserveConfiguration library
* @author Aave
* @notice Implements the bitmap logic to handle the reserve configuration
*/
library ReserveConfiguration {
uint256 internal constant LTV_MASK = 0x000000000000000000000000000000000000000000000000000000000000FFFF; // prettier-ignore
uint256 internal constant LIQUIDATION_THRESHOLD_MASK = 0x00000000000000000000000000000000000000000000000000000000FFFF0000; // prettier-ignore
uint256 internal constant LIQUIDATION_BONUS_MASK = 0x0000000000000000000000000000000000000000000000000000FFFF00000000; // prettier-ignore
uint256 internal constant DECIMALS_MASK = 0x00000000000000000000000000000000000000000000000000FF000000000000; // prettier-ignore
uint256 internal constant ACTIVE_MASK = 0x0000000000000000000000000000000000000000000000000100000000000000; // prettier-ignore
uint256 internal constant FROZEN_MASK = 0x0000000000000000000000000000000000000000000000000200000000000000; // prettier-ignore
uint256 internal constant BORROWING_MASK = 0x0000000000000000000000000000000000000000000000000400000000000000; // prettier-ignore
// @notice there is an unoccupied hole of 1 bit at position 59 from pre 3.2 stableBorrowRateEnabled
uint256 internal constant PAUSED_MASK = 0x0000000000000000000000000000000000000000000000001000000000000000; // prettier-ignore
uint256 internal constant BORROWABLE_IN_ISOLATION_MASK = 0x0000000000000000000000000000000000000000000000002000000000000000; // prettier-ignore
uint256 internal constant SILOED_BORROWING_MASK = 0x0000000000000000000000000000000000000000000000004000000000000000; // prettier-ignore
uint256 internal constant FLASHLOAN_ENABLED_MASK = 0x0000000000000000000000000000000000000000000000008000000000000000; // prettier-ignore
uint256 internal constant RESERVE_FACTOR_MASK = 0x00000000000000000000000000000000000000000000FFFF0000000000000000; // prettier-ignore
uint256 internal constant BORROW_CAP_MASK = 0x00000000000000000000000000000000000FFFFFFFFF00000000000000000000; // prettier-ignore
uint256 internal constant SUPPLY_CAP_MASK = 0x00000000000000000000000000FFFFFFFFF00000000000000000000000000000; // prettier-ignore
uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK = 0x0000000000000000000000FFFF00000000000000000000000000000000000000; // prettier-ignore
//@notice there is an unoccupied hole of 8 bits from 168 to 175 left from pre 3.2 eModeCategory
//@notice there is an unoccupied hole of 34 bits from 176 to 211 left from pre 3.4 unbackedMintCap
uint256 internal constant DEBT_CEILING_MASK = 0x0FFFFFFFFFF00000000000000000000000000000000000000000000000000000; // prettier-ignore
//@notice DEPRECATED: in v3.4 all reserves have virtual accounting enabled
uint256 internal constant VIRTUAL_ACC_ACTIVE_MASK = 0x1000000000000000000000000000000000000000000000000000000000000000; // prettier-ignore
/// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56;
uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57;
uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58;
uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60;
uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61;
uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62;
uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63;
uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64;
uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80;
uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116;
uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152;
//@notice there is an unoccupied hole of 8 bits from 168 to 175 left from pre 3.2 eModeCategory
//@notice there is an unoccupied hole of 34 bits from 176 to 211 left from pre 3.4 unbackedMintCap
uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
//@notice DEPRECATED: in v3.4 all reserves have virtual accounting enabled
uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252;
uint256 internal constant MAX_VALID_LTV = 65535;
uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535;
uint256 internal constant MAX_VALID_DECIMALS = 255;
uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535;
uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735;
uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735;
uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535;
uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775;
uint256 public constant DEBT_CEILING_DECIMALS = 2;
uint16 public constant MAX_RESERVES_COUNT = 128;
/**
* @notice Sets the Loan to Value of the reserve
* @param self The reserve configuration
* @param ltv The new ltv
*/
function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure {
require(ltv <= MAX_VALID_LTV, Errors.InvalidLtv());
self.data = (self.data & ~LTV_MASK) | ltv;
}
/**
* @notice Gets the Loan to Value of the reserve
* @param self The reserve configuration
* @return The loan to value
*/
function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
return self.data & LTV_MASK;
}
/**
* @notice Sets the liquidation threshold of the reserve
* @param self The reserve configuration
* @param threshold The new liquidation threshold
*/
function setLiquidationThreshold(
DataTypes.ReserveConfigurationMap memory self,
uint256 threshold
) internal pure {
require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.InvalidLiquidationThreshold());
self.data =
(self.data & ~LIQUIDATION_THRESHOLD_MASK) |
(threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
}
/**
* @notice Gets the liquidation threshold of the reserve
* @param self The reserve configuration
* @return The liquidation threshold
*/
function getLiquidationThreshold(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
}
/**
* @notice Sets the liquidation bonus of the reserve
* @param self The reserve configuration
* @param bonus The new liquidation bonus
*/
function setLiquidationBonus(
DataTypes.ReserveConfigurationMap memory self,
uint256 bonus
) internal pure {
require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.InvalidLiquidationBonus());
self.data =
(self.data & ~LIQUIDATION_BONUS_MASK) |
(bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
}
/**
* @notice Gets the liquidation bonus of the reserve
* @param self The reserve configuration
* @return The liquidation bonus
*/
function getLiquidationBonus(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
}
/**
* @notice Sets the decimals of the underlying asset of the reserve
* @param self The reserve configuration
* @param decimals The decimals
*/
function setDecimals(
DataTypes.ReserveConfigurationMap memory self,
uint256 decimals
) internal pure {
require(decimals <= MAX_VALID_DECIMALS, Errors.InvalidDecimals());
self.data = (self.data & ~DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
}
/**
* @notice Gets the decimals of the underlying asset of the reserve
* @param self The reserve configuration
* @return The decimals of the asset
*/
function getDecimals(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
}
/**
* @notice Sets the active state of the reserve
* @param self The reserve configuration
* @param active The active state
*/
function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure {
self.data =
(self.data & ~ACTIVE_MASK) |
(uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION);
}
/**
* @notice Gets the active state of the reserve
* @param self The reserve configuration
* @return The active state
*/
function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
return (self.data & ACTIVE_MASK) != 0;
}
/**
* @notice Sets the frozen state of the reserve
* @param self The reserve configuration
* @param frozen The frozen state
*/
function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure {
self.data =
(self.data & ~FROZEN_MASK) |
(uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION);
}
/**
* @notice Gets the frozen state of the reserve
* @param self The reserve configuration
* @return The frozen state
*/
function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
return (self.data & FROZEN_MASK) != 0;
}
/**
* @notice Sets the paused state of the reserve
* @param self The reserve configuration
* @param paused The paused state
*/
function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure {
self.data =
(self.data & ~PAUSED_MASK) |
(uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION);
}
/**
* @notice Gets the paused state of the reserve
* @param self The reserve configuration
* @return The paused state
*/
function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
return (self.data & PAUSED_MASK) != 0;
}
/**
* @notice Sets the borrowable in isolation flag for the reserve.
* @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed
* amount will be accumulated in the isolated collateral's total debt exposure.
* @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
* consistency in the debt ceiling calculations.
* @param self The reserve configuration
* @param borrowable True if the asset is borrowable
*/
function setBorrowableInIsolation(
DataTypes.ReserveConfigurationMap memory self,
bool borrowable
) internal pure {
self.data =
(self.data & ~BORROWABLE_IN_ISOLATION_MASK) |
(uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION);
}
/**
* @notice Gets the borrowable in isolation flag for the reserve.
* @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with
* isolated collateral is accounted for in the isolated collateral's total debt exposure.
* @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
* consistency in the debt ceiling calculations.
* @param self The reserve configuration
* @return The borrowable in isolation flag
*/
function getBorrowableInIsolation(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (bool) {
return (self.data & BORROWABLE_IN_ISOLATION_MASK) != 0;
}
/**
* @notice Sets the siloed borrowing flag for the reserve.
* @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
* @param self The reserve configuration
* @param siloed True if the asset is siloed
*/
function setSiloedBorrowing(
DataTypes.ReserveConfigurationMap memory self,
bool siloed
) internal pure {
self.data =
(self.data & ~SILOED_BORROWING_MASK) |
(uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION);
}
/**
* @notice Gets the siloed borrowing flag for the reserve.
* @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
* @param self The reserve configuration
* @return The siloed borrowing flag
*/
function getSiloedBorrowing(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (bool) {
return (self.data & SILOED_BORROWING_MASK) != 0;
}
/**
* @notice Enables or disables borrowing on the reserve
* @param self The reserve configuration
* @param enabled True if the borrowing needs to be enabled, false otherwise
*/
function setBorrowingEnabled(
DataTypes.ReserveConfigurationMap memory self,
bool enabled
) internal pure {
self.data =
(self.data & ~BORROWING_MASK) |
(uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION);
}
/**
* @notice Gets the borrowing state of the reserve
* @param self The reserve configuration
* @return The borrowing state
*/
function getBorrowingEnabled(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (bool) {
return (self.data & BORROWING_MASK) != 0;
}
/**
* @notice Sets the reserve factor of the reserve
* @param self The reserve configuration
* @param reserveFactor The reserve factor
*/
function setReserveFactor(
DataTypes.ReserveConfigurationMap memory self,
uint256 reserveFactor
) internal pure {
require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.InvalidReserveFactor());
self.data =
(self.data & ~RESERVE_FACTOR_MASK) |
(reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
}
/**
* @notice Gets the reserve factor of the reserve
* @param self The reserve configuration
* @return The reserve factor
*/
function getReserveFactor(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
}
/**
* @notice Sets the borrow cap of the reserve
* @param self The reserve configuration
* @param borrowCap The borrow cap
*/
function setBorrowCap(
DataTypes.ReserveConfigurationMap memory self,
uint256 borrowCap
) internal pure {
require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.InvalidBorrowCap());
self.data = (self.data & ~BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION);
}
/**
* @notice Gets the borrow cap of the reserve
* @param self The reserve configuration
* @return The borrow cap
*/
function getBorrowCap(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION;
}
/**
* @notice Sets the supply cap of the reserve
* @param self The reserve configuration
* @param supplyCap The supply cap
*/
function setSupplyCap(
DataTypes.ReserveConfigurationMap memory self,
uint256 supplyCap
) internal pure {
require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.InvalidSupplyCap());
self.data = (self.data & ~SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION);
}
/**
* @notice Gets the supply cap of the reserve
* @param self The reserve configuration
* @return The supply cap
*/
function getSupplyCap(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
}
/**
* @notice Sets the debt ceiling in isolation mode for the asset
* @param self The reserve configuration
* @param ceiling The maximum debt ceiling for the asset
*/
function setDebtCeiling(
DataTypes.ReserveConfigurationMap memory self,
uint256 ceiling
) internal pure {
require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.InvalidDebtCeiling());
self.data = (self.data & ~DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION);
}
/**
* @notice Gets the debt ceiling for the asset if the asset is in isolation mode
* @param self The reserve configuration
* @return The debt ceiling (0 = isolation mode disabled)
*/
function getDebtCeiling(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return (self.data & DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION;
}
/**
* @notice Sets the liquidation protocol fee of the reserve
* @param self The reserve configuration
* @param liquidationProtocolFee The liquidation protocol fee
*/
function setLiquidationProtocolFee(
DataTypes.ReserveConfigurationMap memory self,
uint256 liquidationProtocolFee
) internal pure {
require(
liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE,
Errors.InvalidLiquidationProtocolFee()
);
self.data =
(self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) |
(liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION);
}
/**
* @dev Gets the liquidation protocol fee
* @param self The reserve configuration
* @return The liquidation protocol fee
*/
function getLiquidationProtocolFee(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256) {
return
(self.data & LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION;
}
/**
* @notice Sets the flashloanable flag for the reserve
* @param self The reserve configuration
* @param flashLoanEnabled True if the asset is flashloanable, false otherwise
*/
function setFlashLoanEnabled(
DataTypes.ReserveConfigurationMap memory self,
bool flashLoanEnabled
) internal pure {
self.data =
(self.data & ~FLASHLOAN_ENABLED_MASK) |
(uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION);
}
/**
* @notice Gets the flashloanable flag for the reserve
* @param self The reserve configuration
* @return The flashloanable flag
*/
function getFlashLoanEnabled(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (bool) {
return (self.data & FLASHLOAN_ENABLED_MASK) != 0;
}
/**
* @notice Forcefully set the virtual account active state of the reserve to `true`
* @dev DEPRECATED: in v3.4 all reserves have virtual accounting enabled.
* The flag is carried along for backward compatibility with integrations directly querying the configuration.
* @param self The reserve configuration
*/
function setVirtualAccActive(DataTypes.ReserveConfigurationMap memory self) internal pure {
self.data =
(self.data & ~VIRTUAL_ACC_ACTIVE_MASK) |
(uint256(1) << VIRTUAL_ACC_START_BIT_POSITION);
}
/**
* @notice Gets the configuration flags of the reserve
* @param self The reserve configuration
* @return The state flag representing active
* @return The state flag representing frozen
* @return The state flag representing borrowing enabled
* @return The state flag representing paused
*/
function getFlags(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (bool, bool, bool, bool) {
uint256 dataLocal = self.data;
return (
(dataLocal & ACTIVE_MASK) != 0,
(dataLocal & FROZEN_MASK) != 0,
(dataLocal & BORROWING_MASK) != 0,
(dataLocal & PAUSED_MASK) != 0
);
}
/**
* @notice Gets the configuration parameters of the reserve from storage
* @param self The reserve configuration
* @return The state param representing ltv
* @return The state param representing liquidation threshold
* @return The state param representing liquidation bonus
* @return The state param representing reserve decimals
* @return The state param representing reserve factor
*/
function getParams(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256, uint256, uint256, uint256, uint256) {
uint256 dataLocal = self.data;
return (
dataLocal & LTV_MASK,
(dataLocal & LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
(dataLocal & LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
(dataLocal & DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
(dataLocal & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
);
}
/**
* @notice Gets the caps parameters of the reserve from storage
* @param self The reserve configuration
* @return The state param representing borrow cap
* @return The state param representing supply cap.
*/
function getCaps(
DataTypes.ReserveConfigurationMap memory self
) internal pure returns (uint256, uint256) {
uint256 dataLocal = self.data;
return (
(dataLocal & BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION,
(dataLocal & SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION
);
}
}
"
},
"lib/aave-helpers/lib/aave-address-book/src/AaveV3.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
import {DataTypes} from 'aave-v3-origin/contracts/protocol/libraries/types/DataTypes.sol';
import {Errors} from 'aave-v3-origin/contracts/protocol/libraries/helpers/Errors.sol';
import {ConfiguratorInputTypes} from 'aave-v3-origin/contracts/protocol/libraries/types/ConfiguratorInputTypes.sol';
import {IPoolAddressesProvider} from 'aave-v3-origin/contracts/interfaces/IPoolAddressesProvider.sol';
import {IAToken} from 'aave-v3-origin/contracts/interfaces/IAToken.sol';
import {IPool} from 'aave-v3-origin/contracts/interfaces/IPool.sol';
import {IPoolConfigurator} from 'aave-v3-origin/contracts/interfaces/IPoolConfigurator.sol';
import {IPriceOracleGetter} from 'aave-v3-origin/contracts/interfaces/IPriceOracleGetter.sol';
import {IAaveOracle} from 'aave-v3-origin/contracts/interfaces/IAaveOracle.sol';
import {IACLManager as BasicIACLManager} from 'aave-v3-origin/contracts/interfaces/IACLManager.sol';
import {IPoolDataProvider} from 'aave-v3-origin/contracts/interfaces/IPoolDataProvider.sol';
import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
import {IReserveInterestRateStrategy} from 'aave-v3-origin/contracts/interfaces/IReserveInterestRateStrategy.sol';
import {IPoolDataProvider as IAaveProtocolDataProvider} from 'aave-v3-origin/contracts/interfaces/IPoolDataProvider.sol';
import {AggregatorInterface} from 'aave-v3-origin/contracts/dependencies/chainlink/AggregatorInterface.sol';
import {ICollector} from 'aave-v3-origin/contracts/treasury/ICollector.sol';
interface IACLManager is BasicIACLManager {
function hasRole(bytes32 role, address account) external view returns (bool);
function DEFAULT_ADMIN_ROLE() external pure returns (bytes32);
function renounceRole(bytes32 role, address account) external;
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
}
"
},
"lib/aave-capo/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
"
},
"lib/aave-capo/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
*
Submitted on: 2025-10-10 11:17:38
Comments
Log in to comment.
No comments yet.