EdgeRiskStewardRates

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);
    }

    /**
     * 

Tags:
ERC20, Multisig, Mintable, Burnable, Swap, Liquidity, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x80cfd14236409107c220d1d0a3de845b48fdcdfc|verified:true|block:23569410|tx:0xbef784358f587b6b3a3e8f54331e1ac3d7a502ec477546cf3eb67405e52c0b98|first_check:1760366029

Submitted on: 2025-10-13 16:33:49

Comments

Log in to comment.

No comments yet.