creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/Liquidation.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IonPool } from \"./IonPool.sol\";\nimport { WadRayMath, RAY } from \"./libraries/math/WadRayMath.sol\";\nimport { ReserveOracle } from \"./oracles/reserve/ReserveOracle.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n/**\n * @notice The liquidation module for the `IonPool`.\n *\n * Liquidations at Ion operate a little differently than traditional liquidation schemes. Usually, liquidations are a\n * function of the market price of an asset. However, the liquidation module is function of the reserve oracle price\n * which reflects a rate based on **beacon-chain balances**.\n *\n * There are 3 different types of liquidations that can take place:\n * - Partial Liquidation: The liquidator pays off a portion of the debt and receives a portion of the collateral.\n * - Dust Liquidation: The liquidator pays off all of the debt and receives some or all of the collateral.\n * - Protocol Liquidation: The liquidator transfers the position's debt and collateral onto the protocol's balance\n * sheet.\n *\n * NOTE: Protocol liqudations are unlikely to ever be executed since there is\n * no profit incentive for a liquidator to do so. They exist solely as a\n * fallback if a liquidator were to ever execute a liquidation onto a vault that\n * had fallen into bad debt.\n *\n * @custom:security-contact security@molecularlabs.io\n */\ncontract Liquidation {\n    using SafeERC20 for IERC20;\n    using WadRayMath for uint256;\n    using SafeCast for uint256;\n\n    error ExchangeRateCannotBeZero();\n    error VaultIsNotUnsafe(uint256 healthRatio);\n\n    error InvalidReserveOraclesLength(uint256 length);\n    error InvalidLiquidationThresholdsLength(uint256 length);\n    error InvalidMaxDiscountsLength(uint256 length);\n    error InvalidTargetHealth(uint256 targetHealth);\n    error InvalidLiquidationThreshold(uint256 liquidationThreshold);\n    error InvalidMaxDiscount(uint256 maxDiscount);\n\n    // --- Parameters ---\n\n    uint256 public immutable TARGET_HEALTH; // [ray] ex) 1.25e27 is 125%\n    uint256 public immutable BASE_DISCOUNT; // [ray] ex) 0.02e27 is 2%\n\n    uint256 public immutable MAX_DISCOUNT; // [ray] ex) 0.2e27 is 20%\n\n    // liquidation thresholds\n    uint256 public immutable LIQUIDATION_THRESHOLD; // [ray] liquidation threshold for ilkIndex 0\n\n    // exchange rates\n    address public immutable RESERVE_ORACLE; // reserve oracle providing exchange rate for ilkIndex 0\n\n    address public immutable PROTOCOL; // receives confiscated vault debt and collateral\n\n    IonPool public immutable POOL;\n    IERC20 public immutable UNDERLYING;\n\n    // --- Events ---\n    event Liquidate(\n        address indexed initiator, address indexed kpr, uint8 indexed ilkIndex, uint256 repay, uint256 gemOut\n    );\n\n    /**\n     * @notice Creates a new `Liquidation` instance.\n     * @param _ionPool The address of the `IonPool` contract.\n     * @param _protocol The address that will represent the protocol balance\n     * sheet (for protocol liquidation purposes).\n     * @param _reserveOracle Reserve oracle for the ilk.\n     * @param _liquidationThreshold Liquidation threshold for the ilk.\n     * @param _targetHealth The target health ratio for positions.\n     * @param _reserveFactor Base discount for collateral.\n     * @param _maxDiscount Max discount for the ilk.\n     */\n    constructor(\n        address _ionPool,\n        address _protocol,\n        address _reserveOracle,\n        uint256 _liquidationThreshold,\n        uint256 _targetHealth,\n        uint256 _reserveFactor,\n        uint256 _maxDiscount\n    ) {\n        IonPool ionPool_ = IonPool(_ionPool);\n        POOL = ionPool_;\n        PROTOCOL = _protocol;\n\n        if (_maxDiscount >= RAY) revert InvalidMaxDiscount(_maxDiscount);\n\n        if (_liquidationThreshold == 0) revert InvalidLiquidationThreshold(_liquidationThreshold);\n\n        // This invariant must hold otherwise all liquidations will revert\n        // when discount == configs.maxDiscount within the _getRepayAmt\n        // function.\n        if (_targetHealth < _liquidationThreshold.rayDivUp(RAY - _maxDiscount)) {\n            revert InvalidTargetHealth(_targetHealth);\n        }\n\n        if (_targetHealth < RAY) revert InvalidTargetHealth(_targetHealth);\n\n        TARGET_HEALTH = _targetHealth;\n        BASE_DISCOUNT = _reserveFactor;\n        MAX_DISCOUNT = _maxDiscount;\n\n        IERC20 underlying = ionPool_.underlying();\n        underlying.approve(address(ionPool_), type(uint256).max); // approve ionPool to transfer the UNDERLYING asset\n        UNDERLYING = underlying;\n\n        LIQUIDATION_THRESHOLD = _liquidationThreshold;\n\n        RESERVE_ORACLE = _reserveOracle;\n    }\n\n    struct Configs {\n        uint256 liquidationThreshold;\n        uint256 maxDiscount;\n        address reserveOracle;\n    }\n\n    /**\n     * @notice Returns the exchange rate, liquidation threshold, and max\n     * discount for the given ilk.\n     */\n    function _getConfig() internal view returns (Configs memory configs) {\n        configs.reserveOracle = RESERVE_ORACLE;\n        configs.liquidationThreshold = LIQUIDATION_THRESHOLD;\n        configs.maxDiscount = MAX_DISCOUNT;\n    }\n\n    /**\n     * @notice If liquidation is possible, returns the amount of WETH necessary\n     * to liquidate a vault.\n     * @param ilkIndex The index of the ilk.\n     * @param vault The address of the vault.\n     * @return repay The amount of WETH necessary to liquidate the vault.\n     */\n    function getRepayAmt(uint8 ilkIndex, address vault) public view returns (uint256 repay) {\n        Configs memory configs = _getConfig();\n\n        // exchangeRate is reported in uint72 in [wad], but should be converted to uint256 [ray]\n        uint256 exchangeRate = uint256(ReserveOracle(configs.reserveOracle).currentExchangeRate()).scaleUpToRay(18);\n        (uint256 collateral, uint256 normalizedDebt) = POOL.vault(ilkIndex, vault);\n        uint256 rate = POOL.rate(ilkIndex);\n\n        if (exchangeRate == 0) {\n            revert ExchangeRateCannotBeZero();\n        }\n\n        // collateralValue = collateral * exchangeRate * liquidationThreshold\n        // debtValue = normalizedDebt * rate\n        // healthRatio = collateralValue / debtValue\n        // collateralValue = [wad] * [ray] * [ray] / RAY = [rad]\n        // debtValue = [wad] * [ray] = [rad]\n        // healthRatio = [rad] * RAY / [rad] = [ray]\n        // round down in protocol favor\n        uint256 collateralValue = (collateral * exchangeRate).rayMulDown(configs.liquidationThreshold);\n\n        uint256 healthRatio = collateralValue.rayDivDown(normalizedDebt * rate); // round down in protocol favor\n        if (healthRatio >= RAY) {\n            revert VaultIsNotUnsafe(healthRatio);\n        }\n\n        uint256 discount = BASE_DISCOUNT + (RAY - healthRatio); // [ray] + ([ray] - [ray])\n        discount = discount <= configs.maxDiscount ? discount : configs.maxDiscount; // cap discount to maxDiscount\n            // favor\n        uint256 repayRad = _getRepayAmt(normalizedDebt * rate, collateralValue, configs.liquidationThreshold, discount);\n\n        if (repayRad > normalizedDebt * rate) return 0;\n        else if (normalizedDebt * rate - repayRad < POOL.dust(ilkIndex)) repayRad = normalizedDebt * rate;\n\n        return repayRad % RAY > 0 ? repayRad / RAY + 1 : repayRad / RAY;\n    }\n\n    /**\n     * @notice Internal helper function for calculating the repay amount.\n     * @param debtValue The total debt. [RAD]\n     * @param collateralValue Calculated with collateral * exchangeRate * liquidationThreshold. [RAD]\n     * @param liquidationThreshold Ratio at which liquidation can occur. [RAY]\n     * @param discount The discount from the exchange rate at which the collateral is sold. [RAY]\n     * @return repay The amount of WETH necessary to liquidate the vault. [RAD]\n     */\n    function _getRepayAmt(\n        uint256 debtValue,\n        uint256 collateralValue,\n        uint256 liquidationThreshold,\n        uint256 discount\n    )\n        internal\n        view\n        returns (uint256 repay)\n    {\n        // repayNum = (targetHealth * totalDebt - collateral * exchangeRate * liquidationThreshold)\n        // repayDen = (targetHealth - (liquidationThreshold / (1 - discount)))\n        // repay = repayNum / repayDen\n\n        // Round up repay in protocol favor for safer post-liquidation position\n        // This will never underflow because at this point we know health ratio\n        // is less than 1, which means that collateralValue < debtValue.\n        uint256 repayNum = debtValue.rayMulUp(TARGET_HEALTH) - collateralValue; // [rad] - [rad] = [rad]\n        uint256 repayDen = TARGET_HEALTH - liquidationThreshold.rayDivUp(RAY - discount); // [ray]\n        repay = repayNum.rayDivUp(repayDen); // [rad] * RAY / [ray] = [rad]\n    }\n\n    struct LiquidateArgs {\n        uint256 repay;\n        uint256 gemOut;\n        uint256 dart;\n        uint256 fee;\n        uint256 price;\n    }\n\n    /**\n     * @notice Closes an unhealthy position on `IonPool`.\n     * @param ilkIndex The index of the collateral.\n     * @param vault The position to be liquidated.\n     * @param kpr Receiver of the collateral.\n     * @return repayAmount The amount of WETH paid to close the position.\n     * @return gemOut The amount of collateral received from the liquidation.\n     */\n    function liquidate(\n        uint8 ilkIndex,\n        address vault,\n        address kpr\n    )\n        external\n        returns (uint256 repayAmount, uint256 gemOut)\n    {\n        LiquidateArgs memory liquidateArgs;\n\n        Configs memory configs = _getConfig();\n\n        // exchangeRate is reported in uint72 in [wad], but should be converted to uint256 [ray]\n        uint256 exchangeRate = ReserveOracle(configs.reserveOracle).currentExchangeRate().scaleUpToRay(18);\n        (uint256 collateral, uint256 normalizedDebt) = POOL.vault(ilkIndex, vault);\n        uint256 rate = POOL.rate(ilkIndex);\n\n        if (exchangeRate == 0) {\n            revert ExchangeRateCannotBeZero();\n        }\n\n        // collateralValue = collateral * exchangeRate * liquidationThreshold\n        // debtValue = normalizedDebt * rate\n        // healthRatio = collateralValue / debtValue\n        // collateralValue = [wad] * [ray] * [ray] / RAY = [rad]\n        // debtValue = [wad] * [ray] = [rad]\n        // healthRatio = [rad] * RAY / [rad] = [ray]\n        // round down in protocol favor\n        uint256 collateralValue = (collateral * exchangeRate).rayMulDown(configs.liquidationThreshold);\n        {\n            uint256 healthRatio = collateralValue.rayDivDown(normalizedDebt * rate); // round down in protocol favor\n            if (healthRatio >= RAY) {\n                revert VaultIsNotUnsafe(healthRatio);\n            }\n\n            uint256 discount = BASE_DISCOUNT + (RAY - healthRatio); // [ray] + ([ray] - [ray])\n            discount = discount <= configs.maxDiscount ? discount : configs.maxDiscount; // cap discount to maxDiscount\n            liquidateArgs.price = exchangeRate.rayMulUp(RAY - discount); // ETH price per LST, round up in protocol\n                // favor\n            liquidateArgs.repay =\n                _getRepayAmt(normalizedDebt * rate, collateralValue, configs.liquidationThreshold, discount);\n        }\n\n        // First branch: protocol liquidation\n        //    if repay > total debt, more debt needs to be paid off than available to go back to target health\n        //    Move exactly all collateral and debt to the protocol.\n        // Second branch: resulting debt is below dust\n        //    There is enough collateral to cover the debt and go back to target health,\n        //    but it would leave a debt amount less than dust.\n        //    Force keeper to pay off all debt including dust and readjust the amount of collateral to sell.\n        //    Resulting debt should always be zero.\n        // Third branch: partial liquidation to target health ratio\n        //    There is enough collateral to be sold to pay off debt.\n        //    Liquidator pays portion of the debt and receives collateral.\n        //    The resulting health ratio should equal target health.\n        if (liquidateArgs.repay > normalizedDebt * rate) {\n            // [rad] > [rad]\n            liquidateArgs.dart = normalizedDebt; // [wad]\n            liquidateArgs.gemOut = collateral; // [wad]\n            POOL.confiscateVault(\n                ilkIndex, vault, PROTOCOL, PROTOCOL, -int256(liquidateArgs.gemOut), -int256(liquidateArgs.dart)\n            );\n            emit Liquidate(msg.sender, kpr, ilkIndex, liquidateArgs.dart, liquidateArgs.gemOut);\n            return (0, 0); // early return\n        } else if (normalizedDebt * rate - liquidateArgs.repay < POOL.dust(ilkIndex)) {\n            // [rad] - [rad] < [rad]\n            liquidateArgs.repay = normalizedDebt * rate; // bound repay to total debt\n            liquidateArgs.dart = normalizedDebt; // pay off all debt including dust\n            liquidateArgs.gemOut = normalizedDebt * rate / liquidateArgs.price; // round down in protocol favor\n        } else {\n            // if (normalizedDebt * rate - liquidateArgs.repay >= dust) do partial liquidation\n            // round up in protocol favor\n            liquidateArgs.dart = liquidateArgs.repay / rate; // [rad] / [ray] = [wad]\n            if (liquidateArgs.repay % rate > 0) ++liquidateArgs.dart; // round up in protocol favor\n            // round down in protocol favor\n            liquidateArgs.gemOut = liquidateArgs.repay / liquidateArgs.price; // readjust amount of collateral\n            liquidateArgs.repay = liquidateArgs.dart * rate; // 27 decimals precision loss on original repay\n        }\n\n        // below code is only reached for dust or partial liquidations\n\n        // exact amount to be transferred in `_transferWeth`\n        uint256 transferAmt = (liquidateArgs.repay / RAY);\n        if (liquidateArgs.repay % RAY > 0) ++transferAmt; // round up in protocol favor\n\n        // transfer WETH from keeper to this contract\n        UNDERLYING.safeTransferFrom(msg.sender, address(this), transferAmt);\n\n        // take the debt to pay off and the collateral to sell from the vault\n        // kpr gets the gemOut\n        POOL.confiscateVault(\n            ilkIndex, vault, kpr, address(this), -(liquidateArgs.gemOut.toInt256()), -(liquidateArgs.dart.toInt256())\n        );\n\n        // pay off the unbacked debt\n        POOL.repayBadDebt(address(this), liquidateArgs.repay);\n\n        emit Liquidate(msg.sender, kpr, ilkIndex, liquidateArgs.dart, liquidateArgs.gemOut);\n\n        return (liquidateArgs.repay, liquidateArgs.gemOut);\n    }\n}\n"
      },
      "src/IonPool.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-or-later\npragma solidity 0.8.21;\n\nimport { Whitelist } from \"./Whitelist.sol\";\nimport { SpotOracle } from \"./oracles/spot/SpotOracle.sol\";\nimport { RewardModule } from \"./reward/RewardModule.sol\";\nimport { InterestRate } from \"./InterestRate.sol\";\nimport { WadRayMath, RAY } from \"./libraries/math/WadRayMath.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { PausableUpgradeable } from \"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol\";\n\n/**\n * @notice `IonPool` is the central contract of the Ion Protocol system. All\n * other contracts in the system revolve around it. Directly interacting with\n * `IonPool` may be unintuitive and it is recommended to interface with the\n * protocol through Handler contracts for a more UX-friendly experience.\n *\n * @custom:security-contact security@molecularlabs.io\n */\ncontract IonPool is PausableUpgradeable, RewardModule {\n    using SafeERC20 for IERC20;\n    using SafeCast for *;\n    using WadRayMath for *;\n    using Math for uint256;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    // --- Errors ---\n    error CeilingExceeded(uint256 newDebt, uint256 debtCeiling);\n    error UnsafePositionChange(uint256 newTotalDebtInVault, uint256 collateral, uint256 spot);\n    error UnsafePositionChangeWithoutConsent(uint8 ilkIndex, address user, address unconsentedOperator);\n    error GemTransferWithoutConsent(uint8 ilkIndex, address user, address unconsentedOperator);\n    error UseOfCollateralWithoutConsent(uint8 ilkIndex, address depositor, address unconsentedOperator);\n    error TakingWethWithoutConsent(address payer, address unconsentedOperator);\n    error VaultCannotBeDusty(uint256 amountLeft, uint256 dust);\n    error ArithmeticError();\n    error IlkAlreadyAdded(address ilkAddress);\n    error IlkNotInitialized(uint256 ilkIndex);\n    error DepositSurpassesSupplyCap(uint256 depositAmount, uint256 supplyCap);\n    error MaxIlksReached();\n\n    error InvalidIlkAddress();\n    error InvalidInterestRateModule(InterestRate invalidInterestRateModule);\n    error InvalidWhitelist();\n\n    // --- Events ---\n    event IlkInitialized(uint8 indexed ilkIndex, address indexed ilkAddress);\n    event IlkSpotUpdated(uint8 indexed ilkIndex, address newSpot);\n    event IlkDebtCeilingUpdated(uint8 indexed ilkIndex, uint256 newDebtCeiling);\n    event IlkDustUpdated(uint8 indexed ilkIndex, uint256 newDust);\n    event SupplyCapUpdated(uint256 newSupplyCap);\n    event InterestRateModuleUpdated(address newModule);\n    event WhitelistUpdated(address newWhitelist);\n\n    event AddOperator(address indexed user, address indexed operator);\n    event RemoveOperator(address indexed user, address indexed operator);\n    event MintAndBurnGem(uint8 indexed ilkIndex, address indexed usr, int256 wad);\n    event TransferGem(uint8 indexed ilkIndex, address indexed src, address indexed dst, uint256 wad);\n\n    event Supply(\n        address indexed user, address indexed underlyingFrom, uint256 amount, uint256 supplyFactor, uint256 newDebt\n    );\n\n    event Withdraw(address indexed user, address indexed target, uint256 amount, uint256 supplyFactor, uint256 newDebt);\n\n    event WithdrawCollateral(uint8 indexed ilkIndex, address indexed user, address indexed recipient, uint256 amount);\n    event DepositCollateral(uint8 indexed ilkIndex, address indexed user, address indexed depositor, uint256 amount);\n    event Borrow(\n        uint8 indexed ilkIndex,\n        address indexed user,\n        address indexed recipient,\n        uint256 amountOfNormalizedDebt,\n        uint256 ilkRate,\n        uint256 totalDebt\n    );\n    event Repay(\n        uint8 indexed ilkIndex,\n        address indexed user,\n        address indexed payer,\n        uint256 amountOfNormalizedDebt,\n        uint256 ilkRate,\n        uint256 totalDebt\n    );\n\n    event RepayBadDebt(address indexed user, address indexed payer, uint256 rad);\n    event ConfiscateVault(\n        uint8 indexed ilkIndex,\n        address indexed u,\n        address v,\n        address indexed w,\n        int256 changeInCollateral,\n        int256 changeInNormalizedDebt\n    );\n\n    bytes32 public constant GEM_JOIN_ROLE = keccak256(\"GEM_JOIN_ROLE\");\n    bytes32 public constant LIQUIDATOR_ROLE = keccak256(\"LIQUIDATOR_ROLE\");\n    bytes32 public constant PAUSE_ROLE = keccak256(\"PAUSE_ROLE\");\n\n    address private immutable ADDRESS_THIS = address(this);\n\n    // --- Modifiers ---\n    modifier onlyWhitelistedBorrowers(uint8 ilkIndex, address user, bytes32[] memory proof) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        $.whitelist.isWhitelistedBorrower(ilkIndex, msg.sender, user, proof);\n        _;\n    }\n\n    modifier onlyWhitelistedLenders(address user, bytes32[] memory proof) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        $.whitelist.isWhitelistedLender(msg.sender, user, proof);\n        _;\n    }\n\n    // --- Data ---\n    struct Ilk {\n        uint104 totalNormalizedDebt; // Total Normalised Debt     [WAD]\n        uint104 rate; // Accumulated Rates         [RAY]\n        uint48 lastRateUpdate; // block.timestamp of last update; overflows in 800_000 years\n        SpotOracle spot; // Oracle that provides price with safety margin\n        uint256 debtCeiling; // Debt Ceiling              [RAD]\n        uint256 dust; // Vault Debt Floor            [RAD]\n    }\n\n    struct Vault {\n        uint256 collateral; // Locked Collateral  [WAD]\n        uint256 normalizedDebt; // Normalised Debt    [WAD]\n    }\n\n    /// @custom:storage-location erc7201:ion.storage.IonPool\n    struct IonPoolStorage {\n        Ilk[] ilks;\n        // remove() should never be called, it will mess up the ordering\n        EnumerableSet.AddressSet ilkAddresses;\n        mapping(uint256 ilkIndex => mapping(address user => Vault)) vaults;\n        mapping(uint256 ilkIndex => mapping(address user => uint256)) gem; // [WAD]\n        mapping(address unbackedDebtor => uint256) unbackedDebt; // [RAD]\n        mapping(address user => mapping(address operator => uint256)) isOperator;\n        uint256 debt; // Total Debt [RAD]\n        uint256 weth; // liquidity in pool [WAD]\n        uint256 wethSupplyCap; // [WAD]\n        uint256 totalUnbackedDebt; // Total Unbacked WETH  [RAD]\n        InterestRate interestRateModule;\n        Whitelist whitelist;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"ion.storage.IonPool\")) - 1)) & ~bytes32(uint256(0xff))\n    // solhint-disable-next-line\n    bytes32 private constant IonPoolStorageLocation = 0xceba3d526b4d5afd91d1b752bf1fd37917c20a6daf576bcb41dd1c57c1f67e00;\n\n    function _getIonPoolStorage() internal pure returns (IonPoolStorage storage $) {\n        assembly {\n            $.slot := IonPoolStorageLocation\n        }\n    }\n\n    constructor() {\n        _disableInitializers();\n    }\n\n    function initialize(\n        address _underlying,\n        address _treasury,\n        uint8 decimals_,\n        string memory name_,\n        string memory symbol_,\n        address initialDefaultAdmin,\n        InterestRate _interestRateModule,\n        Whitelist _whitelist\n    )\n        external\n        initializer\n    {\n        __AccessControlDefaultAdminRules_init(0, initialDefaultAdmin);\n        RewardModule._initialize(_underlying, _treasury, decimals_, name_, symbol_);\n\n        _grantRole(ION, initialDefaultAdmin);\n\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.interestRateModule = _interestRateModule;\n        $.whitelist = _whitelist;\n\n        emit InterestRateModuleUpdated(address(_interestRateModule));\n        emit WhitelistUpdated(address(_whitelist));\n    }\n\n    // --- Administration ---\n\n    /**\n     * @notice Initializes a market with a new collateral type.\n     * @dev This function and the entire protocol as a whole operates under the\n     * assumption that there will never be more than 256 collaterals.\n     * @param ilkAddress address of the ERC-20 collateral.\n     */\n    function initializeIlk(address ilkAddress) external onlyRole(ION) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        if (ilkAddress == address(0)) revert InvalidIlkAddress();\n        if (!$.ilkAddresses.add(ilkAddress)) revert IlkAlreadyAdded(ilkAddress);\n\n        uint256 ilksLength = $.ilks.length;\n\n        // Explicitly enforce the max number of collaterals\n        if (ilksLength >= uint256(type(uint8).max) + 1) revert MaxIlksReached();\n\n        // Unsafe cast OK since we don't plan on having more than 256\n        // collaterals\n        uint8 ilkIndex = uint8(ilksLength);\n        Ilk memory newIlk;\n        $.ilks.push(newIlk);\n        Ilk storage ilk = $.ilks[ilkIndex];\n\n        ilk.rate = uint104(RAY);\n        // Unsafe cast OK\n        ilk.lastRateUpdate = uint48(block.timestamp);\n\n        emit IlkInitialized(ilkIndex, ilkAddress);\n    }\n\n    /**\n     * @dev Updates the spot oracle for a given collateral.\n     * @param ilkIndex index of the collateral.\n     * @param newSpot address of the new spot oracle.\n     */\n    function updateIlkSpot(uint8 ilkIndex, SpotOracle newSpot) external onlyRole(ION) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.ilks[ilkIndex].spot = newSpot;\n\n        emit IlkSpotUpdated(ilkIndex, address(newSpot));\n    }\n\n    /**\n     * @notice A market can be sunset by setting the debt ceiling to 0. It would\n     * still be possible to repay debt but creating new debt would not be\n     * possible.\n     * @dev Updates the debt ceiling for a given collateral.\n     * @param ilkIndex index of the collateral.\n     * @param newCeiling new debt ceiling.\n     */\n    function updateIlkDebtCeiling(uint8 ilkIndex, uint256 newCeiling) external onlyRole(ION) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.ilks[ilkIndex].debtCeiling = newCeiling;\n\n        emit IlkDebtCeilingUpdated(ilkIndex, newCeiling);\n    }\n\n    /**\n     * @notice When increasing the `dust`, it is possible that some vaults will\n     * be dusty after the update. However, changes to the vault position from\n     * there will require that the vault be non-dusty (either by repaying all\n     * debt or increasing debt beyond the `dust`).\n     * @dev Updates the dust amount for a given collateral.\n     * @param ilkIndex index of the collateral.\n     * @param newDust new dust\n     */\n    function updateIlkDust(uint8 ilkIndex, uint256 newDust) external onlyRole(ION) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.ilks[ilkIndex].dust = newDust;\n\n        emit IlkDustUpdated(ilkIndex, newDust);\n    }\n\n    /**\n     * @notice Reducing the supply cap will not affect existing deposits.\n     * However, if it is below the `totalSupply`, then no new deposits will be\n     * allowed until the `totalSupply` is below the new `supplyCap`.\n     * @dev Updates the supply cap.\n     * @param newSupplyCap new supply cap.\n     */\n    function updateSupplyCap(uint256 newSupplyCap) external onlyRole(ION) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.wethSupplyCap = newSupplyCap;\n\n        emit SupplyCapUpdated(newSupplyCap);\n    }\n\n    /**\n     * @dev Updates the interest rate module. There is a check to ensure that\n     * `collateralCount()` on the new interest rate module match the current\n     * number of collaterals in the pool.\n     * @param _interestRateModule new interest rate module.\n     */\n    function updateInterestRateModule(InterestRate _interestRateModule) external onlyRole(ION) {\n        if (address(_interestRateModule) == address(0)) revert InvalidInterestRateModule(_interestRateModule);\n\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        // Sanity check\n        if (_interestRateModule.COLLATERAL_COUNT() != $.ilks.length) {\n            revert InvalidInterestRateModule(_interestRateModule);\n        }\n        $.interestRateModule = _interestRateModule;\n\n        emit InterestRateModuleUpdated(address(_interestRateModule));\n    }\n\n    /**\n     * @dev Updates the whitelist.\n     * @param _whitelist new whitelist address.\n     */\n    function updateWhitelist(Whitelist _whitelist) external onlyRole(ION) {\n        if (address(_whitelist) == address(0)) revert InvalidWhitelist();\n\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.whitelist = _whitelist;\n\n        emit WhitelistUpdated(address(_whitelist));\n    }\n\n    /**\n     * @dev Pause actions but accrue interest as well.\n     *\n     * Under certain protocol conditions, we want to be able to pause the\n     * protocol automatically through monitoring systems. So we want to be able\n     * to grant the PAUSE_ROLE to those private keys. In the case of a\n     * compromised private key, we can revoke the PAUSE_ROLE from that private\n     * key and grant it to a new private key. Unpausing will remain a multisig\n     * operation.\n     */\n    function pause() external onlyRole(PAUSE_ROLE) {\n        _accrueInterest();\n        _pause();\n    }\n\n    /**\n     * @dev Unpause actions but this will also update the `lastRateUpdate` to\n     * the unpause transaction timestamp. This essentially allows for a pausing\n     * and unpausing of the accrual of interest.\n     */\n    function unpause() external onlyRole(ION) {\n        _unpause();\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        uint256 ilksLength = $.ilks.length;\n        for (uint256 i = 0; i < ilksLength;) {\n            // Unsafe cast OK\n            $.ilks[i].lastRateUpdate = uint48(block.timestamp);\n\n            // forgefmt: disable-next-line\n            unchecked { ++i; }\n        }\n    }\n\n    // --- Interest Calculations ---\n\n    /**\n     * @dev Updates accumulators for all `ilk`s based on current interest rates.\n     * @return newTotalDebt the new total debt after interest accrual\n     */\n    function accrueInterest() external whenNotPaused returns (uint256 newTotalDebt) {\n        return _accrueInterest();\n    }\n\n    function _accrueInterest() internal returns (uint256 newTotalDebt) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        uint256 totalEthSupply = totalSupplyUnaccrued();\n\n        uint256 totalSupplyFactorIncrease;\n        uint256 totalTreasuryMintAmount;\n        uint256 totalDebtIncrease;\n\n        uint256 ilksLength = $.ilks.length;\n        for (uint8 i = 0; i < ilksLength;) {\n            (\n                uint256 supplyFactorIncrease,\n                uint256 treasuryMintAmount,\n                uint104 newRateIncrease,\n                uint256 newDebtIncrease,\n                uint48 timestampIncrease\n            ) = _calculateRewardAndDebtDistributionForIlk(i, totalEthSupply);\n\n            if (timestampIncrease > 0) {\n                Ilk storage ilk = $.ilks[i];\n                ilk.rate += newRateIncrease;\n                ilk.lastRateUpdate += timestampIncrease;\n                totalDebtIncrease += newDebtIncrease;\n\n                totalSupplyFactorIncrease += supplyFactorIncrease;\n                totalTreasuryMintAmount += treasuryMintAmount;\n            }\n\n            // forgefmt: disable-next-line\n            unchecked { ++i; }\n        }\n\n        newTotalDebt = $.debt + totalDebtIncrease;\n        $.debt = newTotalDebt;\n        _setSupplyFactor(supplyFactorUnaccrued() + totalSupplyFactorIncrease);\n        _mintToTreasury(totalTreasuryMintAmount);\n    }\n\n    function calculateRewardAndDebtDistribution()\n        public\n        view\n        override\n        returns (\n            uint256 totalSupplyFactorIncrease,\n            uint256 totalTreasuryMintAmount,\n            uint104[] memory rateIncreases,\n            uint256 totalDebtIncrease,\n            uint48[] memory timestampIncreases\n        )\n    {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        uint256 ilksLength = $.ilks.length;\n\n        rateIncreases = new uint104[](ilksLength);\n        timestampIncreases = new uint48[](ilksLength);\n\n        uint256 totalEthSupply = totalSupplyUnaccrued();\n\n        for (uint8 i = 0; i < ilksLength;) {\n            (\n                uint256 supplyFactorIncrease,\n                uint256 treasuryMintAmount,\n                uint104 newRateIncrease,\n                uint256 newDebtIncrease,\n                uint48 timestampIncrease\n            ) = _calculateRewardAndDebtDistributionForIlk(i, totalEthSupply);\n\n            if (timestampIncrease > 0) {\n                rateIncreases[i] = newRateIncrease;\n                timestampIncreases[i] = timestampIncrease;\n                totalDebtIncrease += newDebtIncrease;\n\n                totalSupplyFactorIncrease += supplyFactorIncrease;\n                totalTreasuryMintAmount += treasuryMintAmount;\n            }\n\n            // forgefmt: disable-next-line\n            unchecked { ++i; }\n        }\n    }\n\n    /**\n     * @notice This is primarily for simulation purposes to see how an\n     * individual ilk's state will change after an accrual.\n     * @param ilkIndex index of the collateral.\n     * @return newRateIncrease the rate increase for the ilk.\n     * @return timestampIncrease the timestamp increase for the ilk.\n     */\n    function calculateRewardAndDebtDistributionForIlk(uint8 ilkIndex)\n        public\n        view\n        returns (uint104 newRateIncrease, uint48 timestampIncrease)\n    {\n        (,, newRateIncrease,, timestampIncrease) =\n            _calculateRewardAndDebtDistributionForIlk(ilkIndex, totalSupplyUnaccrued());\n    }\n\n    function _calculateRewardAndDebtDistributionForIlk(\n        uint8 ilkIndex,\n        uint256 totalEthSupply\n    )\n        internal\n        view\n        returns (\n            uint256 supplyFactorIncrease,\n            uint256 treasuryMintAmount,\n            uint104 newRateIncrease,\n            uint256 newDebtIncrease,\n            uint48 timestampIncrease\n        )\n    {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        Ilk storage ilk = $.ilks[ilkIndex];\n\n        uint256 _totalNormalizedDebt = ilk.totalNormalizedDebt;\n        if (_totalNormalizedDebt == 0 || block.timestamp == ilk.lastRateUpdate) {\n            // Unsafe cast OK\n            // block.timestamp - ilk.lastRateUpdate will almost always be 0\n            // here. The exception is on first borrow.\n            return (0, 0, 0, 0, uint48(block.timestamp - ilk.lastRateUpdate));\n        }\n\n        uint256 totalDebt = _totalNormalizedDebt * ilk.rate; // [WAD] * [RAY] = [RAD]\n\n        (uint256 borrowRate, uint256 reserveFactor) =\n            $.interestRateModule.calculateInterestRate(ilkIndex, totalDebt, totalEthSupply);\n\n        if (borrowRate == 0) return (0, 0, 0, 0, 0);\n\n        // Calculates borrowRate ^ (time) and returns the result with RAY precision\n        uint256 borrowRateExpT = _rpow(borrowRate + RAY, block.timestamp - ilk.lastRateUpdate, RAY);\n\n        // Unsafe cast OK\n        timestampIncrease = uint48(block.timestamp) - ilk.lastRateUpdate;\n\n        // Debt distribution\n        // This form of rate accrual is much safer than distributing the new\n        // debt increase to the total debt since low debt amounts won't cause\n        // rounding errors to sky rocket the rate. This form of accrual is still\n        // subject to rate inflation, however, it would only be from an\n        // extremely high borrow rate rather than being a function of the\n        // current total debt in the system. This is very relevant for\n        // sunsetting markets, where the goal will be to reduce the total debt\n        // to 0.\n        newRateIncrease = ilk.rate.rayMulUp(borrowRateExpT - RAY).toUint104(); // [RAY]\n\n        newDebtIncrease = _totalNormalizedDebt * newRateIncrease; // [RAD]\n\n        // Income distribution\n        uint256 _normalizedTotalSupply = normalizedTotalSupplyUnaccrued(); // [WAD]\n\n        // If there is no supply, then nothing is being lent out.\n        supplyFactorIncrease = _normalizedTotalSupply == 0\n            ? 0\n            : newDebtIncrease.mulDiv(RAY - reserveFactor, _normalizedTotalSupply.scaleUpToRad(18)); // [RAD] * [RAY] / [RAD]\n            // = [RAY]\n\n        treasuryMintAmount = newDebtIncrease.mulDiv(reserveFactor, 1e54); // [RAD] * [RAY] / 1e54 = [WAD]\n    }\n\n    // --- Lender Operations ---\n\n    /**\n     * @dev Allows lenders to redeem their interest-bearing position for the\n     * underlying asset. It is possible that dust amounts more of the position\n     * are burned than the underlying received due to rounding.\n     * @param receiverOfUnderlying the address to which the redeemed underlying\n     * asset should be sent to.\n     * @param amount of underlying to reedeem for.\n     */\n    function withdraw(address receiverOfUnderlying, uint256 amount) external whenNotPaused {\n        uint256 newTotalDebt = _accrueInterest();\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.weth -= amount;\n\n        uint256 _supplyFactor =\n            _burn({ user: _msgSender(), receiverOfUnderlying: receiverOfUnderlying, amount: amount });\n\n        emit Withdraw(_msgSender(), receiverOfUnderlying, amount, _supplyFactor, newTotalDebt);\n    }\n\n    /**\n     * @dev Allows lenders to deposit their underlying asset into the pool and\n     * earn interest on it.\n     * @param user the address to receive credit for the position.\n     * @param amount of underlying asset to use to create the position.\n     * @param proof merkle proof that the user is whitelisted.\n     */\n    function supply(\n        address user,\n        uint256 amount,\n        bytes32[] calldata proof\n    )\n        external\n        whenNotPaused\n        onlyWhitelistedLenders(user, proof)\n    {\n        uint256 newTotalDebt = _accrueInterest();\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.weth += amount;\n\n        uint256 _supplyFactor = _mint({ user: user, senderOfUnderlying: _msgSender(), amount: amount });\n\n        uint256 _supplyCap = $.wethSupplyCap;\n        if (totalSupply() > _supplyCap) revert DepositSurpassesSupplyCap(amount, _supplyCap);\n\n        emit Supply(user, _msgSender(), amount, _supplyFactor, newTotalDebt);\n    }\n\n    // --- Borrower Operations ---\n\n    /**\n     * @dev Allows a borrower to create debt in a position.\n     * @param ilkIndex index of the collateral.\n     * @param user to create the position for.\n     * @param recipient to receive the borrowed funds\n     * @param amountOfNormalizedDebt to create.\n     * @param proof merkle proof that the user is whitelist.\n     */\n    function borrow(\n        uint8 ilkIndex,\n        address user,\n        address recipient,\n        uint256 amountOfNormalizedDebt,\n        bytes32[] memory proof\n    )\n        external\n        whenNotPaused\n        onlyWhitelistedBorrowers(ilkIndex, user, proof)\n    {\n        _accrueInterest();\n        (uint104 ilkRate, uint256 newDebt) =\n            _modifyPosition(ilkIndex, user, address(0), recipient, 0, amountOfNormalizedDebt.toInt256());\n\n        emit Borrow(ilkIndex, user, recipient, amountOfNormalizedDebt, ilkRate, newDebt);\n    }\n\n    /**\n     * @dev Allows a borrower to repay debt in a position.\n     * @param ilkIndex index of the collateral.\n     * @param user to repay the debt for.\n     * @param payer to source the funds from.\n     * @param amountOfNormalizedDebt to repay.\n     */\n    function repay(\n        uint8 ilkIndex,\n        address user,\n        address payer,\n        uint256 amountOfNormalizedDebt\n    )\n        external\n        whenNotPaused\n    {\n        _accrueInterest();\n        (uint104 ilkRate, uint256 newDebt) =\n            _modifyPosition(ilkIndex, user, address(0), payer, 0, -(amountOfNormalizedDebt.toInt256()));\n\n        emit Repay(ilkIndex, user, payer, amountOfNormalizedDebt, ilkRate, newDebt);\n    }\n\n    /**\n     * @dev Moves collateral from internal `vault.collateral` balances to `gem`\n     * @param ilkIndex index of the collateral.\n     * @param user to withdraw the collateral for.\n     * @param recipient to receive the collateral.\n     * @param amount to withdraw.\n     */\n    function withdrawCollateral(\n        uint8 ilkIndex,\n        address user,\n        address recipient,\n        uint256 amount\n    )\n        external\n        whenNotPaused\n    {\n        _accrueInterest();\n        _modifyPosition(ilkIndex, user, recipient, address(0), -(amount.toInt256()), 0);\n\n        emit WithdrawCollateral(ilkIndex, user, recipient, amount);\n    }\n\n    /**\n     * @dev Moves collateral from `gem` balances to internal `vault.collateral`\n     * @param ilkIndex index of the collateral.\n     * @param user to deposit the collateral for.\n     * @param depositor to deposit the collateral from.\n     * @param amount to deposit.\n     * @param proof merkle proof that the user is whitelisted.\n     */\n    function depositCollateral(\n        uint8 ilkIndex,\n        address user,\n        address depositor,\n        uint256 amount,\n        bytes32[] calldata proof\n    )\n        external\n        whenNotPaused\n        onlyWhitelistedBorrowers(ilkIndex, user, proof)\n    {\n        _accrueInterest();\n        _modifyPosition(ilkIndex, user, depositor, address(0), amount.toInt256(), 0);\n\n        emit DepositCollateral(ilkIndex, user, depositor, amount);\n    }\n\n    // --- CDP Manipulation ---\n\n    function _modifyPosition(\n        uint8 ilkIndex,\n        address u,\n        address v,\n        address w,\n        int256 changeInCollateral,\n        int256 changeInNormalizedDebt\n    )\n        internal\n        returns (uint104 ilkRate, uint256 newTotalDebt)\n    {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        ilkRate = $.ilks[ilkIndex].rate;\n        // ilk has been initialised\n        if (ilkRate == 0) revert IlkNotInitialized(ilkIndex);\n\n        Vault memory _vault = $.vaults[ilkIndex][u];\n        _vault.collateral = _add(_vault.collateral, changeInCollateral);\n        _vault.normalizedDebt = _add(_vault.normalizedDebt, changeInNormalizedDebt);\n\n        uint104 _totalNormalizedDebt = _add($.ilks[ilkIndex].totalNormalizedDebt, changeInNormalizedDebt).toUint104();\n\n        // Prevent stack too deep\n        {\n            uint256 newTotalDebtInVault = ilkRate * _vault.normalizedDebt;\n            // either debt has decreased, or debt ceilings are not exceeded\n            if (\n                both(\n                    changeInNormalizedDebt > 0,\n                    uint256(_totalNormalizedDebt) * uint256(ilkRate) > $.ilks[ilkIndex].debtCeiling\n                )\n            ) {\n                revert CeilingExceeded(uint256(_totalNormalizedDebt) * uint256(ilkRate), $.ilks[ilkIndex].debtCeiling);\n            }\n            uint256 ilkSpot = $.ilks[ilkIndex].spot.getSpot();\n            // vault is either less risky than before, or it is safe\n            if (\n                both(\n                    either(changeInNormalizedDebt > 0, changeInCollateral < 0),\n                    newTotalDebtInVault > _vault.collateral * ilkSpot\n                )\n            ) revert UnsafePositionChange(newTotalDebtInVault, _vault.collateral, ilkSpot);\n\n            // vault is either more safe, or the owner consents\n            if (both(either(changeInNormalizedDebt > 0, changeInCollateral < 0), !isAllowed(u, _msgSender()))) {\n                revert UnsafePositionChangeWithoutConsent(ilkIndex, u, _msgSender());\n            }\n\n            // collateral src consents\n            if (both(changeInCollateral > 0, !isAllowed(v, _msgSender()))) {\n                revert UseOfCollateralWithoutConsent(ilkIndex, v, _msgSender());\n            }\n            // debt dst consents\n            // Since changeInDebt is no longer being deducted in the form of\n            // internal accounting but rather directly in the erc20 WETH form, this\n            // contract must also have an approved role for the debt dst address on\n            // th erc20 WETH contract. Or else, the transfer will fail.\n            if (both(changeInNormalizedDebt < 0, !isAllowed(w, _msgSender()))) {\n                revert TakingWethWithoutConsent(w, _msgSender());\n            }\n\n            // vault has no debt, or a non-dusty amount\n            if (both(_vault.normalizedDebt != 0, newTotalDebtInVault < $.ilks[ilkIndex].dust)) {\n                revert VaultCannotBeDusty(newTotalDebtInVault, $.ilks[ilkIndex].dust);\n            }\n        }\n\n        int256 changeInDebt = ilkRate.toInt256() * changeInNormalizedDebt;\n\n        $.gem[ilkIndex][v] = _sub($.gem[ilkIndex][v], changeInCollateral);\n        $.vaults[ilkIndex][u] = _vault;\n        $.ilks[ilkIndex].totalNormalizedDebt = _totalNormalizedDebt;\n        newTotalDebt = _add($.debt, changeInDebt);\n        $.debt = newTotalDebt;\n\n        // If changeInDebt < 0, it is a repayment and WETH is being transferred\n        // into the protocol\n        _transferWeth(w, changeInDebt);\n    }\n\n    // --- Settlement ---\n\n    /**\n     * @dev To be used by protocol to settle bad debt using reserves\n     * NOTE: Can pay another user's bad debt with the sender's asset\n     * @param user the address that owns the bad debt being paid off\n     * @param rad amount of debt to be repaid (45 decimals)\n     */\n    function repayBadDebt(address user, uint256 rad) external whenNotPaused {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.unbackedDebt[user] -= rad;\n        $.totalUnbackedDebt -= rad;\n        $.debt -= rad;\n\n        // Must be negative since it is a repayment\n        _transferWeth(_msgSender(), -(rad.toInt256()));\n\n        emit RepayBadDebt(user, _msgSender(), rad);\n    }\n\n    // --- Helpers ---\n\n    /**\n     * @dev Helper function to deal with borrowing and repaying debt. A positive\n     * amount is a borrow while negative amount is a repayment\n     * @param user receiver if transfer to, or sender if transfer from\n     * @param amount amount to transfer [RAD]\n     */\n    function _transferWeth(address user, int256 amount) internal {\n        if (amount == 0) return;\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        if (amount < 0) {\n            uint256 amountUint = uint256(-amount);\n            uint256 amountWad = amountUint / RAY;\n            if (amountUint % RAY > 0) ++amountWad;\n\n            $.weth += amountWad;\n            underlying().safeTransferFrom(user, address(this), amountWad);\n        } else {\n            // Round down in protocol's favor\n            uint256 amountWad = uint256(amount) / RAY;\n\n            $.weth -= amountWad;\n\n            underlying().safeTransfer(user, amountWad);\n        }\n    }\n\n    // --- CDP Confiscation ---\n\n    /**\n     * @dev This function foregoes pausability for pausability at the\n     * liquidation module layer\n     * @param ilkIndex index of the collateral.\n     * @param u user to confiscate the vault from.\n     * @param v address to either credit `gem` to or deduct `gem` from\n     * @param changeInCollateral collateral to add or remove from the vault\n     * @param changeInNormalizedDebt debt to add or remove from the vault\n     */\n    function confiscateVault(\n        uint8 ilkIndex,\n        address u,\n        address v,\n        address w,\n        int256 changeInCollateral,\n        int256 changeInNormalizedDebt\n    )\n        external\n        whenNotPaused\n        onlyRole(LIQUIDATOR_ROLE)\n    {\n        _accrueInterest();\n\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        Vault storage _vault = $.vaults[ilkIndex][u];\n        Ilk storage ilk = $.ilks[ilkIndex];\n        uint104 ilkRate = ilk.rate;\n\n        _vault.collateral = _add(_vault.collateral, changeInCollateral);\n        _vault.normalizedDebt = _add(_vault.normalizedDebt, changeInNormalizedDebt);\n        ilk.totalNormalizedDebt = _add(uint256(ilk.totalNormalizedDebt), changeInNormalizedDebt).toUint104();\n\n        // Unsafe cast OK since we know that ilkRate is less than 2^104\n        int256 changeInDebt = int256(uint256(ilkRate)) * changeInNormalizedDebt;\n\n        $.gem[ilkIndex][v] = _sub($.gem[ilkIndex][v], changeInCollateral);\n        $.unbackedDebt[w] = _sub($.unbackedDebt[w], changeInDebt);\n        $.totalUnbackedDebt = _sub($.totalUnbackedDebt, changeInDebt);\n\n        emit ConfiscateVault(ilkIndex, u, v, w, changeInCollateral, changeInNormalizedDebt);\n    }\n\n    // --- Fungibility ---\n\n    /**\n     * @dev To be called by GemJoin contracts. After a user deposits collateral, credit the user with collateral\n     * internally\n     * @param ilkIndex collateral\n     * @param usr user\n     * @param wad amount to add or remove\n     */\n    function mintAndBurnGem(uint8 ilkIndex, address usr, int256 wad) external onlyRole(GEM_JOIN_ROLE) whenNotPaused {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.gem[ilkIndex][usr] = _add($.gem[ilkIndex][usr], wad);\n\n        emit MintAndBurnGem(ilkIndex, usr, wad);\n    }\n\n    /**\n     * @dev Transfer gem across the internal accounting of the pool\n     * @param ilkIndex index of the collateral\n     * @param src source of the gem\n     * @param dst destination of the gem\n     * @param wad amount of gem\n     */\n    function transferGem(uint8 ilkIndex, address src, address dst, uint256 wad) external whenNotPaused {\n        if (!isAllowed(src, _msgSender())) revert GemTransferWithoutConsent(ilkIndex, src, _msgSender());\n\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.gem[ilkIndex][src] -= wad;\n        $.gem[ilkIndex][dst] += wad;\n        emit TransferGem(ilkIndex, src, dst, wad);\n    }\n\n    // --- Getters ---\n\n    /**\n     * @return The total amount of collateral in the pool.\n     */\n    function ilkCount() external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks.length;\n    }\n\n    /**\n     * @return The index of the collateral with `ilkAddress`.\n     */\n    function getIlkIndex(address ilkAddress) external view returns (uint8) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        bytes32 addressInBytes32 = bytes32(uint256(uint160(ilkAddress)));\n\n        // Since there should never be more than 256 collaterals, an unsafe cast\n        // should be fine\n        return uint8($.ilkAddresses._inner._positions[addressInBytes32] - 1);\n    }\n\n    /**\n     * @return The address of the collateral at index `ilkIndex`.\n     */\n    function getIlkAddress(uint256 ilkIndex) external view returns (address) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilkAddresses.at(ilkIndex);\n    }\n\n    /**\n     * @return Whether or not an address is a supported collateral.\n     */\n    function addressContains(address ilk) external view returns (bool) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilkAddresses.contains(ilk);\n    }\n\n    /**\n     * @return The total amount of normalized debt for collateral with index\n     * `ilkIndex`.\n     */\n    function totalNormalizedDebt(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].totalNormalizedDebt;\n    }\n\n    function rateUnaccrued(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].rate;\n    }\n\n    /**\n     * @return The rate (debt accumulator) for collateral with index `ilkIndex`.\n     */\n    function rate(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        (uint256 newRateIncrease,) = calculateRewardAndDebtDistributionForIlk(ilkIndex);\n\n        return $.ilks[ilkIndex].rate + newRateIncrease;\n    }\n\n    /**\n     * @return The timestamp of the last rate update for collateral with index\n     * `ilkIndex`.\n     */\n    function lastRateUpdate(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].lastRateUpdate;\n    }\n\n    /**\n     * @return The spot oracle for collateral with index `ilkIndex`.\n     */\n    function spot(uint8 ilkIndex) external view returns (SpotOracle) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].spot;\n    }\n\n    /**\n     * @return debt ceiling for collateral with index `ilkIndex`.\n     */\n    function debtCeiling(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].debtCeiling;\n    }\n\n    /**\n     * @return dust amount for collateral with index `ilkIndex`.\n     */\n    function dust(uint8 ilkIndex) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.ilks[ilkIndex].dust;\n    }\n\n    /**\n     * @return The amount of collateral `user` has for collateral with index `ilkIndex`.\n     */\n    function collateral(uint8 ilkIndex, address user) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.vaults[ilkIndex][user].collateral;\n    }\n\n    /**\n     * @return The amount of normalized debt `user` has for collateral with index `ilkIndex`.\n     */\n    function normalizedDebt(uint8 ilkIndex, address user) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.vaults[ilkIndex][user].normalizedDebt;\n    }\n\n    /**\n     * @return All data within vault for `user` with index `ilkIndex`.\n     */\n    function vault(uint8 ilkIndex, address user) external view returns (uint256, uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return ($.vaults[ilkIndex][user].collateral, $.vaults[ilkIndex][user].normalizedDebt);\n    }\n\n    /**\n     * @return Amount of `gem` that `user` has for collateral with index `ilkIndex`.\n     */\n    function gem(uint8 ilkIndex, address user) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.gem[ilkIndex][user];\n    }\n\n    /**\n     * @return The amount of unbacked debt `user` has.\n     */\n    function unbackedDebt(address user) external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.unbackedDebt[user];\n    }\n\n    /**\n     * @return Whether or not `operator` is an `operator` on `user`'s positions.\n     */\n    function isOperator(address user, address operator) external view returns (bool) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.isOperator[user][operator] == 1;\n    }\n\n    /**\n     * @return Whether or not `operator` has permission to make unsafe changes\n     * to `user`'s positions.\n     */\n    function isAllowed(address user, address operator) public view returns (bool) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        return either(user == operator, $.isOperator[user][operator] == 1);\n    }\n\n    function debtUnaccrued() external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.debt;\n    }\n\n    /**\n     * @dev This includes unbacked debt.\n     * @return The total amount of debt.\n     */\n    function debt() external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        (,,, uint256 totalDebtIncrease,) = calculateRewardAndDebtDistribution();\n\n        return $.debt + totalDebtIncrease;\n    }\n\n    /**\n     * @return The total amount of unbacked debt.\n     */\n    function totalUnbackedDebt() external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.totalUnbackedDebt;\n    }\n\n    /**\n     * @return The address of interest rate module.\n     */\n    function interestRateModule() external view returns (address) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return address($.interestRateModule);\n    }\n\n    /**\n     * @return The address of the whitelist.\n     */\n    function whitelist() external view returns (address) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return address($.whitelist);\n    }\n\n    /**\n     * @return The total amount of ETH liquidity in the pool.\n     */\n    function weth() external view returns (uint256) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n        return $.weth;\n    }\n\n    /**\n     * @dev Gets the current borrow rate for borrowing against a given collateral.\n     */\n    function getCurrentBorrowRate(uint8 ilkIndex) external view returns (uint256 borrowRate, uint256 reserveFactor) {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        uint256 totalEthSupply = totalSupplyUnaccrued();\n        uint256 _totalNormalizedDebt = $.ilks[ilkIndex].totalNormalizedDebt;\n        uint256 _rate = $.ilks[ilkIndex].rate;\n\n        uint256 totalDebt = _totalNormalizedDebt * _rate; // [WAD] * [RAY] / [WAD] = [RAY]\n\n        (borrowRate, reserveFactor) = $.interestRateModule.calculateInterestRate(ilkIndex, totalDebt, totalEthSupply);\n        borrowRate += RAY;\n    }\n\n    /**\n     * @dev Address of the implementation. This is stored immutably on the\n     * implementation so that it can be read by the proxy.\n     */\n    function implementation() external view returns (address) {\n        return ADDRESS_THIS;\n    }\n\n    // --- Auth ---\n\n    /**\n     * @dev Allows an `operator` to make unsafe changes to `_msgSender()`s\n     * positions.\n     */\n    function addOperator(address operator) external {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.isOperator[_msgSender()][operator] = 1;\n\n        emit AddOperator(_msgSender(), operator);\n    }\n\n    /**\n     * @dev Disallows an `operator` to make unsafe changes to `_msgSender()`s\n     * positions.\n     */\n    function removeOperator(address operator) external {\n        IonPoolStorage storage $ = _getIonPoolStorage();\n\n        $.isOperator[_msgSender()][operator] = 0;\n\n        emit RemoveOperator(_msgSender(), operator);\n    }\n\n    // --- Math ---\n\n    function _add(uint256 x, int256 y) internal pure returns (uint256 z) {\n        // Overflow desirable\n        unchecked {\n            z = x + uint256(y);\n        }\n        if (y < 0 && z > x) revert ArithmeticError();\n        if (y > 0 && z < x) revert ArithmeticError();\n    }\n\n    function _sub(uint256 x, int256 y) internal pure returns (uint256 z) {\n        // Underflow desirable\n        unchecked {\n            z = x - uint256(y);\n        }\n        if (y > 0 && z > x) revert ArithmeticError();\n        if (y < 0 && z < x) revert ArithmeticError();\n    }\n\n    /**\n     * @dev x and the returned value are to be interpreted as fixed-point\n     * integers with scaling factor b. For example, if b == 100, this specifies\n     * two decimal digits of precision and the normal decimal value 2.1 would be\n     * represented as 210; rpow(210, 2, 100) returns 441 (the two-decimal digit\n     * fixed-point representation of 2.1^2 = 4.41) (From MCD docs)\n     * @param x base\n     * @param n exponent\n     * @param b scaling factor\n     */\n    function _rpow(uint256 x, uint256 n, uint256 b) internal pure returns (uint256 z) {\n        assembly {\n            switch x\n            case 0 {\n                switch n\n                case 0 { z := b }\n                default { z := 0 }\n            }\n            default {\n                switch mod(n, 2)\n                case 0 { z := b }\n                default { z := x }\n                let half := div(b, 2) // for rounding.\n                for { n := div(n, 2) } n { n := div(n, 2) } {\n                    let xx := mul(x, x)\n                    if iszero(eq(div(xx, x), x)) { revert(0, 0) }\n                    let xxRound := add(xx, half)\n                    if lt(xxRound, xx) { revert(0, 0) }\n                    x := div(xxRound, b)\n                    if mod(n, 2) {\n                        let zx := mul(z, x)\n                        if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }\n                        let zxRound := add(zx, half)\n                        if lt(zxRound, zx) { revert(0, 0) }\n                        z := div(zxRound, b)\n                    }\n                }\n            }\n        }\n    }\n\n    // --- Boolean ---\n\n    function either(bool x, bool y) internal pure returns (bool z) {\n        assembly {\n            z := or(x, y)\n        }\n    }\n\n    function both(bool x, bool y) internal pure returns (bool z) {\n        assembly {\n            z := and(x, y)\n        }\n    }\n}\n"
      },
      "src/libraries/math/WadRayMath.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nuint256 constant WAD = 1e18;\nuint256 constant RAY = 1e27;\nuint256 constant RAD = 1e45;\n\n/**\n * @title WadRayMath\n *\n * @notice This library provides mul/div[up/down] functionality for WAD, RAY and\n * RAD with phantom overflow protection as well as scale[up/down] functionality\n * for WAD, RAY and RAD.\n *\n * @custom:security-contact security@molecularlabs.io\n */\nlibrary WadRayMath {\n    using Math for uint256;\n\n    error NotScalingUp(uint256 from, uint256 to);\n    error NotScalingDown(uint256 from, uint256 to);\n\n    /**\n     * @notice Multiplies two WAD numbers and returns the result as a WAD\n     * rounding the result down.\n     * @param a Multiplicand.\n     * @param b Multiplier.\n     */\n    function wadMulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, WAD);\n    }\n\n    /**\n     * @notice Multiplies two WAD numbers and returns the result as a WAD\n     * rounding the result up.\n     * @param a Multiplicand.\n     * @param b Multiplier.\n     */\n    function wadMulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, WAD, Math.Rounding.Ceil);\n    }\n\n    /**\n     * @notice Divides two WAD numbers and returns the result as a WAD rounding\n     * the result down.\n     * @param a Dividend.\n     * @param b Divisor.\n     */\n    function wadDivDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(WAD, b);\n    }\n\n    /**\n     * @notice Divides two WAD numbers and returns the result as a WAD rounding\n     * the result up.\n     * @param a Dividend.\n     * @param b Divisor.\n     */\n    function wadDivUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(WAD, b, Math.Rounding.Ceil);\n    }\n\n    /**\n     * @notice Multiplies two RAY numbers and returns the result as a RAY\n     * rounding the result down.\n     * @param a Multiplicand\n     * @param b Multiplier\n     */\n    function rayMulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, RAY);\n    }\n\n    /**\n     * @notice Multiplies two RAY numbers and returns the result as a RAY\n     * rounding the result up.\n     * @param a Multiplicand\n     * @param b Multiplier\n     */\n    function rayMulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, RAY, Math.Rounding.Ceil);\n    }\n\n    /**\n     * @notice Divides two RAY numbers and returns the result as a RAY\n     * rounding the result down.\n     * @param a Dividend\n     * @param b Divisor\n     */\n    function rayDivDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(RAY, b);\n    }\n\n    /**\n     * @notice Divides two RAY numbers and returns the result as a RAY\n     * rounding the result up.\n     * @param a Dividend\n     * @param b Divisor\n     */\n    function rayDivUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(RAY, b, Math.Rounding.Ceil);\n    }\n\n    /**\n     * @notice Multiplies two RAD numbers and returns the result as a RAD\n     * rounding the result down.\n     * @param a Multiplicand\n     * @param b Multiplier\n     */\n    function radMulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, RAD);\n    }\n\n    /**\n     * @notice Multiplies two RAD numbers and returns the result as a RAD\n     * rounding the result up.\n     * @param a Multiplicand\n     * @param b Multiplier\n     */\n    function radMulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(b, RAD, Math.Rounding.Ceil);\n    }\n\n    /**\n     * @notice Divides two RAD numbers and returns the result as a RAD rounding\n     * the result down.\n     * @param a Dividend\n     * @param b Divisor\n     */\n    function radDivDown(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(RAD, b);\n    }\n\n    /**\n     * @notice Divides two RAD numbers and returns the result as a RAD rounding\n     * the result up.\n     * @param a Dividend\n     * @param b Divisor\n     */\n    function radDivUp(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a.mulDiv(RAD, b, Math.Rounding.Ceil);\n    }\n\n    // --- Scalers ---\n\n    /**\n     * @notice Scales a value up from WAD. NOTE: The `scale` value must be\n     * less than 18.\n     * @param value to scale up.\n     * @param scale of the returned value.\n     */\n    function scaleUpToWad(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleUp(value, scale, 18);\n    }\n\n    /**\n     * @notice Scales a value up from RAY. NOTE: The `scale` value must be\n     * less than 27.\n     * @param value to scale up.\n     * @param scale of the returned value.\n     */\n    function scaleUpToRay(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleUp(value, scale, 27);\n    }\n\n    /**\n     * @notice Scales a value up from RAD. NOTE: The `scale` value must be\n     * less than 45.\n     * @param value to scale up.\n     * @param scale of the returned value.\n     */\n    function scaleUpToRad(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleUp(value, scale, 45);\n    }\n\n    /**\n     * @notice Scales a value down to WAD. NOTE: The `scale` value must be\n     * greater than 18.\n     * @param value to scale down.\n     * @param scale of the returned value.\n     */\n    function scaleDownToWad(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleDown(value, scale, 18);\n    }\n\n    /**\n     * @notice Scales a value down to RAY. NOTE: The `scale` value must be\n     * greater than 27.\n     * @param value to scale down.\n     * @param scale of the returned value.\n     */\n    function scaleDownToRay(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleDown(value, scale, 27);\n    }\n\n    /**\n     * @notice Scales a value down to RAD. NOTE: The `scale` value must be\n     * greater than 45.\n     * @param value to scale down.\n     * @param scale of the returned value.\n     */\n    function scaleDownToRad(uint256 value, uint256 scale) internal pure returns (uint256) {\n        return scaleDown(value, scale, 45);\n    }\n\n    /**\n     * @notice Scales a value up from one fixed-point precision to another.\n     * @param value to scale up.\n     * @param from Precision to scale from.\n     * @param to Precision to scale to.\n     */\n    function scaleUp(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {\n        if (from >= to) revert NotScalingUp(from, to);\n        return value * (10 ** (to - from));\n    }\n\n    /**\n     * @notice Scales a value down from one fixed-point precision to another.\n     * @param value to scale down.\n     * @param from Precision to scale from.\n     * @param to Precision to scale to.\n     */\n    function scaleDown(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {\n        if (from <= to) revert NotScalingDown(from, to);\n        return value / (10 ** (from - to));\n    }\n}\n"
      },
      "src/oracles/reserve/ReserveOracle.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\nimport { IReserveFeed } from \"../../interfaces/IReserveFeed.sol\";\nimport { WadRayMath, RAY } from \"../../libraries/math/WadRayMath.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\n// should equal to the number of feeds available in the contract\nuint8 constant FEED_COUNT = 3;\nuint256 constant UPDATE_COOLDOWN = 58 minutes;\n\n/**\n * @notice Reserve oracles are used to determine the LST provider exchange rate\n * and is utilizated by Ion's liquidation module. Liquidations will only be\n * triggered against this exchange rate and will be completely market-price\n * agnostic. Importantly, this means that liquidations will only be triggered\n * through lack of debt repayment or slashing events.\n *\n * @dev In order to protect against potential provider bugs or incorrect one-off\n * values (malicious or accidental), the reserve oracle does not use live data.\n * Instead it will query the exchange every intermittent period and persist the\n * value and this value can only move up or down by a maximum percentage per query.\n *\n * If additional data sources are available, they can be involved as `FEED`s. If\n * other `FEED`s are provided to the reserve oracle, a mean of all the `FEED`s\n * is compared to the protocol exchange rate and the minimum of the two is used\n * as the new exchange rate. This final value is subject to the bounding rules.\n *\n * @custom:security-contact security@molecularlabs.io\n */\nabstract contract ReserveOracle {\n    using WadRayMath for uint256;\n\n    uint8 public immutable ILK_INDEX;\n    uint8 public immutable QUORUM; // the number of feeds to aggregate\n    uint256 public immutable MAX_CHANGE; // maximum change allowed in percentage [ray] i.e. 3e25 [ray] would be 3%\n\n    IReserveFeed public immutable FEED0; // different reserve oracle feeds excluding the protocol exchange rate\n    IReserveFeed public immutable FEED1;\n    IReserveFeed public immutable FEED2;\n\n    uint256 public currentExchangeRate; // [wad] the bounded queried last time\n    uint256 public lastUpdated; // [wad] the bounded queried last time\n\n    // --- Events ---\n    event UpdateExchangeRate(uint256 exchangeRate);\n\n    // --- Errors ---\n    error InvalidQuorum(uint8 invalidQuorum);\n    error InvalidFeedLength(uint256 invalidLength);\n    error InvalidMaxChange(uint256 invalidMaxChange);\n    error InvalidMinMax(uint256 invalidMin, uint256 invalidMax);\n    error InvalidInitialization(uint256 invalidExchangeRate);\n    error UpdateCooldown(uint256 lastUpdated);\n\n    /**\n     * @notice Creates a new `ReserveOracle` instance.\n     * @param _ilkIndex of the associated collateral.\n     * @param _feeds Alternative data sources to be used for the reserve oracle.\n     * @param _quorum The number of feeds to aggregate.\n     * @param _maxChange Maximum percent change between exchange rate updates. [RAY]\n     */\n    constructor(uint8 _ilkIndex, address[] memory _feeds, uint8 _quorum, uint256 _maxChange) {\n        if (_feeds.length != FEED_COUNT) revert InvalidFeedLength(_feeds.length);\n        if (_quorum > FEED_COUNT) revert InvalidQuorum(_quorum);\n        if (_maxChange == 0 || _maxChange > RAY) revert InvalidMaxChange(_maxChange);\n\n        ILK_INDEX = _ilkIndex;\n        QUORUM = _quorum;\n        MAX_CHANGE = _maxChange;\n\n        FEED0 = IReserveFeed(_feeds[0]);\n        FEED1 = IReserveFeed(_feeds[1]);\n        FEED2 = IReserveFeed(_feeds[2]);\n    }\n\n    // --- Override ---\n\n    /**\n     * @notice Returns the protocol exchange rate.\n     * @dev Must be implemented in the child contract with LST-specific logic.\n     * @return The protocol exchange rate.\n     */\n    function _getProtocolExchangeRate() internal view virtual returns (uint256);\n\n    /**\n     * @notice Returns the protocol exchange rate.\n     * @return The protocol exchange rate.\n     */\n    function getProtocolExchangeRate() external view returns (uint256) {\n        return _getProtocolExchangeRate();\n    }\n\n    /**\n     * @notice Queries values from whitelisted data feeds and calculates the\n     * mean. This does not include the protocol exchange rate.\n     * @param _ILK_INDEX of the associated collateral.\n     */\n    function _aggregate(uint8 _ILK_INDEX) internal view returns (uint256 val) {\n        if (QUORUM == 0) {\n            return type(uint256).max;\n        } else if (QUORUM == 1) {\n            val = FEED0.getExchangeRate(_ILK_INDEX);\n        } else if (QUORUM == 2) {\n            uint256 feed0ExchangeRate = FEED0.getExchangeRate(_ILK_INDEX);\n            uint256 feed1ExchangeRate = FEED1.getExchangeRate(_ILK_INDEX);\n            val = ((feed0ExchangeRate + feed1ExchangeRate) / uint256(QUORUM));\n        } else if (QUORUM == 3) {\n            uint256 feed0ExchangeRate = FEED0.getExchangeRate(_ILK_INDEX);\n            uint256 feed1ExchangeRate = FEED1.getExchangeRate(_ILK_INDEX);\n            uint256 feed2ExchangeRate = FEED2.getExchangeRate(_ILK_INDEX);\n            val = ((feed0ExchangeRate + feed1ExchangeRate + feed2ExchangeRate) / uint256(QUORUM));\n        }\n    }\n\n    /**\n     * @notice Bounds the value between the min and the max.\n     * @param value The value to be bounded.\n     * @param min The minimum bound.\n     * @param max The maximum bound.\n     */\n    function _bound(uint256 value, uint256 min, uint256 max) internal pure returns (uint256) {\n        if (min > max) revert InvalidMinMax(min, max);\n\n        return Math.max(min, Math.min(max, value));\n    }\n\n    /**\n     * @notice Initializes the `currentExchangeRate` state variable.\n     * @dev Called once during construction.\n     */\n    function _initializeExchangeRate() internal {\n        currentExchangeRate = Math.min(_getProtocolExchangeRate(), _aggregate(ILK_INDEX));\n        if (currentExchangeRate == 0) {\n            revert InvalidInitialization(currentExchangeRate);\n        }\n\n        emit UpdateExchangeRate(currentExchangeRate);\n    }\n\n    /**\n     * @notice Updates the `currentExchangeRate` state variable.\n     * @dev Takes the minimum between the aggregated values and the protocol exchange rate,\n     * then bounds it up to the maximum change and writes the bounded value to the state.\n     * NOTE: keepers should call this update to reflect recent values\n     */\n    function updateExchangeRate() external {\n        if (block.timestamp - lastUpdated < UPDATE_COOLDOWN) revert UpdateCooldown(lastUpdated);\n\n        uint256 _currentExchangeRate = currentExchangeRate;\n\n        uint256 minimum = Math.min(_getProtocolExchangeRate(), _aggregate(ILK_INDEX));\n        uint256 diff = _currentExchangeRate.rayMulDown(MAX_CHANGE);\n\n        uint256 bounded = _bound(minimum, _currentExchangeRate - diff, _currentExchangeRate + diff);\n        currentExchangeRate = bounded;\n\n        lastUpdated = block.timestamp;\n\n        emit UpdateExchangeRate(bounded);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC20Permit} from \"../extensions/IERC20Permit.sol\";\nimport {Address} from \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev An operation with an ERC20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data);\n        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n    /**\n     * @dev Value doesn't fit in an uint of `bits` size.\n     */\n    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n    /**\n     * @dev An int value doesn't fit in an uint of `bits` size.\n     */\n    error SafeCastOverflowedIntToUint(int256 value);\n\n    /**\n     * @dev Value doesn't fit in an int of `bits` size.\n     */\n    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n    /**\n     * @dev An uint value doesn't fit in an int of `bits` size.\n     */\n    error SafeCastOverflowedUintToInt(uint256 value);\n\n    /**\n     * @dev Returns the downcasted uint248 from uint256, reverting on\n     * overflow (when the input is greater than largest uint248).\n     *\n     * Counterpart to Solidity's `uint248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     */\n    function toUint248(uint256 value) internal pure returns (uint248) {\n        if (value > type(uint248).max) {\n            revert SafeCastOverflowedUintDowncast(248, value);\n        }\n        return uint248(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint240 from uint256, reverting on\n     * overflow (when the input is greater than largest uint240).\n     *\n     * Counterpart to Solidity's `uint240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     */\n    function toUint240(uint256 value) internal pure returns (uint240) {\n        if (value > type(uint240).max) {\n            revert SafeCastOverflowedUintDowncast(240, value);\n        }\n        return uint240(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint232 from uint256, reverting on\n     * overflow (when the input is greater than largest uint232).\n     *\n     * Counterpart to Solidity's `uint232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     */\n    function toUint232(uint256 value) internal pure returns (uint232) {\n        if (value > type(uint232).max) {\n            revert SafeCastOverflowedUintDowncast(232, value);\n        }\n        return uint232(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint224 from uint256, reverting on\n     * overflow (when the input is greater than largest uint224).\n     *\n     * Counterpart to Solidity's `uint224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     */\n    function toUint224(uint256 value) internal pure returns (uint224) {\n        if (value > type(uint224).max) {\n            revert SafeCastOverflowedUintDowncast(224, value);\n        }\n        return uint224(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint216 from uint256, reverting on\n     * overflow (when the input is greater than largest uint216).\n     *\n     * Counterpart to Solidity's `uint216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     */\n    function toUint216(uint256 value) internal pure returns (uint216) {\n        if (value > type(uint216).max) {\n            revert SafeCastOverflowedUintDowncast(216, value);\n        }\n        return uint216(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint208 from uint256, reverting on\n     * overflow (when the input is greater than largest uint208).\n     *\n     * Counterpart to Solidity's `uint208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     */\n    function toUint208(uint256 value) internal pure returns (uint208) {\n        if (value > type(uint208).max) {\n            revert SafeCastOverflowedUintDowncast(208, value);\n        }\n        return uint208(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint200 from uint256, reverting on\n     * overflow (when the input is greater than largest uint200).\n     *\n     * Counterpart to Solidity's `uint200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     */\n    function toUint200(uint256 value) internal pure returns (uint200) {\n        if (value > type(uint200).max) {\n            revert SafeCastOverflowedUintDowncast(200, value);\n        }\n        return uint200(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint192 from uint256, reverting on\n     * overflow (when the input is greater than largest uint192).\n     *\n     * Counterpart to Solidity's `uint192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     */\n    function toUint192(uint256 value) internal pure returns (uint192) {\n        if (value > type(uint192).max) {\n            revert SafeCastOverflowedUintDowncast(192, value);\n        }\n        return uint192(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint184 from uint256, reverting on\n     * overflow (when the input is greater than largest uint184).\n     *\n     * Counterpart to Solidity's `uint184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     */\n    function toUint184(uint256 value) internal pure returns (uint184) {\n        if (value > type(uint184).max) {\n            revert SafeCastOverflowedUintDowncast(184, value);\n        }\n        return uint184(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint176 from uint256, reverting on\n     * overflow (when the input is greater than largest uint176).\n     *\n     * Counterpart to Solidity's `uint176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     */\n    function toUint176(uint256 value) internal pure returns (uint176) {\n        if (value > type(uint176).max) {\n            revert SafeCastOverflowedUintDowncast(176, value);\n        }\n        return uint176(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint168 from uint256, reverting on\n     * overflow (when the input is greater than largest uint168).\n     *\n     * Counterpart to Solidity's `uint168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     */\n    function toUint168(uint256 value) internal pure returns (uint168) {\n        if (value > type(uint168).max) {\n            revert SafeCastOverflowedUintDowncast(168, value);\n        }\n        return uint168(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint160 from uint256, reverting on\n     * overflow (when the input is greater than largest uint160).\n     *\n     * Counterpart to Solidity's `uint160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     */\n    function toUint160(uint256 value) internal pure returns (uint160) {\n        if (value > type(uint160).max) {\n            revert SafeCastOverflowedUintDowncast(160, value);\n        }\n        return uint160(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint152 from uint256, reverting on\n     * overflow (when the input is greater than largest uint152).\n     *\n     * Counterpart to Solidity's `uint152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     */\n    function toUint152(uint256 value) internal pure returns (uint152) {\n        if (value > type(uint152).max) {\n            revert SafeCastOverflowedUintDowncast(152, value);\n        }\n        return uint152(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint144 from uint256, reverting on\n     * overflow (when the input is greater than largest uint144).\n     *\n     * Counterpart to Solidity's `uint144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     */\n    function toUint144(uint256 value) internal pure returns (uint144) {\n        if (value > type(uint144).max) {\n            revert SafeCastOverflowedUintDowncast(144, value);\n        }\n        return uint144(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint136 from uint256, reverting on\n     * overflow (when the input is greater than largest uint136).\n     *\n     * Counterpart to Solidity's `uint136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     */\n    function toUint136(uint256 value) internal pure returns (uint136) {\n        if (value > type(uint136).max) {\n            revert SafeCastOverflowedUintDowncast(136, value);\n        }\n        return uint136(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint128 from uint256, reverting on\n     * overflow (when the input is greater than largest uint128).\n     *\n     * Counterpart to Solidity's `uint128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        if (value > type(uint128).max) {\n            revert SafeCastOverflowedUintDowncast(128, value);\n        }\n        return uint128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint120 from uint256, reverting on\n     * overflow (when the input is greater than largest uint120).\n     *\n     * Counterpart to Solidity's `uint120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     */\n    function toUint120(uint256 value) internal pure returns (uint120) {\n        if (value > type(uint120).max) {\n            revert SafeCastOverflowedUintDowncast(120, value);\n        }\n        return uint120(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint112 from uint256, reverting on\n     * overflow (when the input is greater than largest uint112).\n     *\n     * Counterpart to Solidity's `uint112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     */\n    function toUint112(uint256 value) internal pure returns (uint112) {\n        if (value > type(uint112).max) {\n            revert SafeCastOverflowedUintDowncast(112, value);\n        }\n        return uint112(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint104 from uint256, reverting on\n     * overflow (when the input is greater than largest uint104).\n     *\n     * Counterpart to Solidity's `uint104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     */\n    function toUint104(uint256 value) internal pure returns (uint104) {\n        if (value > type(uint104).max) {\n            revert SafeCastOverflowedUintDowncast(104, value);\n        }\n        return uint104(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint96 from uint256, reverting on\n     * overflow (when the input is greater than largest uint96).\n     *\n     * Counterpart to Solidity's `uint96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     */\n    function toUint96(uint256 value) internal pure returns (uint96) {\n        if (value > type(uint96).max) {\n            revert SafeCastOverflowedUintDowncast(96, value);\n        }\n        return uint96(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint88 from uint256, reverting on\n     * overflow (when the input is greater than largest uint88).\n     *\n     * Counterpart to Solidity's `uint88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     */\n    function toUint88(uint256 value) internal pure returns (uint88) {\n        if (value > type(uint88).max) {\n            revert SafeCastOverflowedUintDowncast(88, value);\n        }\n        return uint88(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint80 from uint256, reverting on\n     * overflow (when the input is greater than largest uint80).\n     *\n     * Counterpart to Solidity's `uint80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     */\n    function toUint80(uint256 value) internal pure returns (uint80) {\n        if (value > type(uint80).max) {\n            revert SafeCastOverflowedUintDowncast(80, value);\n        }\n        return uint80(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint72 from uint256, reverting on\n     * overflow (when the input is greater than largest uint72).\n     *\n     * Counterpart to Solidity's `uint72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     */\n    function toUint72(uint256 value) internal pure returns (uint72) {\n        if (value > type(uint72).max) {\n            revert SafeCastOverflowedUintDowncast(72, value);\n        }\n        return uint72(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint64 from uint256, reverting on\n     * overflow (when the input is greater than largest uint64).\n     *\n     * Counterpart to Solidity's `uint64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        if (value > type(uint64).max) {\n            revert SafeCastOverflowedUintDowncast(64, value);\n        }\n        return uint64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint56 from uint256, reverting on\n     * overflow (when the input is greater than largest uint56).\n     *\n     * Counterpart to Solidity's `uint56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     */\n    function toUint56(uint256 value) internal pure returns (uint56) {\n        if (value > type(uint56).max) {\n            revert SafeCastOverflowedUintDowncast(56, value);\n        }\n        return uint56(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint48 from uint256, reverting on\n     * overflow (when the input is greater than largest uint48).\n     *\n     * Counterpart to Solidity's `uint48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     */\n    function toUint48(uint256 value) internal pure returns (uint48) {\n        if (value > type(uint48).max) {\n            revert SafeCastOverflowedUintDowncast(48, value);\n        }\n        return uint48(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint40 from uint256, reverting on\n     * overflow (when the input is greater than largest uint40).\n     *\n     * Counterpart to Solidity's `uint40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     */\n    function toUint40(uint256 value) internal pure returns (uint40) {\n        if (value > type(uint40).max) {\n            revert SafeCastOverflowedUintDowncast(40, value);\n        }\n        return uint40(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint32 from uint256, reverting on\n     * overflow (when the input is greater than largest uint32).\n     *\n     * Counterpart to Solidity's `uint32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        if (value > type(uint32).max) {\n            revert SafeCastOverflowedUintDowncast(32, value);\n        }\n        return uint32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint24 from uint256, reverting on\n     * overflow (when the input is greater than largest uint24).\n     *\n     * Counterpart to Solidity's `uint24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     */\n    function toUint24(uint256 value) internal pure returns (uint24) {\n        if (value > type(uint24).max) {\n            revert SafeCastOverflowedUintDowncast(24, value);\n        }\n        return uint24(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint16 from uint256, reverting on\n     * overflow (when the input is greater than largest uint16).\n     *\n     * Counterpart to Solidity's `uint16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        if (value > type(uint16).max) {\n            revert SafeCastOverflowedUintDowncast(16, value);\n        }\n        return uint16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint8 from uint256, reverting on\n     * overflow (when the input is greater than largest uint8).\n     *\n     * Counterpart to Solidity's `uint8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        if (value > type(uint8).max) {\n            revert SafeCastOverflowedUintDowncast(8, value);\n        }\n        return uint8(value);\n    }\n\n    /**\n     * @dev Converts a signed int256 into an unsigned uint256.\n     *\n     * Requirements:\n     *\n     * - input must be greater than or equal to 0.\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        if (value < 0) {\n            revert SafeCastOverflowedIntToUint(value);\n        }\n        return uint256(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int248 from int256, reverting on\n     * overflow (when the input is less than smallest int248 or\n     * greater than largest int248).\n     *\n     * Counterpart to Solidity's `int248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     */\n    function toInt248(int256 value) internal pure returns (int248 downcasted) {\n        downcasted = int248(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(248, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int240 from int256, reverting on\n     * overflow (when the input is less than smallest int240 or\n     * greater than largest int240).\n     *\n     * Counterpart to Solidity's `int240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     */\n    function toInt240(int256 value) internal pure returns (int240 downcasted) {\n        downcasted = int240(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(240, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int232 from int256, reverting on\n     * overflow (when the input is less than smallest int232 or\n     * greater than largest int232).\n     *\n     * Counterpart to Solidity's `int232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     */\n    function toInt232(int256 value) internal pure returns (int232 downcasted) {\n        downcasted = int232(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(232, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int224 from int256, reverting on\n     * overflow (when the input is less than smallest int224 or\n     * greater than largest int224).\n     *\n     * Counterpart to Solidity's `int224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     */\n    function toInt224(int256 value) internal pure returns (int224 downcasted) {\n        downcasted = int224(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(224, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int216 from int256, reverting on\n     * overflow (when the input is less than smallest int216 or\n     * greater than largest int216).\n     *\n     * Counterpart to Solidity's `int216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     */\n    function toInt216(int256 value) internal pure returns (int216 downcasted) {\n        downcasted = int216(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(216, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int208 from int256, reverting on\n     * overflow (when the input is less than smallest int208 or\n     * greater than largest int208).\n     *\n     * Counterpart to Solidity's `int208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     */\n    function toInt208(int256 value) internal pure returns (int208 downcasted) {\n        downcasted = int208(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(208, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int200 from int256, reverting on\n     * overflow (when the input is less than smallest int200 or\n     * greater than largest int200).\n     *\n     * Counterpart to Solidity's `int200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     */\n    function toInt200(int256 value) internal pure returns (int200 downcasted) {\n        downcasted = int200(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(200, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int192 from int256, reverting on\n     * overflow (when the input is less than smallest int192 or\n     * greater than largest int192).\n     *\n     * Counterpart to Solidity's `int192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     */\n    function toInt192(int256 value) internal pure returns (int192 downcasted) {\n        downcasted = int192(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(192, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int184 from int256, reverting on\n     * overflow (when the input is less than smallest int184 or\n     * greater than largest int184).\n     *\n     * Counterpart to Solidity's `int184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     */\n    function toInt184(int256 value) internal pure returns (int184 downcasted) {\n        downcasted = int184(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(184, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int176 from int256, reverting on\n     * overflow (when the input is less than smallest int176 or\n     * greater than largest int176).\n     *\n     * Counterpart to Solidity's `int176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     */\n    function toInt176(int256 value) internal pure returns (int176 downcasted) {\n        downcasted = int176(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(176, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int168 from int256, reverting on\n     * overflow (when the input is less than smallest int168 or\n     * greater than largest int168).\n     *\n     * Counterpart to Solidity's `int168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     */\n    function toInt168(int256 value) internal pure returns (int168 downcasted) {\n        downcasted = int168(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(168, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int160 from int256, reverting on\n     * overflow (when the input is less than smallest int160 or\n     * greater than largest int160).\n     *\n     * Counterpart to Solidity's `int160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     */\n    function toInt160(int256 value) internal pure returns (int160 downcasted) {\n        downcasted = int160(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(160, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int152 from int256, reverting on\n     * overflow (when the input is less than smallest int152 or\n     * greater than largest int152).\n     *\n     * Counterpart to Solidity's `int152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     */\n    function toInt152(int256 value) internal pure returns (int152 downcasted) {\n        downcasted = int152(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(152, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int144 from int256, reverting on\n     * overflow (when the input is less than smallest int144 or\n     * greater than largest int144).\n     *\n     * Counterpart to Solidity's `int144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     */\n    function toInt144(int256 value) internal pure returns (int144 downcasted) {\n        downcasted = int144(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(144, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int136 from int256, reverting on\n     * overflow (when the input is less than smallest int136 or\n     * greater than largest int136).\n     *\n     * Counterpart to Solidity's `int136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     */\n    function toInt136(int256 value) internal pure returns (int136 downcasted) {\n        downcasted = int136(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(136, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int128 from int256, reverting on\n     * overflow (when the input is less than smallest int128 or\n     * greater than largest int128).\n     *\n     * Counterpart to Solidity's `int128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     */\n    function toInt128(int256 value) internal pure returns (int128 downcasted) {\n        downcasted = int128(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(128, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int120 from int256, reverting on\n     * overflow (when the input is less than smallest int120 or\n     * greater than largest int120).\n     *\n     * Counterpart to Solidity's `int120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     */\n    function toInt120(int256 value) internal pure returns (int120 downcasted) {\n        downcasted = int120(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(120, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int112 from int256, reverting on\n     * overflow (when the input is less than smallest int112 or\n     * greater than largest int112).\n     *\n     * Counterpart to Solidity's `int112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     */\n    function toInt112(int256 value) internal pure returns (int112 downcasted) {\n        downcasted = int112(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(112, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int104 from int256, reverting on\n     * overflow (when the input is less than smallest int104 or\n     * greater than largest int104).\n     *\n     * Counterpart to Solidity's `int104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     */\n    function toInt104(int256 value) internal pure returns (int104 downcasted) {\n        downcasted = int104(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(104, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int96 from int256, reverting on\n     * overflow (when the input is less than smallest int96 or\n     * greater than largest int96).\n     *\n     * Counterpart to Solidity's `int96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     */\n    function toInt96(int256 value) internal pure returns (int96 downcasted) {\n        downcasted = int96(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(96, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int88 from int256, reverting on\n     * overflow (when the input is less than smallest int88 or\n     * greater than largest int88).\n     *\n     * Counterpart to Solidity's `int88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     */\n    function toInt88(int256 value) internal pure returns (int88 downcasted) {\n        downcasted = int88(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(88, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int80 from int256, reverting on\n     * overflow (when the input is less than smallest int80 or\n     * greater than largest int80).\n     *\n     * Counterpart to Solidity's `int80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     */\n    function toInt80(int256 value) internal pure returns (int80 downcasted) {\n        downcasted = int80(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(80, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int72 from int256, reverting on\n     * overflow (when the input is less than smallest int72 or\n     * greater than largest int72).\n     *\n     * Counterpart to Solidity's `int72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     */\n    function toInt72(int256 value) internal pure returns (int72 downcasted) {\n        downcasted = int72(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(72, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int64 from int256, reverting on\n     * overflow (when the input is less than smallest int64 or\n     * greater than largest int64).\n     *\n     * Counterpart to Solidity's `int64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     */\n    function toInt64(int256 value) internal pure returns (int64 downcasted) {\n        downcasted = int64(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(64, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int56 from int256, reverting on\n     * overflow (when the input is less than smallest int56 or\n     * greater than largest int56).\n     *\n     * Counterpart to Solidity's `int56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     */\n    function toInt56(int256 value) internal pure returns (int56 downcasted) {\n        downcasted = int56(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(56, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int48 from int256, reverting on\n     * overflow (when the input is less than smallest int48 or\n     * greater than largest int48).\n     *\n     * Counterpart to Solidity's `int48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     */\n    function toInt48(int256 value) internal pure returns (int48 downcasted) {\n        downcasted = int48(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(48, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int40 from int256, reverting on\n     * overflow (when the input is less than smallest int40 or\n     * greater than largest int40).\n     *\n     * Counterpart to Solidity's `int40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     */\n    function toInt40(int256 value) internal pure returns (int40 downcasted) {\n        downcasted = int40(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(40, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int32 from int256, reverting on\n     * overflow (when the input is less than smallest int32 or\n     * greater than largest int32).\n     *\n     * Counterpart to Solidity's `int32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     */\n    function toInt32(int256 value) internal pure returns (int32 downcasted) {\n        downcasted = int32(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(32, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int24 from int256, reverting on\n     * overflow (when the input is less than smallest int24 or\n     * greater than largest int24).\n     *\n     * Counterpart to Solidity's `int24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     */\n    function toInt24(int256 value) internal pure returns (int24 downcasted) {\n        downcasted = int24(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(24, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int16 from int256, reverting on\n     * overflow (when the input is less than smallest int16 or\n     * greater than largest int16).\n     *\n     * Counterpart to Solidity's `int16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     */\n    function toInt16(int256 value) internal pure returns (int16 downcasted) {\n        downcasted = int16(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(16, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int8 from int256, reverting on\n     * overflow (when the input is less than smallest int8 or\n     * greater than largest int8).\n     *\n     * Counterpart to Solidity's `int8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     */\n    function toInt8(int256 value) internal pure returns (int8 downcasted) {\n        downcasted = int8(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(8, value);\n        }\n    }\n\n    /**\n     * @dev Converts an unsigned uint256 into a signed int256.\n     *\n     * Requirements:\n     *\n     * - input must be less than or equal to maxInt256.\n     */\n    function toInt256(uint256 value) internal pure returns (int256) {\n        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n        if (value > uint256(type(int256).max)) {\n            revert SafeCastOverflowedUintToInt(value);\n        }\n        return int256(value);\n    }\n}\n"
      },
      "src/Whitelist.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.21;\n\nimport { Ownable2Step } from \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { MerkleProof } from \"@openzeppelin/contracts/utils/cryptography/MerkleProof.sol\";\n\n/**\n * @notice An external Whitelist module that Ion's system-wide contracts can use\n * to verify that a user is permitted to borrow or lend.\n *\n * A merkle whitelist is used to allow for a large number of addresses to be\n * whitelisted without consuming infordinate amounts of gas for the updates.\n *\n * There is also a protocol whitelist that can be used to allow for a protocol\n * controlled address to bypass the merkle proof check. These\n * protocol-controlled contract are expected to perform whitelist checks\n * themsleves on their own entrypoints.\n *\n * @dev The full merkle tree is stored off-chain and only the root is stored\n * on-chain.\n *\n * @custom:security-contact security@molecularlabs.io\n */\ncontract Whitelist is Ownable2Step {\n    mapping(address protocolControlledAddress => bool) public protocolWhitelist; // peripheral addresses that can bypass\n        // the merkle proof check\n\n    mapping(uint8 ilkIndex => bytes32) public borrowersRoot; // root of the merkle tree of borrowers for each ilk\n\n    bytes32 public lendersRoot; // root of the merkle tree of lenders for each ilk\n\n    // --- Errors ---\n\n    error NotWhitelistedBorrower(uint8 ilkIndex, address addr);\n    error NotWhitelistedLender(address addr);\n\n    /**\n     * @notice Creates a new `Whitelist` instance.\n     * @param _borrowersRoots List borrower merkle roots for each ilk.\n     * @param _lendersRoot The lender merkle root.\n     */\n    constructor(bytes32[] memory _borrowersRoots, bytes32 _lendersRoot) Ownable(msg.sender) {\n        for (uint8 i = 0; i < _borrowersRoots.length; i++) {\n            borrowersRoot[i] = _borrowersRoots[i];\n        }\n        lendersRoot = _lendersRoot;\n    }\n\n    /**\n     * @notice Updates the borrower merkle root for a specific ilk.\n     * @param ilkIndex of the ilk.\n     * @param _borrowersRoot The new borrower merkle root.\n     */\n    function updateBorrowersRoot(uint8 ilkIndex, bytes32 _borrowersRoot) external onlyOwner {\n        borrowersRoot[ilkIndex] = _borrowersRoot;\n    }\n\n    /**\n     * @notice Updates the lender merkle root.\n     * @param _lendersRoot The new lender merkle root.\n     */\n    function updateLendersRoot(bytes32 _lendersRoot) external onlyOwner {\n        lendersRoot = _lendersRoot;\n    }\n\n    /**\n     * @notice Approves a protocol controlled address to bypass the merkle proof check.\n     * @param addr The address to approve.\n     */\n    function approveProtocolWhitelist(address addr) external onlyOwner {\n        protocolWhitelist[addr] = true;\n    }\n\n    /**\n     * @notice Revokes a protocol controlled address to bypass the merkle proof check.\n     * @param addr The address to revoke approval for.\n     */\n    function revokeProtocolWhitelist(address addr) external onlyOwner {\n        protocolWhitelist[addr] = false;\n    }\n\n    /**\n     * @notice Called by external modifiers to prove inclusion as a borrower.\n     * @dev If the root is just zero, then the whitelist is effectively turned\n     * off as every address will be allowed.\n     * @return True if the addr is part of the borrower whitelist or the\n     * protocol whitelist. False otherwise.\n     */\n    function isWhitelistedBorrower(\n        uint8 ilkIndex,\n        address poolCaller,\n        address addr,\n        bytes32[] calldata proof\n    )\n        external\n        view\n        returns (bool)\n    {\n        if (protocolWhitelist[poolCaller]) return true;\n        bytes32 root = borrowersRoot[ilkIndex];\n        if (root == 0) return true;\n        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr))));\n        if (MerkleProof.verify(proof, root, leaf)) {\n            return true;\n        } else {\n            revert NotWhitelistedBorrower(ilkIndex, addr);\n        }\n    }\n\n    /**\n     * @notice Called by external modifiers to prove inclusion as a lender.\n     * @dev If the root is just zero, then the whitelist is effectively turned\n     * off as every address will be allowed.\n     * @return True if the addr is part of the lender whitelist or the protocol\n     * whitelist. False otherwise.\n     */\n    function isWhitelistedLender(\n        address poolCaller,\n        address addr,\n        bytes32[] calldata proof\n    )\n        external\n        view\n        returns (bool)\n    {\n        if (protocolWhitelist[poolCaller]) return true;\n        bytes32 root = lendersRoot;\n        if (root == bytes32(0)) return true;\n        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr))));\n        if (MerkleProof.verify(proof, root, leaf)) {\n            return true;\n        } else {\n            revert NotWhitelistedLender(addr);\n        }\n    }\n}\n"
      },
      "src/oracles/spot/SpotOracle.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.21;\n\nimport { ReserveOracle } from \"../../oracles/reserve/ReserveOracle.sol\";\nimport { WadRayMath, RAY } from \"../../libraries/math/WadRayMath.sol\";\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\n/**\n * @notice The `SpotOracle` is supposed to reflect the current market price of a\n * collateral asset. It is used by `IonPool` to determine the health factor of a\n * vault as a user is opening or closing a position.\n *\n * NOTE: The price data provided by this contract is not used by the liquidation\n * module at all.\n *\n * The spot price will also always be bounded by the collateral's corresponding\n * reserve oracle price to ensure that a user can never open position that is\n * directly liquidatable.\n *\n * @custom:security-contact security@molecularlabs.io\n */\nabstract contract SpotOracle {\n    using WadRayMath for uint256;\n\n    uint256 public immutable LTV; // max LTV for a position (below liquidation threshold) [ray]\n    ReserveOracle public immutable RESERVE_ORACLE;\n\n    // --- Errors ---\n    error InvalidLtv(uint256 ltv);\n    error InvalidReserveOracle();\n\n    /**\n     * @notice Creates a new `SpotOracle` instance.\n     * @param _ltv Loan to value ratio for the collateral.\n     * @param _reserveOracle Address for the associated reserve oracle.\n     */\n    constructor(uint256 _ltv, address _reserveOracle) {\n        if (_ltv > RAY) {\n            revert InvalidLtv(_ltv);\n        }\n        if (address(_reserveOracle) == address(0)) {\n            revert InvalidReserveOracle();\n        }\n        LTV = _ltv;\n        RESERVE_ORACLE = ReserveOracle(_reserveOracle);\n    }\n\n    /**\n     * @notice Gets the price of the collateral asset in ETH.\n     * @dev Overridden by collateral specific spot oracle contracts.\n     * @return price of the asset in ETH. [WAD]\n     */\n    function getPrice() public view virtual returns (uint256 price);\n\n    // @dev Gets the market price multiplied by the LTV.\n    // @return spot value of the asset in ETH [ray]\n\n    /**\n     * @notice Gets the risk-adjusted market price.\n     * @return spot The risk-adjusted market price.\n     */\n    function getSpot() external view returns (uint256 spot) {\n        uint256 price = getPrice(); // must be [wad]\n        uint256 exchangeRate = RESERVE_ORACLE.currentExchangeRate();\n\n        // Min the price with reserve oracle before multiplying by ltv\n        uint256 min = Math.min(price, exchangeRate); // [wad]\n\n        spot = LTV.wadMulDown(min); // [ray] * [wad] / [wad] = [ray]\n    }\n}\n"
      },
      "src/reward/RewardModule.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-or-later\npragma solidity 0.8.21;\n\nimport { WadRayMath, RAY } from \"../libraries/math/WadRayMath.sol\";\n\nimport { ContextUpgradeable } from \"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\";\nimport { AccessControlDefaultAdminRulesUpgradeable } from\n    \"@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n/**\n * @title RewardModule\n * @notice The supply-side reward accounting portion of the protocol. A lender's\n * balance is measured in two parts: a static balance and a dynamic \"supply\n * factor\". Their true balance is the product of the two values. The dynamic\n * portion is then able to be used to distribute interest accrued to the lender.\n *\n * @custom:security-contact security@molecularlabs.io\n */\nabstract contract RewardModule is ContextUpgradeable, AccessControlDefaultAdminRulesUpgradeable {\n    using WadRayMath for uint256;\n    using SafeERC20 for IERC20;\n\n    /**\n     * @dev Cannot burn amount whose normalized value is less than zero.\n     */\n    error InvalidBurnAmount();\n\n    /**\n     * @dev Cannot mint amount whose normalized value is less than zero.\n     */\n    error InvalidMintAmount();\n\n    error InvalidUnderlyingAddress();\n    error InvalidTreasuryAddress();\n\n    /**\n     * @dev Indicates a failure with the token `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     */\n    error InvalidSender(address sender);\n\n    /**\n     * @dev Indicates a failure with the token `receiver`. Used in transfers.\n     * @param receiver Address to which tokens are being transferred.\n     */\n    error InvalidReceiver(address receiver);\n\n    /**\n     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n     * @param account Address whose token balance is insufficient.\n     * @param balance Current balance for the interacting account.\n     * @param needed Minimum amount required to perform a transfer.\n     */\n    error InsufficientBalance(address account, uint256 balance, uint256 needed);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    event MintToTreasury(address indexed treasury, uint256 amount, uint256 supplyFactor);\n\n    event TreasuryUpdate(address treasury);\n\n    /// @custom:storage-location erc7201:ion.storage.RewardModule\n    struct RewardModuleStorage {\n        IERC20 underlying;\n        uint8 decimals;\n        // A user's true balance at any point will be the value in this mapping times the supplyFactor\n        string name;\n        string symbol;\n        address treasury;\n        uint256 normalizedTotalSupply; // [WAD]\n        uint256 supplyFactor; // [RAY]\n        mapping(address account => uint256) _normalizedBalances; // [WAD]\n    }\n\n    bytes32 public constant ION = keccak256(\"ION\");\n\n    // keccak256(abi.encode(uint256(keccak256(\"ion.storage.RewardModule\")) - 1)) & ~bytes32(uint256(0xff))\n    // solhint-disable-next-line\n    bytes32 private constant RewardModuleStorageLocation =\n        0xdb3a0d63a7808d7d0422c40bb62354f42bff7602a547c329c1453dbcbeef4900;\n\n    function _getRewardModuleStorage() private pure returns (RewardModuleStorage storage $) {\n        assembly {\n            $.slot := RewardModuleStorageLocation\n        }\n    }\n\n    function _initialize(\n        address _underlying,\n        address _treasury,\n        uint8 decimals_,\n        string memory name_,\n        string memory symbol_\n    )\n        internal\n        onlyInitializing\n    {\n        if (_underlying == address(0)) revert InvalidUnderlyingAddress();\n        if (_treasury == address(0)) revert InvalidTreasuryAddress();\n\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        $.underlying = IERC20(_underlying);\n        $.treasury = _treasury;\n        $.decimals = decimals_;\n        $.name = name_;\n        $.symbol = symbol_;\n        $.supplyFactor = RAY;\n\n        emit TreasuryUpdate(_treasury);\n    }\n\n    /**\n     *\n     * @param user to burn tokens from\n     * @param receiverOfUnderlying to send underlying tokens to\n     * @param amount to burn\n     */\n    function _burn(address user, address receiverOfUnderlying, uint256 amount) internal returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        uint256 _supplyFactor = $.supplyFactor;\n        uint256 amountScaled = amount.rayDivUp(_supplyFactor);\n\n        if (amountScaled == 0) revert InvalidBurnAmount();\n        _burnNormalized(user, amountScaled);\n\n        $.underlying.safeTransfer(receiverOfUnderlying, amount);\n\n        emit Transfer(user, address(0), amount);\n\n        return _supplyFactor;\n    }\n\n    /**\n     *\n     * @param account to decrease balance of\n     * @param amount of normalized tokens to burn\n     */\n    function _burnNormalized(address account, uint256 amount) private {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        if (account == address(0)) revert InvalidSender(address(0));\n\n        uint256 oldAccountBalance = $._normalizedBalances[account];\n        if (oldAccountBalance < amount) revert InsufficientBalance(account, oldAccountBalance, amount);\n        // Underflow impossible\n        unchecked {\n            $._normalizedBalances[account] = oldAccountBalance - amount;\n        }\n\n        $.normalizedTotalSupply -= amount;\n    }\n\n    /**\n     *\n     * @param user to mint tokens to\n     * @param senderOfUnderlying address to transfer underlying tokens from\n     * @param amount of reward tokens to mint\n     */\n    function _mint(address user, address senderOfUnderlying, uint256 amount) internal returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        uint256 _supplyFactor = $.supplyFactor;\n        uint256 amountScaled = amount.rayDivDown(_supplyFactor); // [WAD] * [RAY] / [RAY] = [WAD]\n        if (amountScaled == 0) revert InvalidMintAmount();\n        _mintNormalized(user, amountScaled);\n\n        $.underlying.safeTransferFrom(senderOfUnderlying, address(this), amount);\n\n        emit Transfer(address(0), user, amount);\n\n        return _supplyFactor;\n    }\n\n    /**\n     *\n     * @param account to increase balance of\n     * @param amount of normalized tokens to mint\n     */\n    function _mintNormalized(address account, uint256 amount) private {\n        if (account == address(0)) revert InvalidReceiver(address(0));\n\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        $.normalizedTotalSupply += amount;\n\n        $._normalizedBalances[account] += amount;\n    }\n\n    /**\n     * @dev This function does not perform any rounding checks.\n     * @param amount of tokens to mint to treasury\n     */\n    function _mintToTreasury(uint256 amount) internal {\n        if (amount == 0) return;\n\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        uint256 _supplyFactor = $.supplyFactor;\n        address _treasury = $.treasury;\n\n        // Compared to the normal mint, we don't check for rounding errors. The\n        // amount to mint can easily be very small since it is a fraction of the\n        // interest accrued. In that case, the treasury will experience a (very\n        // small) loss, but it won't cause potentially valid transactions to\n        // fail.\n        _mintNormalized(_treasury, amount.rayDivDown(_supplyFactor));\n\n        emit Transfer(address(0), _treasury, amount);\n        emit MintToTreasury(_treasury, amount, _supplyFactor);\n    }\n\n    function _setSupplyFactor(uint256 newSupplyFactor) internal {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        $.supplyFactor = newSupplyFactor;\n    }\n\n    /**\n     * @dev Updates the treasury address\n     * @param newTreasury address of new treasury\n     */\n    function updateTreasury(address newTreasury) external onlyRole(ION) {\n        if (newTreasury == address(0)) revert InvalidTreasuryAddress();\n\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        $.treasury = newTreasury;\n\n        emit TreasuryUpdate(newTreasury);\n    }\n\n    // --- Getters ---\n\n    /**\n     * @dev Address of underlying asset\n     */\n    function underlying() public view returns (IERC20) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.underlying;\n    }\n\n    /**\n     * @dev Decimals of the position asset\n     */\n    function decimals() public view returns (uint8) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.decimals;\n    }\n\n    /**\n     * @dev Current token balance\n     * @param user to get balance of\n     */\n    function balanceOf(address user) public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        (uint256 totalSupplyFactorIncrease,,,,) = calculateRewardAndDebtDistribution();\n\n        return $._normalizedBalances[user].rayMulDown($.supplyFactor + totalSupplyFactorIncrease);\n    }\n\n    /**\n     * @dev Accounting is done in normalized balances\n     * @param user to get normalized balance of\n     */\n    function normalizedBalanceOf(address user) external view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $._normalizedBalances[user];\n    }\n\n    /**\n     * @dev Name of the position asset\n     */\n    function name() public view returns (string memory) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.name;\n    }\n\n    /**\n     * @dev Symbol of the position asset\n     */\n    function symbol() public view returns (string memory) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.symbol;\n    }\n\n    /**\n     * @dev Current treasury address\n     */\n    function treasury() public view returns (address) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.treasury;\n    }\n\n    function totalSupplyUnaccrued() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        uint256 _normalizedTotalSupply = $.normalizedTotalSupply;\n\n        if (_normalizedTotalSupply == 0) {\n            return 0;\n        }\n\n        return _normalizedTotalSupply.rayMulDown($.supplyFactor);\n    }\n\n    /**\n     * @dev Current total supply\n     */\n    function totalSupply() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        uint256 _normalizedTotalSupply = $.normalizedTotalSupply;\n\n        if (_normalizedTotalSupply == 0) {\n            return 0;\n        }\n\n        (uint256 totalSupplyFactorIncrease,,,,) = calculateRewardAndDebtDistribution();\n\n        return _normalizedTotalSupply.rayMulDown($.supplyFactor + totalSupplyFactorIncrease);\n    }\n\n    function normalizedTotalSupplyUnaccrued() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.normalizedTotalSupply;\n    }\n\n    /**\n     * @dev Current normalized total supply\n     */\n    function normalizedTotalSupply() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        (uint256 totalSupplyFactorIncrease, uint256 totalTreasuryMintAmount,,,) = calculateRewardAndDebtDistribution();\n\n        uint256 normalizedTreasuryMintAmount =\n            totalTreasuryMintAmount.rayDivDown($.supplyFactor + totalSupplyFactorIncrease);\n\n        return $.normalizedTotalSupply + normalizedTreasuryMintAmount;\n    }\n\n    function supplyFactorUnaccrued() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n        return $.supplyFactor;\n    }\n\n    /**\n     * @dev Current supply factor\n     */\n    function supplyFactor() public view returns (uint256) {\n        RewardModuleStorage storage $ = _getRewardModuleStorage();\n\n        (uint256 totalSupplyFactorIncrease,,,,) = calculateRewardAndDebtDistribution();\n\n        return $.supplyFactor + totalSupplyFactorIncrease;\n    }\n\n    function calculateRewardAndDebtDistribution()\n        public\n        view\n        virtual\n        returns (\n            uint256 totalSupplyFactorIncrease,\n            uint256 totalTreasuryMintAmount,\n            uint104[] memory rateIncreases,\n            uint256 totalDebtIncrease,\n            uint48[] memory timestampIncreases\n        );\n}\n"
      },
      "src/InterestRate.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IYieldOracle } from \"./interfaces/IYieldOracle.sol\";\nimport { WadRayMath } from \"./libraries/math/WadRayMath.sol\";\n\n// forgefmt: disable-start\n\nstruct IlkData {\n    // Word 1\n    uint96 adjustedProfitMargin; // 27 decimals\n    uint96 minimumKinkRate; // 27 decimals\n\n    // Word 2\n    uint16 reserveFactor; // 4 decimals\n    uint96 adjustedBaseRate; // 27 decimals\n    uint96 minimumBaseRate; // 27 decimals\n    uint16 optimalUtilizationRate; // 4 decimals\n    uint16 distributionFactor; // 4 decimals\n\n    // Word 3\n    uint96 adjustedAboveKinkSlope; // 27 decimals\n    uint96 minimumAboveKinkSlope; // 27 decimals\n}\n\n// Word 1\n//\n//                                                256  240   216   192                     96                      0\n//                                                 |    |     |     |     min_kink_rate     |   adj_profit_margin  |\n//\nuint256 constant ADJUSTED_PROFIT_MARGIN_MASK =    0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; \nuint256 constant MINIMUM_KINK_RATE_MASK =         0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000;\n\n// Word 2\n//\n//                                                256  240 224 208                     112                     16   0\n//                                                 | __ |   |   |     min_base_rate     |     adj_base_rate     |   |\n//                                                        ^   ^                                                   ^\n//                                                        ^  opt_util                                 reserve_factor\n//                                       distribution_factor\n\nuint256 constant RESERVE_FACTOR_MASK =            0x000000000000000000000000000000000000000000000000000000000000FFFF;\nuint256 constant ADJUSTED_BASE_RATE_MASK =        0x000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000;\nuint256 constant MINIMUM_BASE_RATE_MASK =         0x000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000;\nuint256 constant OPTIMAL_UTILIZATION_MASK =       0x00000000FFFF0000000000000000000000000000000000000000000000000000;\nuint256 constant DISTRIBUTION_FACTOR_MASK =       0x0000FFFF00000000000000000000000000000000000000000000000000000000;\n\n// Word 3\n//                                                256  240   216   192                     96                      0\n//                                                 |    |     |     |  min_above_kink_slope | adj_above_kink_slope |\n//\nuint256 constant ADJUSTED_ABOVE_KINK_SLOPE_MASK =  0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;\nuint256 constant MINIMUM_ABOVE_KINK_SLOPE_MASK =   0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000; \n\n// forgefmt: disable-end\n\n// Word 1\nuint8 constant ADJUSTED_PROFIT_MARGIN_SHIFT = 0;\nuint8 constant MINIMUM_KINK_RATE_SHIFT = 96;\n\n// Word 2\nuint8 constant RESERVE_FACTOR_SHIFT = 0;\nuint8 constant ADJUSTED_BASE_RATE_SHIFT = 16;\nuint8 constant MINIMUM_BASE_RATE_SHIFT = 16 + 96;\nuint8 constant OPTIMAL_UTILIZATION_SHIFT = 16 + 96 + 96;\nuint8 constant DISTRIBUTION_FACTOR_SHIFT = 16 + 96 + 96 + 16;\n\n// Word 3\nuint8 constant ADJUSTED_ABOVE_KINK_SLOPE_SHIFT = 0;\nuint8 constant MINIMUM_ABOVE_KINK_SLOPE_SHIFT = 96;\n\nuint48 constant SECONDS_IN_A_YEAR = 31_536_000;\n\n/**\n * @notice An external contract that provides the APY for each collateral type.\n * A modular design here allows for updating of the parameters at a later date\n * without upgrading the core protocol.\n *\n * @dev Each collateral has its own interest rate model, and every operation on\n * the `IonPool` (lend, withdraw, borrow, repay) will alter the interest rate\n * for all collaterals. Therefore, before every operation, the previous interest\n * rate must be accrued. Ion determines the interest rate for each collateral\n * based on various collateral-specific parameters which must be stored\n * on-chain. However, to iterate through all these parameters as contract\n * storage on every operation introduces an immense gas overhead, especially as\n * more collaterals are listed on Ion. Therefore, this contract is heavily\n * optimized to reduce storage reads at the unfortunate cost of code-complexity.\n *\n * @custom:security-contact security@molecularlabs.io\n */\ncontract InterestRate {\n    using WadRayMath for *;\n\n    error CollateralIndexOutOfBounds();\n    error DistributionFactorsDoNotSumToOne(uint256 sum);\n    error TotalDebtsLength(uint256 COLLATERAL_COUNT, uint256 totalIlkDebtsLength);\n\n    error InvalidMinimumKinkRate(uint256 minimumKinkRate, uint256 minimumBaseRate);\n    error InvalidIlkDataListLength(uint256 length);\n    error InvalidOptimalUtilizationRate(uint256 optimalUtilizationRate);\n    error InvalidReserveFactor(uint256 reserveFactor);\n    error InvalidYieldOracleAddress();\n\n    uint256 private constant MAX_ILKS = 8;\n\n    /**\n     * @dev Packed collateral configs\n     */\n    uint256 private immutable ILKCONFIG_0A;\n    uint256 private immutable ILKCONFIG_0B;\n    uint256 private immutable ILKCONFIG_0C;\n    uint256 private immutable ILKCONFIG_1A;\n    uint256 private immutable ILKCONFIG_1B;\n    uint256 private immutable ILKCONFIG_1C;\n    uint256 private immutable ILKCONFIG_2A;\n    uint256 private immutable ILKCONFIG_2B;\n    uint256 private immutable ILKCONFIG_2C;\n    uint256 private immutable ILKCONFIG_3A;\n    uint256 private immutable ILKCONFIG_3B;\n    uint256 private immutable ILKCONFIG_3C;\n    uint256 private immutable ILKCONFIG_4A;\n    uint256 private immutable ILKCONFIG_4B;\n    uint256 private immutable ILKCONFIG_4C;\n    uint256 private immutable ILKCONFIG_5A;\n    uint256 private immutable ILKCONFIG_5B;\n    uint256 private immutable ILKCONFIG_5C;\n    uint256 private immutable ILKCONFIG_6A;\n    uint256 private immutable ILKCONFIG_6B;\n    uint256 private immutable ILKCONFIG_6C;\n    uint256 private immutable ILKCONFIG_7A;\n    uint256 private immutable ILKCONFIG_7B;\n    uint256 private immutable ILKCONFIG_7C;\n\n    uint256 public immutable COLLATERAL_COUNT;\n    IYieldOracle public immutable YIELD_ORACLE;\n\n    /**\n     * @notice Creates a new `InterestRate` instance.\n     * @param ilkDataList List of ilk configs.\n     * @param _yieldOracle Address of the Yield oracle.\n     */\n    constructor(IlkData[] memory ilkDataList, IYieldOracle _yieldOracle) {\n        if (address(_yieldOracle) == address(0)) revert InvalidYieldOracleAddress();\n        if (ilkDataList.length > MAX_ILKS) revert InvalidIlkDataListLength(ilkDataList.length);\n\n        COLLATERAL_COUNT = ilkDataList.length;\n        YIELD_ORACLE = _yieldOracle;\n\n        uint256 distributionFactorSum = 0;\n        for (uint256 i = 0; i < COLLATERAL_COUNT;) {\n            distributionFactorSum += ilkDataList[i].distributionFactor;\n\n            if (ilkDataList[i].minimumKinkRate < ilkDataList[i].minimumBaseRate) {\n                revert InvalidMinimumKinkRate(ilkDataList[i].minimumKinkRate, ilkDataList[i].minimumBaseRate);\n            }\n            if (ilkDataList[i].optimalUtilizationRate == 0) {\n                revert InvalidOptimalUtilizationRate(ilkDataList[i].optimalUtilizationRate);\n            }\n            if (ilkDataList[i].reserveFactor > 1e4) {\n                revert InvalidReserveFactor(ilkDataList[i].reserveFactor);\n            }\n\n            // forgefmt: disable-next-line\n            unchecked { ++i; }\n        }\n\n        if (distributionFactorSum != 1e4) revert DistributionFactorsDoNotSumToOne(distributionFactorSum);\n\n        (ILKCONFIG_0A, ILKCONFIG_0B, ILKCONFIG_0C) = _packCollateralConfig(ilkDataList, 0);\n        (ILKCONFIG_1A, ILKCONFIG_1B, ILKCONFIG_1C) = _packCollateralConfig(ilkDataList, 1);\n        (ILKCONFIG_2A, ILKCONFIG_2B, ILKCONFIG_2C) = _packCollateralConfig(ilkDataList, 2);\n        (ILKCONFIG_3A, ILKCONFIG_3B, ILKCONFIG_3C) = _packCollateralConfig(ilkDataList, 3);\n        (ILKCONFIG_4A, ILKCONFIG_4B, ILKCONFIG_4C) = _packCollateralConfig(ilkDataList, 4);\n        (ILKCONFIG_5A, ILKCONFIG_5B, ILKCONFIG_5C) = _packCollateralConfig(ilkDataList, 5);\n        (ILKCONFIG_6A, ILKCONFIG_6B, ILKCONFIG_6C) = _packCollateralConfig(ilkDataList, 6);\n        (ILKCONFIG_7A, ILKCONFIG_7B, ILKCONFIG_7C) = _packCollateralConfig(ilkDataList, 7);\n    }\n\n    /**\n     * @notice Helper function to pack the collateral configs into 3 words. This\n     * function is only called during construction.\n     * @param ilkDataList The list of ilk configs.\n     * @param index The ilkIndex to pack.\n     * @return packedConfig_a\n     * @return packedConfig_b\n     * @return packedConfig_c\n     */\n    function _packCollateralConfig(\n        IlkData[] memory ilkDataList,\n        uint256 index\n    )\n        private\n        view\n        returns (uint256 packedConfig_a, uint256 packedConfig_b, uint256 packedConfig_c)\n    {\n        if (index >= COLLATERAL_COUNT) return (0, 0, 0);\n\n        IlkData memory ilkData = ilkDataList[index];\n\n        packedConfig_a = (\n            uint256(ilkData.adjustedProfitMargin) << ADJUSTED_PROFIT_MARGIN_SHIFT\n                | uint256(ilkData.minimumKinkRate) << MINIMUM_KINK_RATE_SHIFT\n        );\n\n        packedConfig_b = (\n            uint256(ilkData.reserveFactor) << RESERVE_FACTOR_SHIFT\n                | uint256(ilkData.adjustedBaseRate) << ADJUSTED_BASE_RATE_SHIFT\n                | uint256(ilkData.minimumBaseRate) << MINIMUM_BASE_RATE_SHIFT\n                | uint256(ilkData.optimalUtilizationRate) << OPTIMAL_UTILIZATION_SHIFT\n                | uint256(ilkData.distributionFactor) << DISTRIBUTION_FACTOR_SHIFT\n        );\n\n        packedConfig_c = (\n            uint256(ilkData.adjustedAboveKinkSlope) << ADJUSTED_ABOVE_KINK_SLOPE_SHIFT\n                | uint256(ilkData.minimumAboveKinkSlope) << MINIMUM_ABOVE_KINK_SLOPE_SHIFT\n        );\n    }\n\n    /**\n     * @notice Helper function to unpack the collateral configs from the 3\n     * words.\n     * @param index The ilkIndex to unpack.\n     * @return ilkData The unpacked collateral config.\n     */\n    function unpackCollateralConfig(uint256 index) external view returns (IlkData memory ilkData) {\n        return _unpackCollateralConfig(index);\n    }\n\n    function _unpackCollateralConfig(uint256 index) internal view returns (IlkData memory ilkData) {\n        if (index > COLLATERAL_COUNT - 1) revert CollateralIndexOutOfBounds();\n\n        uint256 packedConfig_a;\n        uint256 packedConfig_b;\n        uint256 packedConfig_c;\n\n        if (index == 0) {\n            packedConfig_a = ILKCONFIG_0A;\n            packedConfig_b = ILKCONFIG_0B;\n            packedConfig_c = ILKCONFIG_0C;\n        } else if (index == 1) {\n            packedConfig_a = ILKCONFIG_1A;\n            packedConfig_b = ILKCONFIG_1B;\n            packedConfig_c = ILKCONFIG_1C;\n        } else if (index == 2) {\n            packedConfig_a = ILKCONFIG_2A;\n            packedConfig_b = ILKCONFIG_2B;\n            packedConfig_c = ILKCONFIG_2C;\n        } else if (index == 3) {\n            packedConfig_a = ILKCONFIG_3A;\n            packedConfig_b = ILKCONFIG_3B;\n            packedConfig_c = ILKCONFIG_3C;\n        } else if (index == 4) {\n            packedConfig_a = ILKCONFIG_4A;\n            packedConfig_b = ILKCONFIG_4B;\n            packedConfig_c = ILKCONFIG_4C;\n        } else if (index == 5) {\n            packedConfig_a = ILKCONFIG_5A;\n            packedConfig_b = ILKCONFIG_5B;\n            packedConfig_c = ILKCONFIG_5C;\n        } else if (index == 6) {\n            packedConfig_a = ILKCONFIG_6A;\n            packedConfig_b = ILKCONFIG_6B;\n            packedConfig_c = ILKCONFIG_6C;\n        } else if (index == 7) {\n            packedConfig_a = ILKCONFIG_7A;\n            packedConfig_b = ILKCONFIG_7B;\n            packedConfig_c = ILKCONFIG_7C;\n        }\n\n        uint96 adjustedProfitMargin =\n            uint96((packedConfig_a & ADJUSTED_PROFIT_MARGIN_MASK) >> ADJUSTED_PROFIT_MARGIN_SHIFT);\n        uint96 minimumKinkRate = uint96((packedConfig_a & MINIMUM_KINK_RATE_MASK) >> MINIMUM_KINK_RATE_SHIFT);\n\n        uint16 reserveFactor = uint16((packedConfig_b & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_SHIFT);\n        uint96 adjustedBaseRate = uint96((packedConfig_b & ADJUSTED_BASE_RATE_MASK) >> ADJUSTED_BASE_RATE_SHIFT);\n        uint96 minimumBaseRate = uint96((packedConfig_b & MINIMUM_BASE_RATE_MASK) >> MINIMUM_BASE_RATE_SHIFT);\n        uint16 optimalUtilizationRate = uint16((packedConfig_b & OPTIMAL_UTILIZATION_MASK) >> OPTIMAL_UTILIZATION_SHIFT);\n        uint16 distributionFactor = uint16((packedConfig_b & DISTRIBUTION_FACTOR_MASK) >> DISTRIBUTION_FACTOR_SHIFT);\n\n        uint96 adjustedAboveKinkSlope =\n            uint96((packedConfig_c & ADJUSTED_ABOVE_KINK_SLOPE_MASK) >> ADJUSTED_ABOVE_KINK_SLOPE_SHIFT);\n        uint96 minimumAboveKinkSlope =\n            uint96((packedConfig_c & MINIMUM_ABOVE_KINK_SLOPE_MASK) >> MINIMUM_ABOVE_KINK_SLOPE_SHIFT);\n\n        ilkData = IlkData({\n            adjustedProfitMargin: adjustedProfitMargin,\n            minimumKinkRate: minimumKinkRate,\n            reserveFactor: reserveFactor,\n            adjustedBaseRate: adjustedBaseRate,\n            minimumBaseRate: minimumBaseRate,\n            optimalUtilizationRate: optimalUtilizationRate,\n            distributionFactor: distributionFactor,\n            adjustedAboveKinkSlope: adjustedAboveKinkSlope,\n            minimumAboveKinkSlope: minimumAboveKinkSlope\n        });\n    }\n\n    /**\n     * @notice Calculates the interest rate for a given collateral.\n     * @param ilkIndex Index of the collateral.\n     * @param totalIlkDebt Total debt of the collateral. [RAD]\n     * @param totalEthSupply Total eth supply of the system. [WAD]\n     * @return The borrow rate for the collateral. [RAY]\n     * @return The reserve factor for the collateral. [RAY]\n     */\n    function calculateInterestRate(\n        uint256 ilkIndex,\n        uint256 totalIlkDebt,\n        uint256 totalEthSupply\n    )\n        external\n        view\n        returns (uint256, uint256)\n    {\n        IlkData memory ilkData = _unpackCollateralConfig(ilkIndex);\n        uint256 optimalUtilizationRateRay = ilkData.optimalUtilizationRate.scaleUpToRay(4);\n        uint256 collateralApyRayInSeconds = YIELD_ORACLE.apys(ilkIndex).scaleUpToRay(8) / SECONDS_IN_A_YEAR;\n\n        uint256 distributionFactor = ilkData.distributionFactor;\n        // The only time the distribution factor will be set to 0 is when a\n        // market has been sunset. In this case, we want to prevent division by\n        // 0, but we also want to prevent the borrow rate from skyrocketing. So\n        // we will return a reasonable borrow rate of kink utilization on the\n        // minimum curve.\n        if (distributionFactor == 0) {\n            return (ilkData.minimumKinkRate, ilkData.reserveFactor.scaleUpToRay(4));\n        }\n        // [RAD] / [WAD] = [RAY]\n        uint256 utilizationRate =\n            totalEthSupply == 0 ? 0 : totalIlkDebt / (totalEthSupply.wadMulDown(distributionFactor.scaleUpToWad(4)));\n\n        // Avoid stack too deep\n        uint256 adjustedBelowKinkSlope;\n        {\n            uint256 slopeNumerator;\n            unchecked {\n                slopeNumerator = collateralApyRayInSeconds - ilkData.adjustedProfitMargin - ilkData.adjustedBaseRate;\n            }\n\n            // Underflow occurred\n            // If underflow occurred, then the Apy was too low or the profitMargin was too high and\n            // we would want to switch to minimum borrow rate. Set slopeNumerator to zero such\n            // that adjusted borrow rate is below the minimum borrow rate.\n            if (slopeNumerator > collateralApyRayInSeconds) {\n                slopeNumerator = 0;\n            }\n\n            adjustedBelowKinkSlope = slopeNumerator.rayDivDown(optimalUtilizationRateRay);\n        }\n\n        uint256 minimumBelowKinkSlope =\n            (ilkData.minimumKinkRate - ilkData.minimumBaseRate).rayDivDown(optimalUtilizationRateRay);\n\n        // Below kink\n        if (utilizationRate < optimalUtilizationRateRay) {\n            uint256 adjustedBorrowRate = adjustedBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.adjustedBaseRate;\n            uint256 minimumBorrowRate = minimumBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.minimumBaseRate;\n\n            if (adjustedBorrowRate < minimumBorrowRate) {\n                return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));\n            } else {\n                return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));\n            }\n        }\n        // Above kink\n        else {\n            // For the above kink calculation, we will use the below kink slope\n            // for all utilization up until the kink. From that point on we will\n            // use the above kink slope.\n            uint256 excessUtil = utilizationRate - optimalUtilizationRateRay;\n\n            uint256 adjustedNormalRate =\n                adjustedBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.adjustedBaseRate;\n            uint256 minimumNormalRate =\n                minimumBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.minimumBaseRate;\n\n            // [WAD] * [RAY] / [WAD] = [RAY]\n            uint256 adjustedBorrowRate = ilkData.adjustedAboveKinkSlope.rayMulDown(excessUtil) + adjustedNormalRate;\n            uint256 minimumBorrowRate = ilkData.minimumAboveKinkSlope.rayMulDown(excessUtil) + minimumNormalRate;\n\n            if (adjustedBorrowRate < minimumBorrowRate) {\n                return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));\n            } else {\n                return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));\n            }\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```solidity\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position is the index of the value in the `values` array plus 1.\n        // Position 0 is used to mean a value is not in the set.\n        mapping(bytes32 value => uint256) _positions;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._positions[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We cache the value's position to prevent multiple reads from the same storage slot\n        uint256 position = set._positions[value];\n\n        if (position != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 valueIndex = position - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (valueIndex != lastIndex) {\n                bytes32 lastValue = set._values[lastIndex];\n\n                // Move the lastValue to the index where the value to delete is\n                set._values[valueIndex] = lastValue;\n                // Update the tracked position of the lastValue (that was just moved)\n                set._positions[lastValue] = position;\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the tracked position for the deleted slot\n            delete set._positions[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._positions[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        bytes32[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    /**\n     * @dev Muldiv operation overflow.\n     */\n    error MathOverflowedMulDiv();\n\n    enum Rounding {\n        Floor, // Toward negative infinity\n        Ceil, // Toward positive infinity\n        Trunc, // Toward zero\n        Expand // Away from zero\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n            // benefit is lost if 'b' is also tested.\n            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n            if (a == 0) return (true, 0);\n            uint256 c = a * b;\n            if (c / a != b) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds towards infinity instead\n     * of rounding towards zero.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (b == 0) {\n            // Guarantee the same behavior as in a regular Solidity division.\n            return a / b;\n        }\n\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n     * denominator == 0.\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n     * Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0 = x * y; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            if (denominator <= prod1) {\n                revert MathOverflowedMulDiv();\n            }\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n            uint256 twos = denominator & (0 - denominator);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n            // works in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n     * towards zero.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n     */\n    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n        return uint8(rounding) % 2 == 1;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable\n    struct PausableStorage {\n        bool _paused;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Pausable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;\n\n    function _getPausableStorage() private pure returns (PausableStorage storage $) {\n        assembly {\n            $.slot := PausableStorageLocation\n        }\n    }\n\n    /**\n     * @dev Emitted when the pause is triggered by `account`.\n     */\n    event Paused(address account);\n\n    /**\n     * @dev Emitted when the pause is lifted by `account`.\n     */\n    event Unpaused(address account);\n\n    /**\n     * @dev The operation failed because the contract is paused.\n     */\n    error EnforcedPause();\n\n    /**\n     * @dev The operation failed because the contract is not paused.\n     */\n    error ExpectedPause();\n\n    /**\n     * @dev Initializes the contract in unpaused state.\n     */\n    function __Pausable_init() internal onlyInitializing {\n        __Pausable_init_unchained();\n    }\n\n    function __Pausable_init_unchained() internal onlyInitializing {\n        PausableStorage storage $ = _getPausableStorage();\n        $._paused = false;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is not paused.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    modifier whenNotPaused() {\n        _requireNotPaused();\n        _;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is paused.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    modifier whenPaused() {\n        _requirePaused();\n        _;\n    }\n\n    /**\n     * @dev Returns true if the contract is paused, and false otherwise.\n     */\n    function paused() public view virtual returns (bool) {\n        PausableStorage storage $ = _getPausableStorage();\n        return $._paused;\n    }\n\n    /**\n     * @dev Throws if the contract is paused.\n     */\n    function _requireNotPaused() internal view virtual {\n        if (paused()) {\n            revert EnforcedPause();\n        }\n    }\n\n    /**\n     * @dev Throws if the contract is not paused.\n     */\n    function _requirePaused() internal view virtual {\n        if (!paused()) {\n            revert ExpectedPause();\n        }\n    }\n\n    /**\n     * @dev Triggers stopped state.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    function _pause() internal virtual whenNotPaused {\n        PausableStorage storage $ = _getPausableStorage();\n        $._paused = true;\n        emit Paused(_msgSender());\n    }\n\n    /**\n     * @dev Returns to normal state.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    function _unpause() internal virtual whenPaused {\n        PausableStorage storage $ = _getPausableStorage();\n        $._paused = false;\n        emit Unpaused(_msgSender());\n    }\n}\n"
      },
      "src/interfaces/IReserveFeed.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.21;\n\n/**\n * @title IReserveFeed interface\n * @notice Interface for the reserve feeds for Ion Protocol.\n *\n */\ninterface IReserveFeed {\n    /**\n     * @dev updates the total reserve of the validator backed asset\n     * @param ilkIndex the ilk index of the asset\n     * @param reserve the total ETH reserve of the asset in wei\n     */\n    function updateExchangeRate(uint8 ilkIndex, uint256 reserve) external;\n\n    /**\n     * @dev returns the total reserve of the validator backed asset\n     * @param ilkIndex the ilk index of the asset\n     * @return the total ETH reserve of the asset in wei\n     */\n    function getExchangeRate(uint8 ilkIndex) external view returns (uint256);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev The ETH balance of the account is not enough to perform the operation.\n     */\n    error AddressInsufficientBalance(address account);\n\n    /**\n     * @dev There's no code at `target` (it is not a contract).\n     */\n    error AddressEmptyCode(address target);\n\n    /**\n     * @dev A call to an address target failed. The target may have reverted.\n     */\n    error FailedInnerCall();\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        if (address(this).balance < amount) {\n            revert AddressInsufficientBalance(address(this));\n        }\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        if (!success) {\n            revert FailedInnerCall();\n        }\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason or custom error, it is bubbled\n     * up by this function (like regular Solidity function calls). However, if\n     * the call reverted with no returned reason, this function reverts with a\n     * {FailedInnerCall} error.\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        if (address(this).balance < value) {\n            revert AddressInsufficientBalance(address(this));\n        }\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an\n     * unsuccessful call.\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata\n    ) internal view returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            // only check if target is a contract if the call was successful and the return data is empty\n            // otherwise we already know that it was a contract\n            if (returndata.length == 0 && target.code.length == 0) {\n                revert AddressEmptyCode(target);\n            }\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n     * revert reason or with a default {FailedInnerCall} error.\n     */\n    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.\n     */\n    function _revert(bytes memory returndata) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert FailedInnerCall();\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.20;\n\nimport {Ownable} from \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is specified at deployment time in the constructor for `Ownable`. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n    address private _pendingOwner;\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        return _pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        _pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        delete _pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() public virtual {\n        address sender = _msgSender();\n        if (pendingOwner() != sender) {\n            revert OwnableUnauthorizedAccount(sender);\n        }\n        _transferOwnership(sender);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the Merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates Merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary MerkleProof {\n    /**\n     *@dev The multiproof provided is not valid.\n     */\n    error MerkleProofInvalidMultiproof();\n\n    /**\n     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree\n     * defined by `root`. For this, a `proof` must be provided, containing\n     * sibling hashes on the branch from the leaf to the root of the tree. Each\n     * pair of leaves and each pair of pre-images are assumed to be sorted.\n     */\n    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {\n        return processProof(proof, leaf) == root;\n    }\n\n    /**\n     * @dev Calldata version of {verify}\n     */\n    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {\n        return processProofCalldata(proof, leaf) == root;\n    }\n\n    /**\n     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n     * hash matches the root of the tree. When processing the proof, the pairs\n     * of leafs & pre-images are assumed to be sorted.\n     */\n    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {\n        bytes32 computedHash = leaf;\n        for (uint256 i = 0; i < proof.length; i++) {\n            computedHash = _hashPair(computedHash, proof[i]);\n        }\n        return computedHash;\n    }\n\n    /**\n     * @dev Calldata version of {processProof}\n     */\n    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {\n        bytes32 computedHash = leaf;\n        for (uint256 i = 0; i < proof.length; i++) {\n            computedHash = _hashPair(computedHash, proof[i]);\n        }\n        return computedHash;\n    }\n\n    /**\n     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by\n     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.\n     *\n     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.\n     */\n    function multiProofVerify(\n        bytes32[] memory proof,\n        bool[] memory proofFlags,\n        bytes32 root,\n        bytes32[] memory leaves\n    ) internal pure returns (bool) {\n        return processMultiProof(proof, proofFlags, leaves) == root;\n    }\n\n    /**\n     * @dev Calldata version of {multiProofVerify}\n     *\n     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.\n     */\n    function multiProofVerifyCalldata(\n        bytes32[] calldata proof,\n        bool[] calldata proofFlags,\n        bytes32 root,\n        bytes32[] memory leaves\n    ) internal pure returns (bool) {\n        return processMultiProofCalldata(proof, proofFlags, leaves) == root;\n    }\n\n    /**\n     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction\n     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another\n     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false\n     * respectively.\n     *\n     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree\n     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the\n     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).\n     */\n    function processMultiProof(\n        bytes32[] memory proof,\n        bool[] memory proofFlags,\n        bytes32[] memory leaves\n    ) internal pure returns (bytes32 merkleRoot) {\n        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by\n        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the\n        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of\n        // the Merkle tree.\n        uint256 leavesLen = leaves.length;\n        uint256 proofLen = proof.length;\n        uint256 totalHashes = proofFlags.length;\n\n        // Check proof validity.\n        if (leavesLen + proofLen != totalHashes + 1) {\n            revert MerkleProofInvalidMultiproof();\n        }\n\n        // The xxxPos values are \"pointers\" to the next value to consume in each array. All accesses are done using\n        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's \"pop\".\n        bytes32[] memory hashes = new bytes32[](totalHashes);\n        uint256 leafPos = 0;\n        uint256 hashPos = 0;\n        uint256 proofPos = 0;\n        // At each step, we compute the next hash using two values:\n        // - a value from the \"main queue\". If not all leaves have been consumed, we get the next leaf, otherwise we\n        //   get the next hash.\n        // - depending on the flag, either another value from the \"main queue\" (merging branches) or an element from the\n        //   `proof` array.\n        for (uint256 i = 0; i < totalHashes; i++) {\n            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];\n            bytes32 b = proofFlags[i]\n                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])\n                : proof[proofPos++];\n            hashes[i] = _hashPair(a, b);\n        }\n\n        if (totalHashes > 0) {\n            if (proofPos != proofLen) {\n                revert MerkleProofInvalidMultiproof();\n            }\n            unchecked {\n                return hashes[totalHashes - 1];\n            }\n        } else if (leavesLen > 0) {\n            return leaves[0];\n        } else {\n            return proof[0];\n        }\n    }\n\n    /**\n     * @dev Calldata version of {processMultiProof}.\n     *\n     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.\n     */\n    function processMultiProofCalldata(\n        bytes32[] calldata proof,\n        bool[] calldata proofFlags,\n        bytes32[] memory leaves\n    ) internal pure returns (bytes32 merkleRoot) {\n        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by\n        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the\n        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of\n        // the Merkle tree.\n        uint256 leavesLen = leaves.length;\n        uint256 proofLen = proof.length;\n        uint256 totalHashes = proofFlags.length;\n\n        // Check proof validity.\n        if (leavesLen + proofLen != totalHashes + 1) {\n            revert MerkleProofInvalidMultiproof();\n        }\n\n        // The xxxPos values are \"pointers\" to the next value to consume in each array. All accesses are done using\n        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's \"pop\".\n        bytes32[] memory hashes = new bytes32[](totalHashes);\n        uint256 leafPos = 0;\n        uint256 hashPos = 0;\n        uint256 proofPos = 0;\n        // At each step, we compute the next hash using two values:\n        // - a value from the \"main queue\". If not all leaves have been consumed, we get the next leaf, otherwise we\n        //   get the next hash.\n        // - depending on the flag, either another value from the \"main queue\" (merging branches) or an element from the\n        //   `proof` array.\n        for (uint256 i = 0; i < totalHashes; i++) {\n            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];\n            bytes32 b = proofFlags[i]\n                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])\n                : proof[proofPos++];\n            hashes[i] = _hashPair(a, b);\n        }\n\n        if (totalHashes > 0) {\n            if (proofPos != proofLen) {\n                revert MerkleProofInvalidMultiproof();\n            }\n            unchecked {\n                return hashes[totalHashes - 1];\n            }\n        } else if (leavesLen > 0) {\n            return leaves[0];\n        } else {\n            return proof[0];\n        }\n    }\n\n    /**\n     * @dev Sorts the pair (a, b) and hashes the result.\n     */\n    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {\n        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);\n    }\n\n    /**\n     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.\n     */\n    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x00, a)\n            mstore(0x20, b)\n            value := keccak256(0x00, 0x40)\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n    function __Context_init() internal onlyInitializing {\n    }\n\n    function __Context_init_unchained() internal onlyInitializing {\n    }\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlDefaultAdminRules.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControlDefaultAdminRules} from \"@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol\";\nimport {AccessControlUpgradeable} from \"../AccessControlUpgradeable.sol\";\nimport {IAccessControl} from \"@openzeppelin/contracts/access/IAccessControl.sol\";\nimport {SafeCast} from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport {IERC5313} from \"@openzeppelin/contracts/interfaces/IERC5313.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows specifying special rules to manage\n * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions\n * over other roles that may potentially have privileged rights in the system.\n *\n * If a specific role doesn't have an admin role assigned, the holder of the\n * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.\n *\n * This contract implements the following risk mitigations on top of {AccessControl}:\n *\n * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.\n * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.\n * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.\n * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.\n * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.\n *\n * Example usage:\n *\n * ```solidity\n * contract MyToken is AccessControlDefaultAdminRules {\n *   constructor() AccessControlDefaultAdminRules(\n *     3 days,\n *     msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder\n *    ) {}\n * }\n * ```\n */\nabstract contract AccessControlDefaultAdminRulesUpgradeable is Initializable, IAccessControlDefaultAdminRules, IERC5313, AccessControlUpgradeable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControlDefaultAdminRules\n    struct AccessControlDefaultAdminRulesStorage {\n        // pending admin pair read/written together frequently\n        address _pendingDefaultAdmin;\n        uint48 _pendingDefaultAdminSchedule; // 0 == unset\n\n        uint48 _currentDelay;\n        address _currentDefaultAdmin;\n\n        // pending delay pair read/written together frequently\n        uint48 _pendingDelay;\n        uint48 _pendingDelaySchedule; // 0 == unset\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.AccessControlDefaultAdminRules\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant AccessControlDefaultAdminRulesStorageLocation = 0xeef3dac4538c82c8ace4063ab0acd2d15cdb5883aa1dff7c2673abb3d8698400;\n\n    function _getAccessControlDefaultAdminRulesStorage() private pure returns (AccessControlDefaultAdminRulesStorage storage $) {\n        assembly {\n            $.slot := AccessControlDefaultAdminRulesStorageLocation\n        }\n    }\n\n    /**\n     * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.\n     */\n    function __AccessControlDefaultAdminRules_init(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {\n        __AccessControlDefaultAdminRules_init_unchained(initialDelay, initialDefaultAdmin);\n    }\n\n    function __AccessControlDefaultAdminRules_init_unchained(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        if (initialDefaultAdmin == address(0)) {\n            revert AccessControlInvalidDefaultAdmin(address(0));\n        }\n        $._currentDelay = initialDelay;\n        _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC5313-owner}.\n     */\n    function owner() public view virtual returns (address) {\n        return defaultAdmin();\n    }\n\n    ///\n    /// Override AccessControl role management\n    ///\n\n    /**\n     * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function grantRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControl) {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super.grantRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function revokeRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControl) {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super.revokeRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-renounceRole}.\n     *\n     * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling\n     * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule\n     * has also passed when calling this function.\n     *\n     * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.\n     *\n     * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},\n     * thereby disabling any functionality that is only available for it, and the possibility of reassigning a\n     * non-administrated role.\n     */\n    function renounceRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControl) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {\n            (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();\n            if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {\n                revert AccessControlEnforcedDefaultAdminDelay(schedule);\n            }\n            delete $._pendingDefaultAdminSchedule;\n        }\n        super.renounceRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_grantRole}.\n     *\n     * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the\n     * role has been previously renounced.\n     *\n     * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`\n     * assignable again. Make sure to guarantee this is the expected behavior in your implementation.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        if (role == DEFAULT_ADMIN_ROLE) {\n            if (defaultAdmin() != address(0)) {\n                revert AccessControlEnforcedDefaultAdminRules();\n            }\n            $._currentDefaultAdmin = account;\n        }\n        return super._grantRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_revokeRole}.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {\n            delete $._currentDefaultAdmin;\n        }\n        return super._revokeRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super._setRoleAdmin(role, adminRole);\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules accessors\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdmin() public view virtual returns (address) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        return $._currentDefaultAdmin;\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        return ($._pendingDefaultAdmin, $._pendingDefaultAdminSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdminDelay() public view virtual returns (uint48) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        uint48 schedule = $._pendingDelaySchedule;\n        return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? $._pendingDelay : $._currentDelay;\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        schedule = $._pendingDelaySchedule;\n        return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? ($._pendingDelay, schedule) : (0, 0);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {\n        return 5 days;\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _beginDefaultAdminTransfer(newAdmin);\n    }\n\n    /**\n     * @dev See {beginDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _beginDefaultAdminTransfer(address newAdmin) internal virtual {\n        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();\n        _setPendingDefaultAdmin(newAdmin, newSchedule);\n        emit DefaultAdminTransferScheduled(newAdmin, newSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _cancelDefaultAdminTransfer();\n    }\n\n    /**\n     * @dev See {cancelDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _cancelDefaultAdminTransfer() internal virtual {\n        _setPendingDefaultAdmin(address(0), 0);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function acceptDefaultAdminTransfer() public virtual {\n        (address newDefaultAdmin, ) = pendingDefaultAdmin();\n        if (_msgSender() != newDefaultAdmin) {\n            // Enforce newDefaultAdmin explicit acceptance.\n            revert AccessControlInvalidDefaultAdmin(_msgSender());\n        }\n        _acceptDefaultAdminTransfer();\n    }\n\n    /**\n     * @dev See {acceptDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _acceptDefaultAdminTransfer() internal virtual {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        (address newAdmin, uint48 schedule) = pendingDefaultAdmin();\n        if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {\n            revert AccessControlEnforcedDefaultAdminDelay(schedule);\n        }\n        _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());\n        _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);\n        delete $._pendingDefaultAdmin;\n        delete $._pendingDefaultAdminSchedule;\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _changeDefaultAdminDelay(newDelay);\n    }\n\n    /**\n     * @dev See {changeDefaultAdminDelay}.\n     *\n     * Internal function without access restriction.\n     */\n    function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {\n        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);\n        _setPendingDelay(newDelay, newSchedule);\n        emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _rollbackDefaultAdminDelay();\n    }\n\n    /**\n     * @dev See {rollbackDefaultAdminDelay}.\n     *\n     * Internal function without access restriction.\n     */\n    function _rollbackDefaultAdminDelay() internal virtual {\n        _setPendingDelay(0, 0);\n    }\n\n    /**\n     * @dev Returns the amount of seconds to wait after the `newDelay` will\n     * become the new {defaultAdminDelay}.\n     *\n     * The value returned guarantees that if the delay is reduced, it will go into effect\n     * after a wait that honors the previously set delay.\n     *\n     * See {defaultAdminDelayIncreaseWait}.\n     */\n    function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {\n        uint48 currentDelay = defaultAdminDelay();\n\n        // When increasing the delay, we schedule the delay change to occur after a period of \"new delay\" has passed, up\n        // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day\n        // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new\n        // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like\n        // using milliseconds instead of seconds.\n        //\n        // When decreasing the delay, we wait the difference between \"current delay\" and \"new delay\". This guarantees\n        // that an admin transfer cannot be made faster than \"current delay\" at the time the delay change is scheduled.\n        // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.\n        return\n            newDelay > currentDelay\n                ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48\n                : currentDelay - newDelay;\n    }\n\n    ///\n    /// Private setters\n    ///\n\n    /**\n     * @dev Setter of the tuple for pending admin and its schedule.\n     *\n     * May emit a DefaultAdminTransferCanceled event.\n     */\n    function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        (, uint48 oldSchedule) = pendingDefaultAdmin();\n\n        $._pendingDefaultAdmin = newAdmin;\n        $._pendingDefaultAdminSchedule = newSchedule;\n\n        // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.\n        if (_isScheduleSet(oldSchedule)) {\n            // Emit for implicit cancellations when another default admin was scheduled.\n            emit DefaultAdminTransferCanceled();\n        }\n    }\n\n    /**\n     * @dev Setter of the tuple for pending delay and its schedule.\n     *\n     * May emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {\n        AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();\n        uint48 oldSchedule = $._pendingDelaySchedule;\n\n        if (_isScheduleSet(oldSchedule)) {\n            if (_hasSchedulePassed(oldSchedule)) {\n                // Materialize a virtual delay\n                $._currentDelay = $._pendingDelay;\n            } else {\n                // Emit for implicit cancellations when another delay was scheduled.\n                emit DefaultAdminDelayChangeCanceled();\n            }\n        }\n\n        $._pendingDelay = newDelay;\n        $._pendingDelaySchedule = newSchedule;\n    }\n\n    ///\n    /// Private helpers\n    ///\n\n    /**\n     * @dev Defines if an `schedule` is considered set. For consistency purposes.\n     */\n    function _isScheduleSet(uint48 schedule) private pure returns (bool) {\n        return schedule != 0;\n    }\n\n    /**\n     * @dev Defines if an `schedule` is considered passed. For consistency purposes.\n     */\n    function _hasSchedulePassed(uint48 schedule) private view returns (bool) {\n        return schedule < block.timestamp;\n    }\n}\n"
      },
      "src/interfaces/IYieldOracle.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IYieldOracle {\n    function apys(uint256 ilkIndex) external view returns (uint32);\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Storage of the initializable contract.\n     *\n     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n     * when using with upgradeable contracts.\n     *\n     * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n     */\n    struct InitializableStorage {\n        /**\n         * @dev Indicates that the contract has been initialized.\n         */\n        uint64 _initialized;\n        /**\n         * @dev Indicates that the contract is in the process of being initialized.\n         */\n        bool _initializing;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n    /**\n     * @dev The contract is already initialized.\n     */\n    error InvalidInitialization();\n\n    /**\n     * @dev The contract is not initializing.\n     */\n    error NotInitializing();\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint64 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n     * production.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        // Cache values to avoid duplicated sloads\n        bool isTopLevelCall = !$._initializing;\n        uint64 initialized = $._initialized;\n\n        // Allowed calls:\n        // - initialSetup: the contract is not in the initializing state and no previous version was\n        //                 initialized\n        // - construction: the contract is initialized at version 1 (no reininitialization) and the\n        //                 current contract is just being deployed\n        bool initialSetup = initialized == 0 && isTopLevelCall;\n        bool construction = initialized == 1 && address(this).code.length == 0;\n\n        if (!initialSetup && !construction) {\n            revert InvalidInitialization();\n        }\n        $._initialized = 1;\n        if (isTopLevelCall) {\n            $._initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            $._initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint64 version) {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing || $._initialized >= version) {\n            revert InvalidInitialization();\n        }\n        $._initialized = version;\n        $._initializing = true;\n        _;\n        $._initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        _checkInitializing();\n        _;\n    }\n\n    /**\n     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n     */\n    function _checkInitializing() internal view virtual {\n        if (!_isInitializing()) {\n            revert NotInitializing();\n        }\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing) {\n            revert InvalidInitialization();\n        }\n        if ($._initialized != type(uint64).max) {\n            $._initialized = type(uint64).max;\n            emit Initialized(type(uint64).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint64) {\n        return _getInitializableStorage()._initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _getInitializableStorage()._initializing;\n    }\n\n    /**\n     * @dev Returns a pointer to the storage namespace.\n     */\n    // solhint-disable-next-line var-name-mixedcase\n    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n        assembly {\n            $.slot := INITIALIZABLE_STORAGE\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlDefaultAdminRules.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"../IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection.\n */\ninterface IAccessControlDefaultAdminRules is IAccessControl {\n    /**\n     * @dev The new default admin is not a valid default admin.\n     */\n    error AccessControlInvalidDefaultAdmin(address defaultAdmin);\n\n    /**\n     * @dev At least one of the following rules was violated:\n     *\n     * - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.\n     * - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.\n     * - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.\n     */\n    error AccessControlEnforcedDefaultAdminRules();\n\n    /**\n     * @dev The delay for transferring the default admin delay is enforced and\n     * the operation must wait until `schedule`.\n     *\n     * NOTE: `schedule` can be 0 indicating there's no transfer scheduled.\n     */\n    error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);\n\n    /**\n     * @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next\n     * address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`\n     * passes.\n     */\n    event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);\n\n    /**\n     * @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.\n     */\n    event DefaultAdminTransferCanceled();\n\n    /**\n     * @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next\n     * delay to be applied between default admin transfer after `effectSchedule` has passed.\n     */\n    event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);\n\n    /**\n     * @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.\n     */\n    event DefaultAdminDelayChangeCanceled();\n\n    /**\n     * @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.\n     */\n    function defaultAdmin() external view returns (address);\n\n    /**\n     * @dev Returns a tuple of a `newAdmin` and an accept schedule.\n     *\n     * After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role\n     * by calling {acceptDefaultAdminTransfer}, completing the role transfer.\n     *\n     * A zero value only in `acceptSchedule` indicates no pending admin transfer.\n     *\n     * NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.\n     */\n    function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);\n\n    /**\n     * @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.\n     *\n     * This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set\n     * the acceptance schedule.\n     *\n     * NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this\n     * function returns the new delay. See {changeDefaultAdminDelay}.\n     */\n    function defaultAdminDelay() external view returns (uint48);\n\n    /**\n     * @dev Returns a tuple of `newDelay` and an effect schedule.\n     *\n     * After the `schedule` passes, the `newDelay` will get into effect immediately for every\n     * new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.\n     *\n     * A zero value only in `effectSchedule` indicates no pending delay change.\n     *\n     * NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}\n     * will be zero after the effect schedule.\n     */\n    function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);\n\n    /**\n     * @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance\n     * after the current timestamp plus a {defaultAdminDelay}.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * Emits a DefaultAdminRoleChangeStarted event.\n     */\n    function beginDefaultAdminTransfer(address newAdmin) external;\n\n    /**\n     * @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.\n     *\n     * A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * May emit a DefaultAdminTransferCanceled event.\n     */\n    function cancelDefaultAdminTransfer() external;\n\n    /**\n     * @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.\n     *\n     * After calling the function:\n     *\n     * - `DEFAULT_ADMIN_ROLE` should be granted to the caller.\n     * - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.\n     * - {pendingDefaultAdmin} should be reset to zero values.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.\n     * - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.\n     */\n    function acceptDefaultAdminTransfer() external;\n\n    /**\n     * @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting\n     * into effect after the current timestamp plus a {defaultAdminDelay}.\n     *\n     * This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this\n     * method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}\n     * set before calling.\n     *\n     * The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then\n     * calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}\n     * complete transfer (including acceptance).\n     *\n     * The schedule is designed for two scenarios:\n     *\n     * - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by\n     * {defaultAdminDelayIncreaseWait}.\n     * - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.\n     *\n     * A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function changeDefaultAdminDelay(uint48 newDelay) external;\n\n    /**\n     * @dev Cancels a scheduled {defaultAdminDelay} change.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * May emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function rollbackDefaultAdminDelay() external;\n\n    /**\n     * @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})\n     * to take effect. Default to 5 days.\n     *\n     * When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with\n     * the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)\n     * that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can\n     * be overrode for a custom {defaultAdminDelay} increase scheduling.\n     *\n     * IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,\n     * there's a risk of setting a high new delay that goes into effect almost immediately without the\n     * possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).\n     */\n    function defaultAdminDelayIncreaseWait() external view returns (uint48);\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"@openzeppelin/contracts/access/IAccessControl.sol\";\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {ERC165Upgradeable} from \"../utils/introspection/ERC165Upgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {\n    struct RoleData {\n        mapping(address account => bool) hasRole;\n        bytes32 adminRole;\n    }\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl\n    struct AccessControlStorage {\n        mapping(bytes32 role => RoleData) _roles;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.AccessControl\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;\n\n    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {\n        assembly {\n            $.slot := AccessControlStorageLocation\n        }\n    }\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with an {AccessControlUnauthorizedAccount} error including the required role.\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    function __AccessControl_init() internal onlyInitializing {\n    }\n\n    function __AccessControl_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        return $._roles[role].hasRole[account];\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n     * is missing `role`.\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert AccessControlUnauthorizedAccount(account, role);\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        return $._roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n        if (callerConfirmation != _msgSender()) {\n            revert AccessControlBadConfirmation();\n        }\n\n        _revokeRole(role, callerConfirmation);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        $._roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        if (!hasRole(role, account)) {\n            $._roles[role].hasRole[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        if (hasRole(role, account)) {\n            $._roles[role].hasRole[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev The `account` is missing a role.\n     */\n    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n    /**\n     * @dev The caller of a function is not the expected one.\n     *\n     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n     */\n    error AccessControlBadConfirmation();\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/interfaces/IERC5313.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5313.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface for the Light Contract Ownership Standard.\n *\n * A standardized minimal interface required to identify an account that controls a contract\n */\ninterface IERC5313 {\n    /**\n     * @dev Gets the address of the owner.\n     */\n    function owner() external view returns (address);\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165 {\n    function __ERC165_init() internal onlyInitializing {\n    }\n\n    function __ERC165_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
        "@balancer-labs/v2-interfaces/=lib/balancer-v2-monorepo/pkg/interfaces/",
        "@balancer-labs/v2-pool-stable/=lib/balancer-v2-monorepo/pkg/pool-stable/",
        "@chainlink/contracts/=lib/chainlink/contracts/",
        "@uniswap/v3-periphery/=lib/v3-periphery/",
        "@uniswap/v3-core/=lib/v3-core/",
        "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
        "balancer-v2-monorepo/=lib/balancer-v2-monorepo/",
        "chainlink/=lib/chainlink/",
        "ds-test/=lib/forge-safe/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
        "forge-safe/=lib/forge-safe/src/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "solady/=lib/solady/",
        "solidity-stringutils/=lib/forge-safe/lib/surl/lib/solidity-stringutils/",
        "solmate/=lib/forge-safe/lib/solmate/src/",
        "surl/=lib/forge-safe/lib/surl/",
        "v3-core/=lib/v3-core/",
        "v3-periphery/=lib/v3-periphery/contracts/",
        "solarray/=lib/solarray/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 300
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": false,
      "libraries": {}
    }
  }}