creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/modules/adaptors/Aave/V3/AaveV3ATokenManagerAdaptor.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { BaseAdaptor, ERC20, SafeTransferLib, Fund, PriceRouter, Math } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { IAaveToken } from \"src/interfaces/external/IAaveToken.sol\";\nimport { IAaveOracle } from \"src/interfaces/external/IAaveOracle.sol\";\nimport { AaveV3AccountHelper, Address } from \"./AaveV3AccountHelper.sol\";\nimport { AaveV3AccountExtension } from \"./AaveV3AccountExtension.sol\";\n\n/**\n * @title Aave aToken Adaptor\n * @notice Allows Funds to interact with Aave aToken positions.\n */\ncontract AaveV3ATokenManagerAdaptor is BaseAdaptor, AaveV3AccountHelper {\n    using SafeTransferLib for ERC20;\n    using Math for uint256;\n\n    //==================== Adaptor Data Specification ====================\n    // adaptorData = abi.encode(uint8 accountId, address aToken)\n    // Where:\n    // `accountId` is the account extension id this adaptor is working with\n    // `aToken` is the aToken address the position this adaptor is working with\n    //================= Configuration Data Specification =================\n    // configurationData = abi.encode(minimumHealthFactor uint256)\n    // Where:\n    // `minimumHealthFactor` dictates how much assets can be taken from this position\n    // If zero:\n    //      position returns ZERO for `withdrawableFrom`\n    // else:\n    //      position calculates `withdrawableFrom` based off minimum specified\n    //      position reverts if a user withdraw lowers health factor below minimum\n    //\n    // **************************** IMPORTANT ****************************\n    // It's advised for Funds with multiple aToken positions to specify the minimum\n    // health factor on ONE of the positions only as withdrawing from multiple positions\n    // can lead to a lower health factor than expected and thus revert.\n    // An aToken should always have a position in the fund as well as a position\n    // for its underlying asset. The adapter does not check the latter for gas optimzation purposes.\n    //====================================================================\n\n    /**\n     @notice Attempted withdraw would lower Fund health factor too low.\n     */\n    error AaveV3ATokenAdaptor__HealthFactorTooLow();\n\n    /**\n     * @notice Aave V3 oracle uses a different base asset than this contract was designed for.\n     */\n    error AaveV3ATokenAdaptor__OracleUsesDifferentBase();\n\n    /**\n     * @notice This value is used to prevent calculation errors when computing the max withdrawable\n     * amount before going under the minimum configured HF.\n     * @dev This value can be modified based on testing.\n     */\n    uint256 internal constant _CUSHION = 0.01e18;\n\n    /// @dev Used to convert from BPS (4 decimals) to WAD (18 decimals).\n    uint256 internal constant _BPS_TO_WAD = 1e14;\n\n    /// @dev expected USD units as base currency.\n    uint256 internal constant _USD_CURRENCY_UNIT = 1e8;\n\n    /**\n     * @notice The Aave V3 Oracle on current network.\n     * @dev For mainnet use 0x54586bE62E3c3580375aE3723C145253060Ca0C2.\n     */\n    IAaveOracle public immutable aaveOracle;\n\n    /**\n     * @notice Minimum Health Factor enforced after every aToken withdraw.\n     * @notice Overwrites strategist set minimums if they are lower.\n     */\n    uint256 public immutable minimumHealthFactor;\n\n    constructor(address v3Pool, address v3Oracle, uint256 minHealthFactor) AaveV3AccountHelper(v3Pool) {\n        _verifyConstructorMinimumHealthFactor(minHealthFactor);\n        aaveOracle = IAaveOracle(v3Oracle);\n        minimumHealthFactor = minHealthFactor;\n    }\n\n    //============================================ Global Functions ===========================================\n    /**\n     * @dev Identifier unique to this adaptor for a shared registry.\n     * Normally the identifier would just be the address of this contract, but this\n     * Identifier is needed during Fund Delegate Call Operations, so getting the address\n     * of the adaptor is more difficult.\n     */\n    function identifier() public pure override returns (bytes32) {\n        return keccak256(abi.encode(\"Aave V3 aToken Account Manager Adaptor V 1.0\"));\n    }\n\n    //============================================ Implement Base Functions ===========================================\n    /**\n     * @notice Fund must approve Pool to spend its assets, then call deposit to lend its assets.\n     * @param assets the amount of assets to lend on Aave\n     * @param adaptorData adaptor data containining the abi encoded aToken\n     * @dev configurationData is NOT used because this action will only increase the health factor\n     */\n    function deposit(uint256 assets, bytes memory adaptorData, bytes memory) public override {\n        // we need to verify that the position is used by the fund before depositing assets\n        // this is to prevent the strategist from depositing assets into a position that is not used by the fund\n        _verifyUsedPositionIfDeployed(adaptorData);\n\n        // Deposit assets to Aave.\n        (uint8 accountId, address aToken) = _decodeAdaptorData(adaptorData);\n\n        address accountAddress = _createAccountExtensionIfNeeded(accountId);\n\n        ERC20 token = ERC20(IAaveToken(aToken).UNDERLYING_ASSET_ADDRESS());\n\n        // instead of transferting the assets to the account, we deposit to the pool on behalf of the account\n        // for gas optimization purposes\n        token.safeApprove(address(pool), assets);\n        pool.supply(address(token), assets, accountAddress, 0);\n\n        // Zero out approvals if necessary.\n        _revokeExternalApproval(token, address(pool));\n    }\n\n    /**\n     @notice Funds must withdraw from Aave, check if a minimum health factor is specified\n     *       then transfer assets to receiver.\n     * @dev Important to verify that external receivers are allowed if receiver is not Fund address.\n     * @param assets the amount of assets to withdraw from Aave\n     * @param receiver the address to send withdrawn assets to\n     * @param adaptorData adaptor data containining the abi encoded aToken\n     * @param configData abi encoded minimum health factor, if zero user withdraws are not allowed.\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        bytes memory adaptorData,\n        bytes memory configData\n    ) public override {\n        // Run external receiver check.\n        _externalReceiverCheck(receiver);\n\n        // Withdraw assets from Aave.\n        (address accountAddress, address aToken) = _extractAdaptorDataAndVerify(adaptorData, address(this));\n        address underlyingAsset = IAaveToken(aToken).UNDERLYING_ASSET_ADDRESS();\n\n        AaveV3AccountExtension(accountAddress).withdrawFromAave(receiver, underlyingAsset, assets);\n\n        (, uint256 totalDebtBase, , , , uint256 healthFactor) = pool.getUserAccountData(accountAddress);\n        if (totalDebtBase > 0) {\n            // If fund has entered an EMode, and has debt, user withdraws are not allowed.\n            if (pool.getUserEMode(accountAddress) != 0) revert BaseAdaptor__UserWithdrawsNotAllowed();\n\n            // Run minimum health factor checks.\n            uint256 minHealthFactor = abi.decode(configData, (uint256));\n            if (minHealthFactor == 0) {\n                revert BaseAdaptor__UserWithdrawsNotAllowed();\n            }\n            // Check if adaptor minimum health factor is more conservative than strategist set.\n            if (minHealthFactor < minimumHealthFactor) minHealthFactor = minimumHealthFactor;\n            if (healthFactor < minHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n        }\n    }\n\n    /**\n     * @notice Uses configurartion data minimum health factor to calculate withdrawable assets from Aave.\n     * @dev Applies a `cushion` value to the health factor checks and calculation.\n     *      The goal of this is to minimize scenarios where users are withdrawing a very small amount of\n     *      assets from Aave. This function returns zero if\n     *      -minimum health factor is NOT set.\n     *      -the current health factor is less than the minimum health factor + 2x `cushion`\n     *      Otherwise this function calculates the withdrawable amount using\n     *      minimum health factor + `cushion` for its calcualtions.\n     * @dev It is possible for the math below to lose a small amount of precision since it is only\n     *      maintaining 18 decimals during the calculation, but this is desired since\n     *      doing so lowers the withdrawable from amount which in turn raises the health factor.\n     */\n    function withdrawableFrom(\n        bytes memory adaptorData,\n        bytes memory configData\n    ) public view override returns (uint256) {\n        (address accountAddress, address aToken) = _extractAdaptorData(adaptorData, msg.sender);\n\n        // if the account does not exist (yet) but has aToken sent to it, we still want to return 0\n        // because the fund cannot withdraw from it unless it has been created\n        if (!Address.isContract(accountAddress)) return 0;\n\n        (\n            uint256 totalCollateralBase,\n            uint256 totalDebtBase,\n            ,\n            uint256 currentLiquidationThreshold,\n            ,\n            uint256 healthFactor\n        ) = pool.getUserAccountData(accountAddress);\n\n        // If Fund has no Aave debt, then return the funds balance of the aToken.\n        if (totalDebtBase == 0) return ERC20(aToken).balanceOf(accountAddress);\n\n        // Fund has Aave debt, so if fund is entered into a non zero emode, return 0.\n        if (pool.getUserEMode(accountAddress) != 0) return 0;\n\n        // Otherwise we need to look at minimum health factor.\n        uint256 minHealthFactor = abi.decode(configData, (uint256));\n        // Check if minimum health factor is set.\n        // If not the strategist does not want users to withdraw from this position.\n        if (minHealthFactor == 0) return 0;\n        // Check if adaptor minimum health factor is more conservative than strategist set.\n        if (minHealthFactor < minimumHealthFactor) minHealthFactor = minimumHealthFactor;\n\n        uint256 maxBorrowableWithMin;\n\n        // Add cushion to min health factor.\n        minHealthFactor += _CUSHION;\n\n        // If current health factor is less than the minHealthFactor + 2X cushion, return 0.\n        if (healthFactor < (minHealthFactor + _CUSHION)) return 0;\n        // Calculate max amount withdrawable while preserving minimum health factor.\n        else {\n            maxBorrowableWithMin =\n                totalCollateralBase -\n                minHealthFactor.mulDivDown(totalDebtBase, (currentLiquidationThreshold * _BPS_TO_WAD));\n        }\n        /// @dev We want right side of \"-\" to have 8 decimals, so we need to dviide by 18 decimals.\n        // `currentLiquidationThreshold` has 4 decimals, so multiply by 1e14 to get 18 decimals on denominator.\n\n        // Need to convert Base into position underlying.\n        ERC20 underlyingAsset = ERC20(IAaveToken(aToken).UNDERLYING_ASSET_ADDRESS());\n\n        // Convert `maxBorrowableWithMin` from Base to position underlying asset.\n        PriceRouter priceRouter = Fund(msg.sender).priceRouter();\n        uint256 underlyingAssetToUSD = priceRouter.getPriceInUSD(underlyingAsset);\n        uint256 withdrawable = maxBorrowableWithMin.mulDivDown(10 ** underlyingAsset.decimals(), underlyingAssetToUSD);\n        uint256 balance = ERC20(aToken).balanceOf(accountAddress);\n        // Check if withdrawable is greater than the position balance and if so return the balance instead of withdrawable.\n        return withdrawable > balance ? balance : withdrawable;\n    }\n\n    /**\n     * @notice Returns the funds balance of the positions aToken.\n     */\n    function balanceOf(bytes memory adaptorData) public view override returns (uint256) {\n        (address accountAddress, address aToken) = _extractAdaptorData(adaptorData, msg.sender);\n\n        // if the account does not exist (yet) but has aToken sent to it, we still want to return 0\n        // because the fund cannot withdraw from it unless it has been created\n        if (!Address.isContract(accountAddress)) return 0;\n\n        return ERC20(aToken).balanceOf(accountAddress);\n    }\n\n    /**\n     * @notice Returns the positions aToken underlying asset.\n     */\n    function assetOf(bytes memory adaptorData) public view override returns (ERC20) {\n        (, address aToken) = _decodeAdaptorData(adaptorData);\n        return ERC20(IAaveToken(aToken).UNDERLYING_ASSET_ADDRESS());\n    }\n\n    function assetsUsed(bytes memory adaptorData) public view override returns (ERC20[] memory assets) {\n        assets = new ERC20[](1);\n        assets[0] = assetOf(adaptorData);\n\n        // Make sure Aave Oracle uses USD base asset with 8 decimals.\n        // BASE_CURRENCY and BASE_CURRENCY_UNIT are both immutable values, so only check this on initial position setup.\n        if (aaveOracle.BASE_CURRENCY() != address(0) || aaveOracle.BASE_CURRENCY_UNIT() != _USD_CURRENCY_UNIT)\n            revert AaveV3ATokenAdaptor__OracleUsesDifferentBase();\n    }\n\n    /**\n     * @notice This adaptor returns collateral, and not debt.\n     */\n    function isDebt() public pure override returns (bool) {\n        return false;\n    }\n\n    //============================================ Strategist Functions ===========================================\n    /**\n     * @notice Allows strategists to lend assets on Aave.\n     * @dev Uses `_maxAvailable` helper function, see BaseAdaptor.sol\n     * @param accountId the account id to lend on Aave\n     * @param aToken the token to lend on Aave (corresponds to the aToken)\n     * @param amountToDeposit the amount of `tokenToDeposit` to lend on Aave.\n     */\n    function depositToAave(uint8 accountId, IAaveToken aToken, uint256 amountToDeposit) public {\n        // since this function can be called by the strategist, we need to verify that the adaptorData is defined in\n        // in a position that is used by the fund\n        _validateAToken(accountId, aToken);\n\n        address accountAddress = _createAccountExtensionIfNeeded(accountId);\n\n        ERC20 tokenToDeposit = ERC20(aToken.UNDERLYING_ASSET_ADDRESS());\n\n        amountToDeposit = _maxAvailable(tokenToDeposit, amountToDeposit);\n        tokenToDeposit.safeApprove(address(pool), amountToDeposit);\n        pool.supply(address(tokenToDeposit), amountToDeposit, accountAddress, 0);\n\n        // Zero out approvals if necessary.\n        _revokeExternalApproval(tokenToDeposit, address(pool));\n    }\n\n    /**\n     * @notice Allows strategists to withdraw assets from Aave.\n     * @param underlyingToken the underlying token to withdraw from Aave.\n     * @param amountToWithdraw the amount of `tokenToWithdraw` to withdraw from Aave\n     */\n    function withdrawFromAave(uint8 accountId, address underlyingToken, uint256 amountToWithdraw) public {\n        // should revert if account extension does not exist\n        address accountAddress = _getAccountAddressAndVerify(accountId, address(this));\n\n        AaveV3AccountExtension(accountAddress).withdrawFromAave(address(this), underlyingToken, amountToWithdraw);\n\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(accountAddress);\n        if (healthFactor < minimumHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategists to adjust an asset's isolation mode.\n     * @param accountId the account id to adjust isolation mode / colalteral mode for\n     * @param underlyingToken the underlying asset to adjust isolation mode / collateral mode for\n     * @param useAsCollateral whether to use the asset as collateral or not\n     */\n    function adjustIsolationModeAssetAsCollateral(\n        uint8 accountId,\n        address underlyingToken,\n        bool useAsCollateral\n    ) public {\n        // should revert if account extension does not exist\n        address accountAddress = _getAccountAddressAndVerify(accountId, address(this));\n\n        AaveV3AccountExtension(accountAddress).adjustIsolationModeAssetAsCollateral(underlyingToken, useAsCollateral);\n\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(accountAddress);\n        if (healthFactor < minimumHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategist to use aTokens to repay debt tokens with the same underlying.\n     * @param accountId the id of the account used as an extension to the fund.\n     * @param aToken any aave aToken used in the account that is used by a position in the fund.\n     */\n    function createAccountExtension(uint8 accountId, IAaveToken aToken) public returns (address) {\n        _validateAToken(accountId, aToken);\n        return _createAccountExtensionIfNeeded(accountId);\n    }\n\n    function _validateAToken(uint8 accountId, IAaveToken aToken) internal view {\n        // should revert if account extension does not exist\n        _verifyUsedPosition(abi.encode(accountId, address(aToken)));\n    }\n}\n"
      },
      "src/modules/adaptors/BaseAdaptor.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { Math } from \"src/utils/Math.sol\";\nimport { SafeTransferLib } from \"@solmate/utils/SafeTransferLib.sol\";\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\nimport { Registry } from \"src/Registry.sol\";\nimport { Fund } from \"src/base/Fund.sol\";\nimport { PriceRouter } from \"src/modules/price-router/PriceRouter.sol\";\n\n/**\n * @title Base Adaptor\n * @notice Base contract all adaptors must inherit from.\n * @dev Allows Funds to interact with arbritrary DeFi assets and protocols.\n * @author crispymangoes\n */\nabstract contract BaseAdaptor {\n    using SafeTransferLib for ERC20;\n    using Math for uint256;\n\n    /**\n     * @notice Attempted to specify an external receiver during a Fund `callOnAdaptor` call.\n     */\n    error BaseAdaptor__ExternalReceiverBlocked();\n\n    /**\n     * @notice Attempted to deposit to a position where user deposits were not allowed.\n     */\n    error BaseAdaptor__UserDepositsNotAllowed();\n\n    /**\n     * @notice Attempted to withdraw from a position where user withdraws were not allowed.\n     */\n    error BaseAdaptor__UserWithdrawsNotAllowed();\n\n    /**\n     * @notice Attempted swap has bad slippage.\n     */\n    error BaseAdaptor__Slippage();\n\n    /**\n     * @notice Attempted swap used unsupported output asset.\n     */\n    error BaseAdaptor__PricingNotSupported(address asset);\n\n    /**\n     * @notice Attempted to set a constructor minimum health factor to a value\n     *         below `MINIMUM_CONSTRUCTOR_HEALTH_FACTOR()`.\n     */\n    error BaseAdaptor__ConstructorHealthFactorTooLow();\n\n    /**\n     * @notice Attempted to interact with a position that is not used in the calling fund.\n     */\n    error BaseAdaptor__PositionNotUsed(bytes adaptorData);\n\n    //============================================ Global Functions ===========================================\n    /**\n     * @dev Identifier unique to this adaptor for a shared registry.\n     * Normally the identifier would just be the address of this contract, but this\n     * Identifier is needed during Fund Delegate Call Operations, so getting the address\n     * of the adaptor is more difficult.\n     */\n    function identifier() public pure virtual returns (bytes32) {\n        return keccak256(abi.encode(\"Base Adaptor V 0.0\"));\n    }\n\n    function SWAP_ROUTER_REGISTRY_SLOT() internal pure returns (uint256) {\n        return 1;\n    }\n\n    function PRICE_ROUTER_REGISTRY_SLOT() internal pure returns (uint256) {\n        return 2;\n    }\n\n    /**\n     * @notice Max possible slippage when making a swap router swap.\n     */\n    function slippage() public pure virtual returns (uint32) {\n        return 0.9e4;\n    }\n\n    /**\n     * @notice The default minimum constructor health factor.\n     * @dev Adaptors can choose to override this if they need a different value.\n     */\n    function MINIMUM_CONSTRUCTOR_HEALTH_FACTOR() internal pure virtual returns (uint256) {\n        return 1.05e18;\n    }\n\n    //============================================ Implement Base Functions ===========================================\n    //==================== Base Function Specification ====================\n    // Base functions are functions designed to help the Fund interact with\n    // an adaptor position, strategists are not intended to use these functions.\n    // Base functions MUST be implemented in adaptor contracts, even if that is just\n    // adding a revert statement to make them uncallable by normal user operations.\n    //\n    // All view Base functions will be called used normal staticcall.\n    // All mutative Base functions will be called using delegatecall.\n    //=====================================================================\n    /**\n     * @notice Function Funds call to deposit users funds into holding position.\n     * @param assets the amount of assets to deposit\n     * @param adaptorData data needed to deposit into a position\n     * @param configurationData data settable when strategists add positions to their Fund\n     *                          Allows strategist to control how the adaptor interacts with the position\n     */\n    function deposit(uint256 assets, bytes memory adaptorData, bytes memory configurationData) public virtual;\n\n    /**\n     * @notice Function Funds call to withdraw funds from positions to send to users.\n     * @param receiver the address that should receive withdrawn funds\n     * @param adaptorData data needed to withdraw from a position\n     * @param configurationData data settable when strategists add positions to their Fund\n     *                          Allows strategist to control how the adaptor interacts with the position\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        bytes memory adaptorData,\n        bytes memory configurationData\n    ) public virtual;\n\n    /**\n     * @notice Function Funds use to determine `assetOf` balance of an adaptor position.\n     * @param adaptorData data needed to interact with the position\n     * @return balance of the position in terms of `assetOf`\n     */\n    function balanceOf(bytes memory adaptorData) public view virtual returns (uint256);\n\n    /**\n     * @notice Functions Funds use to determine the withdrawable balance from an adaptor position.\n     * @dev Debt positions MUST return 0 for their `withdrawableFrom`\n     * @notice accepts adaptorData and configurationData\n     * @return withdrawable balance of the position in terms of `assetOf`\n     */\n    function withdrawableFrom(bytes memory, bytes memory) public view virtual returns (uint256);\n\n    /**\n     * @notice Function Funds use to determine the underlying ERC20 asset of a position.\n     * @param adaptorData data needed to withdraw from a position\n     * @return the underlying ERC20 asset of a position\n     */\n    function assetOf(bytes memory adaptorData) public view virtual returns (ERC20);\n\n    /**\n     * @notice When positions are added to the Registry, this function can be used in order to figure out\n     *         what assets this adaptor needs to price, and confirm pricing is properly setup.\n     */\n    function assetsUsed(bytes memory adaptorData) public view virtual returns (ERC20[] memory assets) {\n        assets = new ERC20[](1);\n        assets[0] = assetOf(adaptorData);\n    }\n\n    /**\n     * @notice Functions Registry/Funds use to determine if this adaptor reports debt values.\n     * @dev returns true if this adaptor reports debt values.\n     */\n    function isDebt() public view virtual returns (bool);\n\n    //============================================ Strategist Functions ===========================================\n    //==================== Strategist Function Specification ====================\n    // Strategist functions are only callable by strategists through the Funds\n    // `callOnAdaptor` function. A fund will never call any of these functions,\n    // when a normal user interacts with a fund(depositing/withdrawing)\n    //\n    // All strategist functions will be called using delegatecall.\n    // Strategist functions are intentionally \"blind\" to what positions the fund\n    // is currently holding. This allows strategists to enter temporary positions\n    // while rebalancing.\n    // To mitigate strategist from abusing this and moving funds in untracked\n    // positions, the fund will enforce a Total Value Locked check that\n    // insures TVL has not deviated too much from `callOnAdaptor`.\n    //===========================================================================\n\n    //============================================ Helper Functions ===========================================\n    /**\n     * @notice Helper function that allows adaptor calls to use the max available of an ERC20 asset\n     * by passing in type(uint256).max\n     * @param token the ERC20 asset to work with\n     * @param amount when `type(uint256).max` is used, this function returns `token`s `balanceOf`\n     * otherwise this function returns amount.\n     */\n    function _maxAvailable(ERC20 token, uint256 amount) internal view virtual returns (uint256) {\n        if (amount == type(uint256).max) return token.balanceOf(address(this));\n        else return amount;\n    }\n\n    /**\n     * @notice Helper function that checks if `spender` has any more approval for `asset`, and if so revokes it.\n     */\n    function _revokeExternalApproval(ERC20 asset, address spender) internal {\n        if (asset.allowance(address(this), spender) > 0) asset.safeApprove(spender, 0);\n    }\n\n    /**\n     * @notice Helper function that validates external receivers are allowed.\n     */\n    function _externalReceiverCheck(address receiver) internal view {\n        if (receiver != address(this) && Fund(address(this)).blockExternalReceiver())\n            revert BaseAdaptor__ExternalReceiverBlocked();\n    }\n\n    /**\n     * @notice Verifies if the configured minimum health factor is in the allowed range.\n     * @param minimumHealthFactor the configured minimum health factor to verify.\n     */\n    function _verifyConstructorMinimumHealthFactor(uint256 minimumHealthFactor) internal pure {\n        if (minimumHealthFactor < MINIMUM_CONSTRUCTOR_HEALTH_FACTOR())\n            revert BaseAdaptor__ConstructorHealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategists to zero out an approval for a given `asset`.\n     * @param asset the ERC20 asset to revoke `spender`s approval for\n     * @param spender the address to revoke approval for\n     */\n    function revokeApproval(ERC20 asset, address spender) public {\n        asset.safeApprove(spender, 0);\n    }\n\n    /**\n     * @notice Allows fund to validate if a position is used in the calling fund.\n     * If the fund is deployed (code size > 0), this function will check if the position is used.\n     * If not deployed, this function will not check if the position is being used as it is assumed that it is\n     * being used by the initial deposit function in the constructor which already adds the position to the fund.\n     */\n    function _verifyUsedPositionIfDeployed(bytes memory adaptorData) internal view {\n        uint256 fundCodeSize;\n        address fundAddress = address(this);\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            fundCodeSize := extcodesize(fundAddress)\n        }\n\n        if (fundCodeSize > 0) {\n            _verifyUsedPosition(adaptorData);\n        }\n    }\n\n    /**\n     * @notice Allows fund to validate if a position is used in the calling fund.\n     */\n    function _verifyUsedPosition(bytes memory adaptorData) internal view {\n        // Check that erc4626Vault position is setup to be used in the calling fund.\n        bytes32 positionHash = keccak256(abi.encode(identifier(), isDebt(), adaptorData));\n        uint32 positionId = Fund(address(this)).registry().getPositionHashToPositionId(positionHash);\n        if (!Fund(address(this)).isPositionUsed(positionId)) revert BaseAdaptor__PositionNotUsed(adaptorData);\n    }\n}\n"
      },
      "src/interfaces/external/IAaveToken.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IAaveToken is IERC20 {\n    function UNDERLYING_ASSET_ADDRESS() external view returns (address);\n}\n"
      },
      "src/interfaces/external/IAaveOracle.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\ninterface IAaveOracle {\n    function BASE_CURRENCY_UNIT() external view returns (uint256);\n\n    function BASE_CURRENCY() external view returns (address);\n\n    function getAssetPrice(address asset) external view returns (uint256);\n}\n"
      },
      "src/modules/adaptors/Aave/V3/AaveV3AccountHelper.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { AaveV3AccountExtension } from \"./AaveV3AccountExtension.sol\";\nimport { Create2 } from \"@openzeppelin/contracts/utils/Create2.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { AaveV3ATokenAdaptor } from \"./AaveV3ATokenAdaptor.sol\";\nimport { AaveV3DebtTokenAdaptor } from \"./AaveV3DebtTokenAdaptor.sol\";\nimport { IPoolV3 } from \"src/interfaces/external/IPoolV3.sol\";\n\n/**\n * @title Aave V3 Account Helper Contract\n * @notice Allows Funds to create multiple aave accounts and get their addresses.\n */\nabstract contract AaveV3AccountHelper {\n    /**\n     * @notice The Aave V3 Pool contract on current network.\n     * @dev For mainnet use 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2.\n     */\n    IPoolV3 public immutable pool;\n\n    bytes32 public immutable accountBytecodeHash;\n\n    constructor(address v3Pool) {\n        pool = IPoolV3(v3Pool);\n\n        bytes memory creationCode = _getCreationCode();\n        accountBytecodeHash = keccak256(creationCode);\n    }\n\n    error AaveV3AccountHelper__AccountExtensionDoesNotExist();\n    error AaveV3AccountHelper__AccountExtensionAddresDoNotMatch();\n\n    event AccountExtensionCreated(uint8 indexed accountId, address accountAddress);\n\n    function _createAccountExtensionIfNeeded(uint8 accountId) internal returns (address) {\n        address expectedAccountAddress = _getAccountAddress(accountId, address(this));\n\n        // if the account does not exist (yet), we still want to create the account\n        if (!Address.isContract(expectedAccountAddress)) {\n            bytes memory creationCode = _getCreationCode();\n            // Create 2 will check if the account extension already exists and reverts if it does\n            bytes32 salt = _getSaltById(accountId);\n            address deployedAccountAddress = Create2.deploy(0, salt, creationCode);\n\n            // this should never happen, but just in case\n            if (deployedAccountAddress != expectedAccountAddress) {\n                revert AaveV3AccountHelper__AccountExtensionAddresDoNotMatch();\n            }\n\n            // by default the account does not have any E mode enabled on Aave\n            if (accountId != 0) {\n                // we enable the E mode for the account if it is not the default account\n                AaveV3AccountExtension(expectedAccountAddress).changeEMode(accountId);\n            }\n\n            emit AccountExtensionCreated(accountId, expectedAccountAddress);\n        }\n\n        return expectedAccountAddress;\n    }\n\n    // extracts the account address and aave token from the adaptor data without verifying that the account exists\n    function _extractAdaptorData(\n        bytes memory adaptorData,\n        address fundAddress\n    ) internal view returns (address, address) {\n        (uint8 id, address aaveToken) = _decodeAdaptorData(adaptorData);\n\n        address accountAddress = _getAccountAddress(id, fundAddress);\n\n        return (accountAddress, aaveToken);\n    }\n\n    // extracts the account address and aave token from the adaptor data and verifies that the account exists\n    function _extractAdaptorDataAndVerify(\n        bytes memory adaptorData,\n        address fundAddress\n    ) internal view returns (address, address) {\n        (address accountAddress, address aaveToken) = _extractAdaptorData(adaptorData, fundAddress);\n\n        if (!Address.isContract(accountAddress)) {\n            revert AaveV3AccountHelper__AccountExtensionDoesNotExist();\n        }\n\n        return (accountAddress, aaveToken);\n    }\n\n    // extracts the account address\n    function _getAccountAddress(uint8 id, address fundAddress) internal view returns (address) {\n        bytes32 salt = _getSaltById(id);\n        // create 2 is necessary to have a deterministic address for the account extension using the bytecode hash\n        return Create2.computeAddress(salt, accountBytecodeHash, fundAddress);\n    }\n\n    // extracts the account address and aave token from the adaptor data and verifies that the account exists\n    function _getAccountAddressAndVerify(uint8 id, address fundAddress) internal view returns (address) {\n        address accountAddress = _getAccountAddress(id, fundAddress);\n\n        if (!Address.isContract(accountAddress)) {\n            revert AaveV3AccountHelper__AccountExtensionDoesNotExist();\n        }\n\n        return accountAddress;\n    }\n\n    function _getCreationCode() internal view returns (bytes memory) {\n        bytes memory constructorArgs = abi.encode(pool);\n        return abi.encodePacked(type(AaveV3AccountExtension).creationCode, constructorArgs);\n    }\n\n    function _getSaltById(uint8 accountId) internal pure returns (bytes32) {\n        return bytes32(uint256(accountId));\n    }\n\n    function _decodeAdaptorData(bytes memory adaptorData) internal pure returns (uint8, address) {\n        (uint8 accountId, address aaveToken) = abi.decode(adaptorData, (uint8, address));\n        return (accountId, aaveToken);\n    }\n}\n"
      },
      "src/modules/adaptors/Aave/V3/AaveV3AccountExtension.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { ERC20, SafeTransferLib, Fund, PriceRouter, Math } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { IPoolV3 } from \"src/interfaces/external/IPoolV3.sol\";\nimport { IAaveToken } from \"src/interfaces/external/IAaveToken.sol\";\nimport { IAaveOracle } from \"src/interfaces/external/IAaveOracle.sol\";\nimport { ICreditDelegationToken } from \"src/interfaces/external/ICreditDelegationToken.sol\";\n\n/**\n * @title Aave Account Adaptor\n * @notice Allows Funds to create multiple aave accounts and interact with Aave positions.\n * @dev This adaptor should be used in conjunction with the AaveV3AccountManager and is not\n * meant to be used as a standalone contract.\n */\ncontract AaveV3AccountExtension {\n    using SafeTransferLib for ERC20;\n\n    //==================== Adaptor Data Specification ====================\n    // NOT USED\n    //================= Configuration Data Specification =================\n    // NOT USED\n    // **************************** IMPORTANT ****************************\n    // NOT USED\n    //====================================================================\n\n    address public immutable fund;\n\n    IPoolV3 public immutable pool;\n\n    constructor(address v3Pool) {\n        fund = msg.sender;\n        pool = IPoolV3(v3Pool);\n    }\n\n    error AaveV3AccountExtension__CallerNotFund();\n\n    modifier onlyFund() {\n        if (msg.sender != fund) {\n            revert AaveV3AccountExtension__CallerNotFund();\n        }\n        _;\n    }\n\n    //============================================ Global Functions ===========================================\n\n    function approveATokenToFund(address aToken) external onlyFund {\n        ERC20(aToken).safeApprove(fund, type(uint256).max);\n    }\n\n    function approveDebtDelegationToFund(address debtToken) external onlyFund {\n        ICreditDelegationToken(debtToken).approveDelegation(fund, type(uint256).max);\n    }\n\n    /**\n     * @notice Allows strategists to adjust an asset's isolation mode.\n     */\n    function adjustIsolationModeAssetAsCollateral(address underlyingToken, bool useAsCollateral) external onlyFund {\n        pool.setUserUseReserveAsCollateral(underlyingToken, useAsCollateral);\n    }\n\n    /**\n     * @notice Allows strategists to enter different EModes.\n     */\n    function changeEMode(uint8 categoryId) external onlyFund {\n        pool.setUserEMode(categoryId);\n    }\n\n    /**\n     * @notice Allows strategist to use aTokens to repay debt tokens with the same underlying.\n     */\n    function repayWithATokens(ERC20 underlying, uint256 amount) public onlyFund {\n        pool.repayWithATokens(address(underlying), amount, 2);\n    }\n\n    /**\n     * @notice Allows strategist to withdraw assets from Aave.\n     * @param receiver the address to receive the withdrawed assets\n     * @param amount the amount of `underlyingToken` to withdraw from Aave.\n     */\n    function withdrawFromAave(address receiver, address underlyingToken, uint256 amount) external onlyFund {\n        pool.withdraw(underlyingToken, amount, receiver);\n    }\n}\n"
      },
      "src/utils/Math.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nlibrary Math {\n    /**\n     * @notice Substract with a floor of 0 for the result.\n     */\n    function subMinZero(uint256 x, uint256 y) internal pure returns (uint256) {\n        return x > y ? x - y : 0;\n    }\n\n    /**\n     * @notice Used to change the decimals of precision used for an amount.\n     */\n    function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {\n        if (fromDecimals == toDecimals) {\n            return amount;\n        } else if (fromDecimals < toDecimals) {\n            return amount * 10 ** (toDecimals - fromDecimals);\n        } else {\n            return amount / 10 ** (fromDecimals - toDecimals);\n        }\n    }\n\n    // ===================================== OPENZEPPELIN'S MATH =====================================\n\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    // ================================= SOLMATE's FIXEDPOINTMATHLIB =================================\n\n    uint256 public constant WAD = 1e18; // The scalar of ETH and most ERC20s.\n\n    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {\n        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.\n    }\n\n    function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Store x * y in z for now.\n            z := mul(x, y)\n\n            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))\n            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {\n                revert(0, 0)\n            }\n\n            // Divide z by the denominator.\n            z := div(z, denominator)\n        }\n    }\n\n    function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Store x * y in z for now.\n            z := mul(x, y)\n\n            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))\n            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {\n                revert(0, 0)\n            }\n\n            // First, divide z - 1 by the denominator and add 1.\n            // We allow z - 1 to underflow if z is 0, because we multiply the\n            // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n        }\n    }\n}\n"
      },
      "lib/solmate/src/utils/SafeTransferLib.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.\n/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n    /*//////////////////////////////////////////////////////////////\n                             ETH OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferETH(address to, uint256 amount) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Transfer the ETH and store if it succeeded or not.\n            success := call(gas(), to, amount, 0, 0, 0, 0)\n        }\n\n        require(success, \"ETH_TRANSFER_FAILED\");\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                            ERC20 OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferFrom(\n        ERC20 token,\n        address from,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), from) // Append the \"from\" argument.\n            mstore(add(freeMemoryPointer, 36), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 68), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)\n            )\n        }\n\n        require(success, \"TRANSFER_FROM_FAILED\");\n    }\n\n    function safeTransfer(\n        ERC20 token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n            )\n        }\n\n        require(success, \"TRANSFER_FAILED\");\n    }\n\n    function safeApprove(\n        ERC20 token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n            )\n        }\n\n        require(success, \"APPROVE_FAILED\");\n    }\n}\n"
      },
      "lib/solmate/src/tokens/ERC20.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /*//////////////////////////////////////////////////////////////\n                            METADATA STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    uint8 public immutable decimals;\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC20 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /*//////////////////////////////////////////////////////////////\n                            EIP-2612 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal immutable INITIAL_CHAIN_ID;\n\n    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n    mapping(address => uint256) public nonces;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) {\n        name = _name;\n        symbol = _symbol;\n        decimals = _decimals;\n\n        INITIAL_CHAIN_ID = block.chainid;\n        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               ERC20 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        allowance[msg.sender][spender] = amount;\n\n        emit Approval(msg.sender, spender, amount);\n\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        balanceOf[msg.sender] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(msg.sender, to, amount);\n\n        return true;\n    }\n\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual returns (bool) {\n        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n        balanceOf[from] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        return true;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             EIP-2612 LOGIC\n    //////////////////////////////////////////////////////////////*/\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    ) public virtual {\n        require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n        // Unchecked because the only math done is incrementing\n        // the owner's nonce which cannot realistically overflow.\n        unchecked {\n            address recoveredAddress = ecrecover(\n                keccak256(\n                    abi.encodePacked(\n                        \"\\x19\\x01\",\n                        DOMAIN_SEPARATOR(),\n                        keccak256(\n                            abi.encode(\n                                keccak256(\n                                    \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n                                ),\n                                owner,\n                                spender,\n                                value,\n                                nonces[owner]++,\n                                deadline\n                            )\n                        )\n                    )\n                ),\n                v,\n                r,\n                s\n            );\n\n            require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n            allowance[recoveredAddress][spender] = value;\n        }\n\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n    }\n\n    function computeDomainSeparator() internal view virtual returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                    keccak256(bytes(name)),\n                    keccak256(\"1\"),\n                    block.chainid,\n                    address(this)\n                )\n            );\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 amount) internal virtual {\n        totalSupply += amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal virtual {\n        balanceOf[from] -= amount;\n\n        // Cannot underflow because a user's balance\n        // will never be larger than the total supply.\n        unchecked {\n            totalSupply -= amount;\n        }\n\n        emit Transfer(from, address(0), amount);\n    }\n}\n"
      },
      "src/Registry.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { Fund } from \"src/base/Fund.sol\";\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\nimport { BaseAdaptor } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { PriceRouter } from \"src/modules/price-router/PriceRouter.sol\";\nimport { FeesManager } from \"src/modules/fees/FeesManager.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\ncontract Registry is Ownable {\n    using Address for address;\n    // ============================================= ADDRESS CONFIG =============================================\n\n    /**\n     * @notice Emitted when the address of a contract is changed.\n     * @param id value representing the unique ID tied to the changed contract\n     * @param oldAddress address of the contract before the change\n     * @param newAddress address of the contract after the contract\n     */\n    event AddressChanged(uint256 indexed id, address oldAddress, address newAddress);\n\n    /**\n     * @notice Attempted to set the address of a contract that is not registered.\n     * @param id id of the contract that is not registered\n     */\n    error Registry__ContractNotRegistered(uint256 id);\n\n    /**\n     * @notice Emitted when depositor privilege changes.\n     * @param depositor depositor address\n     * @param state the new state of the depositor privilege\n     */\n    event DepositorOnBehalfChanged(address indexed depositor, bool state);\n\n    /**\n     * @notice The unique ID that the next registered contract will have.\n     */\n    uint256 public nextId;\n\n    /**\n     * @notice Get the address associated with an id.\n     */\n    mapping(uint256 => address) public getAddress;\n\n    /**\n     * @notice In order for an address to make deposits on behalf of users they must be approved.\n     */\n    mapping(address => bool) public approvedForDepositOnBehalf;\n\n    /**\n     * @notice In order to receive a flash loan from a source, it must be approved first.\n     */\n    mapping(address => bool) public approvedFlashLoanSource;\n\n    /**\n     * @notice Defines spender address of an aggregator\n     * @dev if spender address is 0, the aggregator was not approved\n     */\n    mapping(address => address) public aggregatorSpender;\n\n    /**\n     * @notice The FeesManager contract.\n     */\n    FeesManager public immutable FEES_MANAGER;\n\n    /**\n     * @notice Allows caller to call multiple functions in a single TX.\n     * @dev Does NOT return the function return values.\n     */\n    function multicall(bytes[] calldata data) external {\n        for (uint256 i; i < data.length; ++i) address(this).functionDelegateCall(data[i]);\n    }\n\n    /**\n     * @notice toggles a depositors  ability to deposit into funds on behalf of users.\n     */\n    function setApprovedForDepositOnBehalf(address depositor, bool state) external onlyOwner {\n        approvedForDepositOnBehalf[depositor] = state;\n        emit DepositorOnBehalfChanged(depositor, state);\n    }\n\n    /**\n     * @notice Set the address of the contract at a given id.\n     */\n    function setAddress(uint256 id, address newAddress) external {\n        if (id > 0) {\n            _checkOwner();\n            if (id >= nextId) revert Registry__ContractNotRegistered(id);\n        } else {\n            if (msg.sender != getAddress[0]) revert Registry__OnlyCallableByZeroId();\n        }\n\n        emit AddressChanged(id, getAddress[id], newAddress);\n\n        getAddress[id] = newAddress;\n    }\n\n    // ============================================= INITIALIZATION =============================================\n\n    /**\n     * @param protocolDAO address of ProtocolDAO contract\n     * @param swapRouter address of SwapRouter contract\n     * @param priceRouter address of PriceRouter contract\n     */\n    constructor(address newOwner, address protocolDAO, address swapRouter, address priceRouter) Ownable() {\n        _register(protocolDAO);\n        _register(swapRouter);\n        _register(priceRouter);\n        FEES_MANAGER = new FeesManager(address(this), newOwner);\n        transferOwnership(newOwner);\n    }\n\n    // ============================================ REGISTER CONFIG ============================================\n\n    /**\n     * @notice Emitted when a new contract is registered.\n     * @param id value representing the unique ID tied to the new contract\n     * @param newContract address of the new contract\n     */\n    event Registered(uint256 indexed id, address indexed newContract);\n\n    /**\n     * @notice Register the address of a new contract.\n     * @param newContract address of the new contract to register\n     */\n    function register(address newContract) external onlyOwner {\n        _register(newContract);\n    }\n\n    function _register(address newContract) internal {\n        getAddress[nextId] = newContract;\n\n        emit Registered(nextId, newContract);\n\n        nextId++;\n    }\n\n    // ============================================= ADDRESS 0 LOGIC =============================================\n    /**\n     * Address 0 is the address of the ProtocolDAO, and special abilities that the owner does not have.\n     * - It can change what address is stored at address 0.\n     * - It can change the owner of this contract.\n     */\n\n    /**\n     * @notice Emitted when an ownership transition is started.\n     */\n    event OwnerTransitionStarted(address indexed pendingOwner, uint256 startTime);\n\n    /**\n     * @notice Emitted when an ownership transition is cancelled.\n     */\n    event OwnerTransitionCancelled(address indexed pendingOwner);\n\n    /**\n     * @notice Emitted when an ownership transition is completed.\n     */\n    event OwnerTransitionComplete(address indexed newOwner);\n\n    /**\n     * @notice Attempted to call a function intended for Zero Id address.\n     */\n    error Registry__OnlyCallableByZeroId();\n\n    /**\n     * @notice Attempted to transition owner to the zero address.\n     */\n    error Registry__NewOwnerCanNotBeZero();\n\n    /**\n     * @notice Attempted to perform a restricted action while ownership transition is pending.\n     */\n    error Registry__TransitionPending();\n\n    /**\n     * @notice Attempted to cancel or complete a transition when one is not active.\n     */\n    error Registry__TransitionNotPending();\n\n    /**\n     * @notice Attempted to call `completeTransition` from an address that is not the pending owner.\n     */\n    error Registry__OnlyCallableByPendingOwner();\n\n    /**\n     * @notice The amount of time it takes for an ownership transition to work.\n     */\n    uint256 public constant TRANSITION_PERIOD = 7 days;\n\n    /**\n     * @notice The Pending Owner, that becomes the owner after the transition period, and they call `completeTransition`.\n     */\n    address public pendingOwner;\n\n    /**\n     * @notice The starting time stamp of the transition.\n     */\n    uint256 public transitionStart;\n\n    /**\n     * @notice Allows Zero Id address to set a new owner, after the transition period is up.\n     */\n    function transitionOwner(address newOwner) external {\n        if (msg.sender != getAddress[0]) revert Registry__OnlyCallableByZeroId();\n        if (pendingOwner != address(0)) revert Registry__TransitionPending();\n        if (newOwner == address(0)) revert Registry__NewOwnerCanNotBeZero();\n\n        emit OwnerTransitionStarted(newOwner, transitionStart);\n\n        pendingOwner = newOwner;\n        transitionStart = block.timestamp;\n    }\n\n    /**\n     * @notice Allows Zero Id address to cancel an ongoing owner transition.\n     */\n    function cancelTransition() external {\n        address _pendingOwner = pendingOwner;\n\n        if (msg.sender != getAddress[0]) revert Registry__OnlyCallableByZeroId();\n        if (_pendingOwner == address(0)) revert Registry__TransitionNotPending();\n\n        emit OwnerTransitionCancelled(_pendingOwner);\n\n        pendingOwner = address(0);\n        transitionStart = 0;\n    }\n\n    /**\n     * @notice Allows pending owner to complete the ownership transition.\n     */\n    function completeTransition() external {\n        address _pendingOwner = pendingOwner;\n\n        if (msg.sender != _pendingOwner) revert Registry__OnlyCallableByPendingOwner();\n        if (block.timestamp < transitionStart + TRANSITION_PERIOD) revert Registry__TransitionPending();\n\n        _transferOwnership(_pendingOwner);\n\n        emit OwnerTransitionComplete(_pendingOwner);\n\n        pendingOwner = address(0);\n        transitionStart = 0;\n    }\n\n    /**\n     * @notice Extends OZ Ownable `_checkOwner` function to block owner calls, if there is an ongoing transition.\n     */\n    function _checkOwner() internal view override {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        if (transitionStart != 0) revert Registry__TransitionPending();\n    }\n\n    // ============================================ Flashloan LOGIC ============================================\n\n    /**\n     * @notice Emitted when a flashloan source is added or removed\n     */\n    event FlashLoanSourceChanged(address indexed source, bool state);\n\n    /**\n     * @notice Allows to set or remove a flashloan source\n     * @param source Address from which the flashloan can be received\n     * @param state The authorization state\n     */\n    function setApprovedFlashLoanSource(address source, bool state) external onlyOwner {\n        approvedFlashLoanSource[source] = state;\n        emit FlashLoanSourceChanged(source, state);\n    }\n\n    // ============================================ PAUSE LOGIC ============================================\n\n    /**\n     * @notice Emitted when a target is paused.\n     */\n    event TargetPaused(address indexed target);\n\n    /**\n     * @notice Emitted when a target is unpaused.\n     */\n    event TargetUnpaused(address indexed target);\n\n    /**\n     * @notice Attempted to unpause a target that was not paused.\n     */\n    error Registry__TargetNotPaused(address target);\n\n    /**\n     * @notice Mapping stores whether or not a fund is paused.\n     */\n    mapping(address => bool) public isCallerPaused;\n\n    /**\n     * @notice Allows multisig to pause multiple funds in a single call.\n     */\n    function batchPause(address[] calldata targets) external onlyOwner {\n        for (uint256 i; i < targets.length; ++i) _pauseTarget(targets[i]);\n    }\n\n    /**\n     * @notice Allows multisig to unpause multiple funds in a single call.\n     */\n    function batchUnpause(address[] calldata targets) external onlyOwner {\n        for (uint256 i; i < targets.length; ++i) _unpauseTarget(targets[i]);\n    }\n\n    /**\n     * @notice Helper function to pause some target.\n     */\n    function _pauseTarget(address target) internal {\n        isCallerPaused[target] = true;\n        emit TargetPaused(target);\n    }\n\n    /**\n     * @notice Helper function to unpause some target.\n     */\n    function _unpauseTarget(address target) internal {\n        if (!isCallerPaused[target]) revert Registry__TargetNotPaused(target);\n        isCallerPaused[target] = false;\n        emit TargetUnpaused(target);\n    }\n\n    // ============================================ ADAPTOR LOGIC ============================================\n\n    /**\n     * @notice Emitted when an adaptor is trusted.\n     * @param adaptor address of the trusted adaptor\n     */\n    event Registry__AdaptorTrusted(address indexed adaptor);\n\n    /**\n     * @notice Emitted when an adaptor is not trusted.\n     * @param adaptor address of the distrusted adaptor\n     */\n    event Registry__AdaptorDistrusted(address indexed adaptor);\n\n    /**\n     * @notice Attempted to trust an adaptor with non unique identifier.\n     */\n    error Registry__IdentifierNotUnique();\n\n    /**\n     * @notice Attempted to use an untrusted adaptor.\n     */\n    error Registry__AdaptorNotTrusted(address adaptor);\n\n    /**\n     * @notice Attempted to trust an already trusted adaptor.\n     */\n    error Registry__AdaptorAlreadyTrusted(address adaptor);\n\n    /**\n     * @notice Maps an adaptor address to bool indicating whether it has been set up in the registry.\n     */\n    mapping(address => bool) public isAdaptorTrusted;\n\n    /**\n     * @notice Maps an adaptors identfier to bool, to track if the identifier is unique wrt the registry.\n     */\n    mapping(bytes32 => bool) public isIdentifierUsed;\n\n    /**\n     * @notice Trust an adaptor to be used by funds\n     * @param adaptor address of the adaptor to trust\n     */\n    function trustAdaptor(address adaptor) external onlyOwner {\n        if (isAdaptorTrusted[adaptor]) revert Registry__AdaptorAlreadyTrusted(adaptor);\n        bytes32 identifier = BaseAdaptor(adaptor).identifier();\n        if (isIdentifierUsed[identifier]) revert Registry__IdentifierNotUnique();\n        isAdaptorTrusted[adaptor] = true;\n        isIdentifierUsed[identifier] = true;\n        emit Registry__AdaptorTrusted(adaptor);\n    }\n\n    /**\n     * @notice Allows registry to distrust adaptors.\n     * @dev Doing so prevents Funds from adding this adaptor to their catalogue.\n     */\n    function distrustAdaptor(address adaptor) external onlyOwner {\n        if (!isAdaptorTrusted[adaptor]) revert Registry__AdaptorNotTrusted(adaptor);\n        // Set trust to false.\n        isAdaptorTrusted[adaptor] = false;\n\n        emit Registry__AdaptorDistrusted(adaptor);\n\n        // We are NOT resetting `isIdentifierUsed` because if this adaptor is distrusted, then something needs\n        // to change about the new one being re-trusted.\n    }\n\n    /**\n     * @notice Reverts if `adaptor` is not trusted by the registry.\n     */\n    function revertIfAdaptorIsNotTrusted(address adaptor) external view {\n        if (!isAdaptorTrusted[adaptor]) revert Registry__AdaptorNotTrusted(adaptor);\n    }\n\n    // ============================================ POSITION LOGIC ============================================\n    /**\n     * @notice stores data related to Fund positions.\n     * @param adaptors address of the adaptor to use for this position\n     * @param isDebt bool indicating whether this position takes on debt or not\n     * @param adaptorData arbitrary data needed to correclty set up a position\n     * @param configurationData arbitrary data settable by strategist to change fund <-> adaptor interaction\n     */\n    struct PositionData {\n        address adaptor;\n        bool isDebt;\n        bytes adaptorData;\n        bytes configurationData;\n    }\n\n    /**\n     * @notice Emitted when a new position is added to the registry.\n     * @param id the positions id\n     * @param adaptor address of the adaptor this position uses\n     * @param isDebt bool indicating whether this position takes on debt or not\n     * @param adaptorData arbitrary bytes used to configure this position\n     */\n    event Registry__PositionTrusted(uint32 indexed id, address indexed adaptor, bool isDebt, bytes adaptorData);\n\n    /**\n     * @notice Emitted when a position is distrusted.\n     * @param id the positions id\n     */\n    event Registry__PositionDistrusted(uint32 indexed id);\n\n    /**\n     * @notice Attempted to trust a position not being used.\n     * @param position address of the invalid position\n     */\n    error Registry__PositionPricingNotSetUp(address position);\n\n    /**\n     * @notice Attempted to add a position with bad input values.\n     */\n    error Registry__InvalidPositionInput();\n\n    /**\n     * @notice Attempted to add a position that does not exist.\n     */\n    error Registry__PositionDoesNotExist();\n\n    /**\n     * @notice Attempted to add a position that is not trusted.\n     */\n    error Registry__PositionIsNotTrusted(uint32 position);\n\n    /**\n     * @notice Addresses of the positions currently used by the fund.\n     */\n    uint256 public constant PRICE_ROUTER_REGISTRY_SLOT = 2;\n\n    /**\n     * @notice Maps a position hash to a position Id.\n     * @dev can be used by adaptors to verify that a certain position is open during Fund `callOnAdaptor` calls.\n     */\n    mapping(bytes32 => uint32) public getPositionHashToPositionId;\n\n    /**\n     * @notice Maps a position id to its position data.\n     * @dev used by Funds when adding new positions.\n     */\n    mapping(uint32 => PositionData) public getPositionIdToPositionData;\n\n    /**\n     * @notice Maps a position to a bool indicating whether or not it is trusted.\n     */\n    mapping(uint32 => bool) public isPositionTrusted;\n\n    /**\n     * @notice Trust a position to be used by the fund.\n     * @param positionId the position id of the newly added position\n     * @param adaptor the adaptor address this position uses\n     * @param adaptorData arbitrary bytes used to configure this position\n     */\n    function trustPosition(uint32 positionId, address adaptor, bytes memory adaptorData) external onlyOwner {\n        bytes32 identifier = BaseAdaptor(adaptor).identifier();\n        bool isDebt = BaseAdaptor(adaptor).isDebt();\n        bytes32 positionHash = keccak256(abi.encode(identifier, isDebt, adaptorData));\n\n        if (positionId == 0) revert Registry__InvalidPositionInput();\n        // Make sure positionId is not already in use.\n        PositionData storage pData = getPositionIdToPositionData[positionId];\n        if (pData.adaptor != address(0)) revert Registry__InvalidPositionInput();\n\n        // Check that...\n        // `adaptor` is a non zero address\n        // position has not been already set up\n        if (adaptor == address(0) || getPositionHashToPositionId[positionHash] != 0)\n            revert Registry__InvalidPositionInput();\n\n        if (!isAdaptorTrusted[adaptor]) revert Registry__AdaptorNotTrusted(adaptor);\n\n        // Set position data.\n        pData.adaptor = adaptor;\n        pData.isDebt = isDebt;\n        pData.adaptorData = adaptorData;\n        pData.configurationData = abi.encode(0);\n\n        // Globally trust the position.\n        isPositionTrusted[positionId] = true;\n\n        getPositionHashToPositionId[positionHash] = positionId;\n\n        // Check that assets position uses are supported for pricing operations.\n        ERC20[] memory assets = BaseAdaptor(adaptor).assetsUsed(adaptorData);\n        PriceRouter priceRouter = PriceRouter(getAddress[PRICE_ROUTER_REGISTRY_SLOT]);\n        for (uint256 i; i < assets.length; ++i) {\n            if (!priceRouter.isSupported(assets[i])) revert Registry__PositionPricingNotSetUp(address(assets[i]));\n        }\n\n        emit Registry__PositionTrusted(positionId, adaptor, isDebt, adaptorData);\n    }\n\n    /**\n     * @notice Allows registry to distrust positions.\n     * @dev Doing so prevents Funds from adding this position to their catalogue,\n     *      and adding the position to their tracked arrays.\n     */\n    function distrustPosition(uint32 positionId) external onlyOwner {\n        if (!isPositionTrusted[positionId]) revert Registry__PositionIsNotTrusted(positionId);\n        isPositionTrusted[positionId] = false;\n        emit Registry__PositionDistrusted(positionId);\n    }\n\n    /**\n     * @notice Called by Funds to add a new position to themselves.\n     * @param positionId the id of the position the fund wants to add\n     * @return adaptor the address of the adaptor, isDebt bool indicating whether position is\n     *         debt or not, and adaptorData needed to interact with position\n     */\n    function addPositionToFund(\n        uint32 positionId\n    ) external view returns (address adaptor, bool isDebt, bytes memory adaptorData) {\n        if (positionId == 0) revert Registry__PositionDoesNotExist();\n        PositionData memory positionData = getPositionIdToPositionData[positionId];\n        if (positionData.adaptor == address(0)) revert Registry__PositionDoesNotExist();\n\n        revertIfPositionIsNotTrusted(positionId);\n\n        return (positionData.adaptor, positionData.isDebt, positionData.adaptorData);\n    }\n\n    /**\n     * @notice Reverts if `positionId` is not trusted by the registry.\n     */\n    function revertIfPositionIsNotTrusted(uint32 positionId) public view {\n        if (!isPositionTrusted[positionId]) revert Registry__PositionIsNotTrusted(positionId);\n    }\n\n    // ========================================= APPROVED AGGREGATOR SPENDER LOGIC =========================================\n\n    /**\n     * @notice Emitted when an aggregator spender is changed or removed\n     */\n    event AggregatorSpenderChanged(address indexed aggregator, address indexed spender);\n\n    /**\n     * @notice Trust aggregator and add the corresponding spender\n     */\n    function changeAggregatorSpender(address aggregator, address spender) external {\n        aggregatorSpender[aggregator] = spender;\n        emit AggregatorSpenderChanged(aggregator, spender);\n    }\n\n    /**\n     * @notice Untrust aggregator and remove spender\n     */\n    function removeAggregatorSpender(address aggregator) external {\n        delete aggregatorSpender[aggregator];\n        emit AggregatorSpenderChanged(aggregator, address(0));\n    }\n\n    // ========================================== LIMIT ADAPTOR SWAP VOLUME LOGIC ==========================================\n\n    event FundTradeVolumeDataUpdated(\n        address indexed fund,\n        uint48 lastUpdate,\n        uint48 periodLength,\n        uint80 volumeInUSD,\n        uint80 maxVolumeInUSD\n    );\n\n    error Registry__FundTradingVolumeExceeded(address fund);\n    error Registry__InvalidVolumeInput();\n\n    struct FundVolumeData {\n        uint48 lastUpdate;\n        uint48 periodLength;\n        uint80 volumeInUSD; // volume per period in USD and 8 decimals (80 bits are largely sufficient)\n        uint80 maxVolumeInUSD; // volume per period in USD and 8 decimals (80 bits are largely sufficient)\n    }\n\n    mapping(address => FundVolumeData) public fundsAdaptorVolumeData;\n\n    /**\n     * @notice Updates and checks if the fund's traded volume is violated.\n     * @param volumeInUSD the volume in USD to add to the fund's traded volume.\n     * @dev If the fund volume parameters were not set, the fund won't be able to trade on reblances.\n     */\n    function checkAndUpdateFundTradeVolume(uint256 volumeInUSD) external {\n        // caller should be the fund through the swap adapters\n        FundVolumeData storage fundVolumeData = fundsAdaptorVolumeData[msg.sender];\n\n        if (fundVolumeData.maxVolumeInUSD == type(uint80).max) return;\n\n        // input sanity check\n        if (volumeInUSD > type(uint80).max) revert Registry__InvalidVolumeInput();\n\n        uint256 endPeriod;\n        unchecked {\n            endPeriod = uint256(fundVolumeData.lastUpdate) + uint256(fundVolumeData.periodLength);\n        }\n\n        if (block.timestamp > endPeriod) {\n            fundVolumeData.lastUpdate = uint48(block.timestamp);\n            fundVolumeData.volumeInUSD = uint80(volumeInUSD);\n        } else {\n            fundVolumeData.volumeInUSD += uint80(volumeInUSD);\n        }\n\n        if (fundVolumeData.volumeInUSD > fundVolumeData.maxVolumeInUSD)\n            revert Registry__FundTradingVolumeExceeded(msg.sender);\n\n        emit FundTradeVolumeDataUpdated(\n            msg.sender,\n            fundVolumeData.lastUpdate,\n            fundVolumeData.periodLength,\n            fundVolumeData.volumeInUSD,\n            fundVolumeData.maxVolumeInUSD\n        );\n    }\n\n    /**\n     * @notice Set the max allowed volume for an adaptor to trade in a period.\n     * @param fund the address of the fund\n     * @param periodLength the length of the period in seconds\n     * @param maxVolumeInUSD the max volume an adaptor can trade in a period\n     * @param resetVolume reset or not the current volume an adaptor has traded\n     */\n    function setMaxAllowedAdaptorVolumeParams(\n        address fund,\n        uint48 periodLength,\n        uint80 maxVolumeInUSD,\n        bool resetVolume\n    ) external onlyOwner {\n        // there is no explicit limit on volume since it can depend on the size of the fund,\n        // as well as the strategies involved\n        FundVolumeData storage fundVolumeData = fundsAdaptorVolumeData[fund];\n        fundVolumeData.periodLength = periodLength;\n        fundVolumeData.maxVolumeInUSD = maxVolumeInUSD;\n\n        if (resetVolume) {\n            fundVolumeData.lastUpdate = uint48(block.timestamp);\n            fundVolumeData.volumeInUSD = 0;\n        }\n\n        emit FundTradeVolumeDataUpdated(\n            fund,\n            fundVolumeData.lastUpdate,\n            periodLength,\n            fundVolumeData.volumeInUSD,\n            maxVolumeInUSD\n        );\n    }\n}\n"
      },
      "src/base/Fund.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { Math } from \"src/utils/Math.sol\";\nimport { ERC4626, ERC20 } from \"src/base/ERC4626.sol\";\nimport { SafeTransferLib } from \"@solmate/utils/SafeTransferLib.sol\";\nimport { Registry } from \"src/Registry.sol\";\nimport { PriceRouter } from \"src/modules/price-router/PriceRouter.sol\";\nimport { Uint32Array } from \"src/utils/Uint32Array.sol\";\nimport { BaseAdaptor } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { Owned } from \"@solmate/auth/Owned.sol\";\nimport { FeesManager } from \"src/modules/fees/FeesManager.sol\";\n\n/**\n * @title Swaap Fund\n * @notice A composable ERC4626 that can use arbitrary DeFi assets/positions using adaptors.\n * @dev Forked from https://github.com/PeggyJV/cellar-contracts\n */\ncontract Fund is ERC4626, Ownable {\n    using Uint32Array for uint32[];\n    using SafeTransferLib for ERC20;\n    using Math for uint256;\n    using Address for address;\n\n    // ========================================= One Slot Values =========================================\n    // Below values are frequently accessed in the same TXs. By moving them to the top\n    // they will be stored in the same slot, reducing cold access reads.\n\n    /**\n     * @notice The maximum amount of shares that can be in circulation.\n     * @dev Can be increase or decreased by Fund's Owner.\n     */\n    uint192 public shareSupplyCap;\n\n    /**\n     * @notice `locked` is public, so that the state can be checked even during view function calls.\n     */\n    bool public locked;\n\n    /**\n     * @notice Whether or not the contract is shutdown in case of an emergency.\n     */\n    bool public isShutdown;\n\n    /**\n     * @notice This bool is used to stop rebalancers from abusing Base Adaptor functions(deposit/withdraw).\n     */\n    bool public blockExternalReceiver;\n\n    /**\n     * @notice Stores the position id of the holding position in the creditPositions array.\n     */\n    uint32 public holdingPosition;\n\n    /**\n     * @notice Sets the end date when the fund pause mode will be disregarded whatever its state.\n     */\n    uint256 public immutable endPauseTimestamp;\n\n    // ========================================= MULTICALL =========================================\n\n    /**\n     * @notice Allows caller to call multiple functions in a single TX.\n     * @dev Does NOT return the function return values.\n     */\n    function multicall(bytes[] calldata data) external {\n        for (uint256 i; i < data.length; ++i) address(this).functionDelegateCall(data[i]);\n    }\n\n    // ========================================= REENTRANCY GUARD =========================================\n\n    error Fund__Reentrancy();\n\n    function _revertWhenReentrant() internal view {\n        if (locked) revert Fund__Reentrancy();\n    }\n\n    function _nonReentrantAfter() internal {\n        locked = false;\n    }\n\n    function _nonReentrantBefore() internal {\n        _revertWhenReentrant();\n        locked = true;\n    }\n\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    // ========================================= PRICE ROUTER CACHE =========================================\n    /// @dev Represents 100% in basis points, where 1 basis point is 1/100th of 1%.\n    uint16 internal constant _BPS_ONE_HUNDRED_PER_CENT = 1e4;\n\n    /**\n     * @notice Cached price router contract.\n     * @dev This way fund has to \"opt in\" to price router changes.\n     */\n    PriceRouter public priceRouter;\n\n    /**\n     * @notice Updates the fund to use the latest price router in the registry.\n     * @param checkTotalAssets If true totalAssets is checked before and after updating the price router,\n     *        and is verified to be withing a +- 5% envelope.\n     *        If false totalAssets is only called after updating the price router.]\n     * @param allowableRange The +- range the total assets may deviate between the old and new price router.\n     *                       - 1_000 == 10%\n     *                       - 500 == 5%\n     * @param expectedPriceRouter The registry price router differed from the expected price router.\n     * @dev `allowableRange` reverts from arithmetic underflow if it is greater than 10_000, this is\n     *      desired behavior.\n     * @dev Callable by the Fund's owner.\n     */\n    function cachePriceRouter(\n        bool checkTotalAssets,\n        uint16 allowableRange,\n        address expectedPriceRouter\n    ) external onlyOwner {\n        uint256 minAssets;\n        uint256 maxAssets;\n\n        if (checkTotalAssets) {\n            uint256 assetsBefore = totalAssets();\n            minAssets = assetsBefore.mulDivDown(_BPS_ONE_HUNDRED_PER_CENT - allowableRange, _BPS_ONE_HUNDRED_PER_CENT);\n            maxAssets = assetsBefore.mulDivDown(_BPS_ONE_HUNDRED_PER_CENT + allowableRange, _BPS_ONE_HUNDRED_PER_CENT);\n        }\n\n        // Make sure expected price router is equal to price router grabbed from registry.\n        _checkRegistryAddressAgainstExpected(_PRICE_ROUTER_REGISTRY_SLOT, expectedPriceRouter);\n\n        priceRouter = PriceRouter(expectedPriceRouter);\n\n        if (checkTotalAssets) {\n            uint256 assetsAfter = totalAssets();\n            if (assetsAfter < minAssets || assetsAfter > maxAssets)\n                revert Fund__TotalAssetDeviatedOutsideRange(assetsAfter, minAssets, maxAssets);\n        }\n    }\n\n    // ========================================= POSITIONS CONFIG =========================================\n\n    /**\n     * @notice Emitted when a position is added.\n     * @param position id of position that was added\n     * @param index index that position was added at\n     */\n    event PositionAdded(uint32 position, uint256 index);\n\n    /**\n     * @notice Emitted when a position is removed.\n     * @param position id of position that was removed\n     * @param index index that position was removed from\n     */\n    event PositionRemoved(uint32 position, uint256 index);\n\n    /**\n     * @notice Emitted when the positions at two indexes are swapped.\n     * @param newPosition1 id of position (previously at index2) that replaced index1.\n     * @param newPosition2 id of position (previously at index1) that replaced index2.\n     * @param index1 index of first position involved in the swap\n     * @param index2 index of second position involved in the swap.\n     */\n    event PositionSwapped(uint32 newPosition1, uint32 newPosition2, uint256 index1, uint256 index2);\n\n    /**\n     * @notice Emitted when owner adds/removes a position to/from the funds catalogue.\n     */\n    event PositionCatalogueAltered(uint32 positionId, bool inCatalogue);\n\n    /**\n     * @notice Emitted when owner adds/removes an adaptor to/from the funds catalogue.\n     */\n    event AdaptorCatalogueAltered(address adaptor, bool inCatalogue);\n\n    /**\n     * @notice Attempted to add a position that is already being used.\n     * @param position id of the position\n     */\n    error Fund__PositionAlreadyUsed(uint32 position);\n\n    /**\n     * @notice Attempted to make an unused position the holding position.\n     * @param position id of the position\n     */\n    error Fund__PositionNotUsed(uint32 position);\n\n    /**\n     * @notice Attempted to add a position that is not in the catalogue.\n     * @param position id of the position\n     */\n    error Fund__PositionNotInCatalogue(uint32 position);\n\n    /**\n     * @notice Attempted an action on a position that is required to be empty before the action can be performed.\n     * @param position address of the non-empty position\n     * @param sharesRemaining amount of shares remaining in the position\n     */\n    error Fund__PositionNotEmpty(uint32 position, uint256 sharesRemaining);\n\n    /**\n     * @notice Attempted an operation with an asset that was different then the one expected.\n     * @param asset address of the asset\n     * @param expectedAsset address of the expected asset\n     */\n    error Fund__AssetMismatch(address asset, address expectedAsset);\n\n    /**\n     * @notice Attempted to add a position when the position array is full.\n     * @param maxPositions maximum number of positions that can be used\n     */\n    error Fund__PositionArrayFull(uint256 maxPositions);\n\n    /**\n     * @notice Attempted to add a position, with mismatched debt.\n     * @param position the posiiton id that was mismatched\n     */\n    error Fund__DebtMismatch(uint32 position);\n\n    /**\n     * @notice Attempted to remove the Funds holding position.\n     */\n    error Fund__RemovingHoldingPosition();\n\n    /**\n     * @notice Attempted to remove an index with an unexpected positionId.\n     */\n    error Fund__WrongPositionId();\n\n    /**\n     * @notice Attempted to add an invalid holding position.\n     * @param positionId the id of the invalid position.\n     */\n    error Fund__InvalidHoldingPosition(uint32 positionId);\n\n    /**\n     * @notice Attempted to force out the wrong position.\n     */\n    error Fund__FailedToForceOutPosition();\n\n    /**\n     * @notice Array of uint32s made up of funds credit positions Ids.\n     */\n    uint32[] public creditPositions;\n\n    /**\n     * @notice Array of uint32s made up of funds debt positions Ids.\n     */\n    uint32[] public debtPositions;\n\n    /**\n     * @notice Tell whether a position is currently used.\n     */\n    mapping(uint256 => bool) public isPositionUsed;\n\n    /**\n     * @notice Get position data given position id.\n     */\n    mapping(uint32 => Registry.PositionData) public getPositionData;\n\n    /**\n     * @notice Get the ids of the credit positions currently used by the fund.\n     */\n    function getCreditPositions() external view returns (uint32[] memory) {\n        return creditPositions;\n    }\n\n    /**\n     * @notice Get the ids of the debt positions currently used by the fund.\n     */\n    function getDebtPositions() external view returns (uint32[] memory) {\n        return debtPositions;\n    }\n\n    /**\n     * @notice Maximum amount of positions a fund can have in its credit/debt arrays.\n     */\n    uint256 internal constant _MAX_POSITIONS = 32;\n\n    /**\n     * @notice Allows owner to change the holding position.\n     * @dev Callable by the Fund's owner.\n     */\n    function setHoldingPosition(uint32 positionId) public onlyOwner {\n        if (!isPositionUsed[positionId]) revert Fund__PositionNotUsed(positionId);\n        if (_assetOf(positionId) != asset) revert Fund__AssetMismatch(address(asset), address(_assetOf(positionId)));\n        if (getPositionData[positionId].isDebt) revert Fund__InvalidHoldingPosition(positionId);\n        holdingPosition = positionId;\n    }\n\n    /**\n     * @notice Positions the rebalancers can use.\n     */\n    mapping(uint32 => bool) public positionCatalogue;\n\n    /**\n     * @notice Adaptors the rebalancers can use.\n     */\n    mapping(address => bool) public adaptorCatalogue;\n\n    /**\n     * @notice Allows the Owner to add positions to this fund's catalogue.\n     * @dev Callable by the Fund's owner.\n     */\n    function addPositionToCatalogue(uint32 positionId) public onlyOwner {\n        // Make sure position is not paused and is trusted.\n        registry.revertIfPositionIsNotTrusted(positionId);\n        positionCatalogue[positionId] = true;\n        emit PositionCatalogueAltered(positionId, true);\n    }\n\n    /**\n     * @notice Allows owner to remove positions from this fund's catalogue.\n     * @dev Callable by the Fund's owner.\n     */\n    function removePositionFromCatalogue(uint32 positionId) external onlyOwner {\n        positionCatalogue[positionId] = false;\n        emit PositionCatalogueAltered(positionId, false);\n    }\n\n    /**\n     * @notice Allows owner to add adaptors to this fund's catalogue.\n     * @dev Callable by the Fund's owner.\n     */\n    function addAdaptorToCatalogue(address adaptor) external onlyOwner {\n        // Make sure adaptor is not paused and is trusted.\n        registry.revertIfAdaptorIsNotTrusted(adaptor);\n        adaptorCatalogue[adaptor] = true;\n        emit AdaptorCatalogueAltered(adaptor, true);\n    }\n\n    /**\n     * @notice Allows owner to remove adaptors from this fund's catalogue.\n     * @dev Callable by the Fund's owner.\n     */\n    function removeAdaptorFromCatalogue(address adaptor) external onlyOwner {\n        adaptorCatalogue[adaptor] = false;\n        emit AdaptorCatalogueAltered(adaptor, false);\n    }\n\n    /**\n     * @notice Insert a trusted position to the list of positions used by the fund at a given index.\n     * @param index index at which to insert the position\n     * @param positionId id of position to add\n     * @param configurationData data used to configure how the position behaves\n     * @dev Callable by the Fund's owner.\n     */\n    function addPosition(\n        uint32 index,\n        uint32 positionId,\n        bytes memory configurationData,\n        bool inDebtArray\n    ) public onlyOwner {\n        _whenNotShutdown();\n\n        // Check if position is already being used.\n        if (isPositionUsed[positionId]) revert Fund__PositionAlreadyUsed(positionId);\n\n        // Check if position is in the position catalogue.\n        if (!positionCatalogue[positionId]) revert Fund__PositionNotInCatalogue(positionId);\n\n        // Grab position data from registry.\n        // Also checks if position is not trusted and reverts if so.\n        (address adaptor, bool isDebt, bytes memory adaptorData) = registry.addPositionToFund(positionId);\n\n        if (isDebt != inDebtArray) revert Fund__DebtMismatch(positionId);\n\n        // Copy position data from registry to here.\n        getPositionData[positionId] = Registry.PositionData({\n            adaptor: adaptor,\n            isDebt: isDebt,\n            adaptorData: adaptorData,\n            configurationData: configurationData\n        });\n\n        if (isDebt) {\n            if (debtPositions.length >= _MAX_POSITIONS) revert Fund__PositionArrayFull(_MAX_POSITIONS);\n            // Add new position at a specified index.\n            debtPositions.add(index, positionId);\n        } else {\n            if (creditPositions.length >= _MAX_POSITIONS) revert Fund__PositionArrayFull(_MAX_POSITIONS);\n            // Add new position at a specified index.\n            creditPositions.add(index, positionId);\n        }\n\n        isPositionUsed[positionId] = true;\n\n        emit PositionAdded(positionId, index);\n    }\n\n    /**\n     * @notice Remove the position at a given index from the list of positions used by the fund.\n     * @dev Callable by the Fund's owner.\n     * @param index index at which to remove the position\n     */\n    function removePosition(uint32 index, uint32 expectedPositionId, bool inDebtArray) external onlyOwner {\n        // Get position being removed.\n        uint32 positionId = inDebtArray ? debtPositions[index] : creditPositions[index];\n\n        if (expectedPositionId != positionId) revert Fund__WrongPositionId();\n\n        // Only remove position if it is empty, and if it is not the holding position.\n        uint256 positionBalance = _balanceOf(positionId);\n        if (positionBalance > 0) revert Fund__PositionNotEmpty(positionId, positionBalance);\n\n        _removePosition(index, positionId, inDebtArray);\n    }\n\n    /**\n     * @notice Allows Fund's owner to forceably remove a position from the Fund without checking its balance is zero.\n     * @dev Callable by the Fund's owner.\n     */\n    function forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray) external onlyOwner {\n        // Get position being removed.\n        uint32 _positionId = inDebtArray ? debtPositions[index] : creditPositions[index];\n        // Make sure position id right, and is distrusted.\n        if (positionId != _positionId || registry.isPositionTrusted(positionId))\n            revert Fund__FailedToForceOutPosition();\n\n        _removePosition(index, positionId, inDebtArray);\n    }\n\n    /**\n     * @notice Internal helper function to remove positions from funds tracked arrays.\n     */\n    function _removePosition(uint32 index, uint32 positionId, bool inDebtArray) internal {\n        if (positionId == holdingPosition) revert Fund__RemovingHoldingPosition();\n\n        if (inDebtArray) {\n            // Remove position at the given index.\n            debtPositions.remove(index);\n        } else {\n            creditPositions.remove(index);\n        }\n\n        isPositionUsed[positionId] = false;\n        delete getPositionData[positionId];\n\n        emit PositionRemoved(positionId, index);\n    }\n\n    /**\n     * @notice Swap the positions at two given indexes.\n     * @param index1 index of first position to swap\n     * @param index2 index of second position to swap\n     * @param inDebtArray bool indicating to switch positions in the debt array, or the credit array.\n     * @dev Callable by the Fund's owner.\n     */\n    function swapPositions(uint32 index1, uint32 index2, bool inDebtArray) external onlyOwner {\n        // Get the new positions that will be at each index.\n        uint32 newPosition1;\n        uint32 newPosition2;\n\n        if (inDebtArray) {\n            newPosition1 = debtPositions[index2];\n            newPosition2 = debtPositions[index1];\n            // Swap positions.\n            (debtPositions[index1], debtPositions[index2]) = (newPosition1, newPosition2);\n        } else {\n            newPosition1 = creditPositions[index2];\n            newPosition2 = creditPositions[index1];\n            // Swap positions.\n            (creditPositions[index1], creditPositions[index2]) = (newPosition1, newPosition2);\n        }\n\n        emit PositionSwapped(newPosition1, newPosition2, index1, index2);\n    }\n\n    // =========================================== EMERGENCY LOGIC ===========================================\n\n    /**\n     * @notice Emitted when fund emergency state is changed.\n     * @param isShutdown whether the fund is shutdown\n     */\n    event ShutdownChanged(bool isShutdown);\n\n    /**\n     * @notice Attempted action was prevented due to contract being shutdown.\n     */\n    error Fund__ContractShutdown();\n\n    /**\n     * @notice Attempted action was prevented due to contract not being shutdown.\n     */\n    error Fund__ContractNotShutdown();\n\n    /**\n     * @notice Attempted to interact with the fund when it is paused.\n     */\n    error Fund__Paused();\n\n    /**\n     * @notice View function external contracts can use to see if the fund is paused.\n     */\n    function isPaused() public view returns (bool) {\n        if (block.timestamp < endPauseTimestamp) {\n            return registry.isCallerPaused(address(this));\n        }\n        return false;\n    }\n\n    /**\n     * @notice Pauses all user entry/exits, and rebalances.\n     */\n    function _whenNotPaused() internal view {\n        if (isPaused()) revert Fund__Paused();\n    }\n\n    /**\n     * @notice Prevent a function from being called during a shutdown.\n     */\n    function _whenNotShutdown() internal view {\n        if (isShutdown) revert Fund__ContractShutdown();\n    }\n\n    /**\n     * @notice Shutdown the fund. Used in an emergency or if the fund has been deprecated.\n     * @dev Callable by the Fund's owner.\n     */\n    function initiateShutdown() external onlyOwner {\n        _whenNotShutdown();\n        isShutdown = true;\n\n        emit ShutdownChanged(true);\n    }\n\n    /**\n     * @notice Restart the fund.\n     * @dev Callable by the Fund's owner.\n     */\n    function liftShutdown() external onlyOwner {\n        if (!isShutdown) revert Fund__ContractNotShutdown();\n        isShutdown = false;\n\n        emit ShutdownChanged(false);\n    }\n\n    // =========================================== CONSTRUCTOR ===========================================\n\n    /**\n     * @notice Delay between the creation of the fund and the end of the pause period.\n     */\n    uint256 internal constant _DELAY_UNTIL_END_PAUSE = 30 days * 9; // 9 months\n\n    /**\n     * @notice Id to get the price router from the registry.\n     */\n    uint256 internal constant _PRICE_ROUTER_REGISTRY_SLOT = 2;\n\n    /**\n     * @notice The minimum amount of shares to be minted in the contructor.\n     */\n    uint256 internal constant _MINIMUM_CONSTRUCTOR_MINT = 1e4;\n\n    uint8 internal constant _FUND_DECIMALS = 18;\n\n    /**\n     * @notice Attempted to deploy contract without minting enough shares.\n     */\n    error Fund__MinimumConstructorMintNotMet();\n\n    /**\n     * @notice Address of the platform's registry contract. Used to get the latest address of modules.\n     */\n    Registry public immutable registry;\n\n    uint8 internal immutable _ASSET_DECIMALS;\n\n    /**\n     * @notice Address of the fees manager contract.\n     */\n    FeesManager public immutable FEES_MANAGER;\n\n    /**\n     * @dev Owner should be set to the ProtocolDAO\n     * @param _registry address of the platform's registry contract\n     * @param _asset address of underlying token used for the for accounting, depositing, and withdrawing\n     * @param _name name of this fund's share token\n     * @param _symbol symbol of this fund's share token\n     * @param _holdingPosition the holding position of the Fund\n     *        must use a position that does NOT call back to fund on use(Like ERC20 positions).\n     * @param _holdingPositionConfig configuration data for holding position\n     * @param _initialDeposit initial amount of assets to deposit into the Fund\n     * @param _shareSupplyCap starting share supply cap\n     */\n    constructor(\n        address _owner,\n        Registry _registry,\n        ERC20 _asset,\n        string memory _name,\n        string memory _symbol,\n        uint32 _holdingPosition,\n        bytes memory _holdingPositionConfig,\n        uint256 _initialDeposit,\n        uint192 _shareSupplyCap\n    ) ERC4626(_asset) ERC20(_name, _symbol, _FUND_DECIMALS) Ownable() {\n        endPauseTimestamp = block.timestamp + _DELAY_UNTIL_END_PAUSE;\n        registry = _registry;\n        priceRouter = PriceRouter(_registry.getAddress(_PRICE_ROUTER_REGISTRY_SLOT));\n\n        // Initialize holding position.\n        addPositionToCatalogue(_holdingPosition);\n        addPosition(0, _holdingPosition, _holdingPositionConfig, false);\n        setHoldingPosition(_holdingPosition);\n\n        // Update Share Supply Cap.\n        shareSupplyCap = _shareSupplyCap;\n\n        if (_initialDeposit < _MINIMUM_CONSTRUCTOR_MINT) revert Fund__MinimumConstructorMintNotMet();\n\n        // Deposit into Fund, and mint shares to Deployer address.\n        _asset.safeTransferFrom(_owner, address(this), _initialDeposit);\n        // Set the share price as 1:1 * 10**(fund.decimals - asset.decimals) with underlying asset.\n        _ASSET_DECIMALS = _asset.decimals(); // reverts if asset decimals > fund decimals\n        _mint(msg.sender, _initialDeposit * (10 ** (_FUND_DECIMALS - _ASSET_DECIMALS)));\n        // Deposit _initialDeposit into holding position.\n        _depositTo(_holdingPosition, _initialDeposit);\n\n        FEES_MANAGER = _registry.FEES_MANAGER();\n\n        transferOwnership(_owner);\n    }\n\n    // =========================================== CORE LOGIC ===========================================\n\n    /**\n     * @notice Attempted an action with zero shares.\n     */\n    error Fund__ZeroShares();\n\n    /**\n     * @notice Attempted an action with zero assets.\n     */\n    error Fund__ZeroAssets();\n\n    /**\n     * @notice Withdraw did not withdraw all assets.\n     * @param assetsOwed the remaining assets owed that were not withdrawn.\n     */\n    error Fund__IncompleteWithdraw(uint256 assetsOwed);\n\n    /**\n     * @notice called at the beginning of deposit.\n     */\n    function beforeDeposit(uint256, uint256, address) internal view virtual {\n        _whenNotShutdown();\n        _whenNotPaused();\n    }\n\n    /**\n     * @notice called at the end of deposit.\n     * @param assets amount of assets deposited by user.\n     */\n    function afterDeposit(uint256 assets, uint256, address) internal virtual {\n        _depositTo(holdingPosition, assets);\n    }\n\n    /**\n     * @notice called at the beginning of withdraw.\n     */\n    function beforeWithdraw(uint256, uint256, address, address) internal view virtual {\n        _whenNotPaused();\n    }\n\n    /**\n     * @notice Called when users enter the fund via deposit or mint.\n     */\n    function _enter(uint256 assets, uint256 shares, address receiver) internal {\n        beforeDeposit(assets, shares, receiver);\n\n        // Need to transfer before minting or ERC777s could reenter.\n        asset.safeTransferFrom(msg.sender, address(this), assets);\n\n        _mint(receiver, shares);\n\n        emit Deposit(msg.sender, receiver, assets, shares);\n\n        afterDeposit(assets, shares, receiver);\n    }\n\n    /**\n     * @notice Deposits assets into the fund, and returns shares to receiver.\n     * @param assets amount of assets deposited by user.\n     * @param receiver address to receive the shares.\n     * @return shares amount of shares given for deposit.\n     */\n    function deposit(uint256 assets, address receiver) public virtual override nonReentrant returns (uint256 shares) {\n        // the total supply is the equivalent of total shares after applying the performance and management fees\n        (uint256 _totalAssets, uint256 _totalSupply) = _collectFeesAndGetTotalAssetsAndTotalSupply(true);\n\n        if ((shares = _convertToShares(assets, _totalAssets, _totalSupply)) == 0) revert Fund__ZeroShares();\n\n        if ((_totalSupply + shares) > shareSupplyCap) revert Fund__ShareSupplyCapExceeded();\n\n        _enter(assets, shares, receiver);\n    }\n\n    /**\n     * @notice Mints shares from the fund, and returns shares to receiver.\n     * @param shares amount of shares requested by user.\n     * @param receiver address to receive the shares.\n     * @return assets amount of assets deposited into the fund.\n     */\n    function mint(uint256 shares, address receiver) public virtual override nonReentrant returns (uint256 assets) {\n        // the total supply is the equivalent of total shares after applying the performance and management fees\n        (uint256 _totalAssets, uint256 _totalSupply) = _collectFeesAndGetTotalAssetsAndTotalSupply(true);\n\n        // previewMint rounds up, but initial mint could return zero assets, so check for rounding error.\n        if ((assets = _previewMint(shares, _totalAssets, _totalSupply)) == 0) revert Fund__ZeroAssets();\n\n        if ((_totalSupply + shares) > shareSupplyCap) revert Fund__ShareSupplyCapExceeded();\n\n        _enter(assets, shares, receiver);\n    }\n\n    /**\n     * @notice Called when users exit the fund via withdraw or redeem.\n     */\n    function _exit(uint256 assets, uint256 shares, address receiver, address owner) internal {\n        beforeWithdraw(assets, shares, receiver, owner);\n\n        if (msg.sender != owner) {\n            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.\n\n            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;\n        }\n\n        _burn(owner, shares);\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n        _withdrawInOrder(assets, receiver);\n\n        /// @notice `afterWithdraw` is currently not used.\n        // afterWithdraw(assets, shares, receiver, owner);\n    }\n\n    /**\n     * @notice Withdraw assets from the fund by redeeming shares.\n     * @dev Unlike conventional ERC4626 contracts, this may not always return one asset to the receiver.\n     *      Since there are no swaps involved in this function, the receiver may receive multiple\n     *      assets. The value of all the assets returned will be equal to the amount defined by\n     *      `assets` denominated in the `asset` of the fund (eg. if `asset` is USDC and `assets`\n     *      is 1000, then the receiver will receive $1000 worth of assets in either one or many\n     *      tokens).\n     * @param assets equivalent value of the assets withdrawn, denominated in the fund's asset\n     * @param receiver address that will receive withdrawn assets\n     * @param owner address that owns the shares being redeemed\n     * @return shares amount of shares redeemed\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    ) public override nonReentrant returns (uint256 shares) {\n        // the total supply is the equivalent of total shares after applying the performance and management fees\n        (uint256 _totalAssets, uint256 _totalSupply) = _collectFeesAndGetTotalAssetsAndTotalSupply(false);\n\n        // No need to check for rounding error, `previewWithdraw` rounds up.\n        shares = _previewWithdraw(assets, _totalAssets, _totalSupply);\n\n        _exit(assets, shares, receiver, owner);\n    }\n\n    /**\n     * @notice Redeem shares to withdraw assets from the fund.\n     * @dev Unlike conventional ERC4626 contracts, this may not always return one asset to the receiver.\n     *      Since there are no swaps involved in this function, the receiver may receive multiple\n     *      assets. The value of all the assets returned will be equal to the amount defined by\n     *      `assets` denominated in the `asset` of the fund (eg. if `asset` is USDC and `assets`\n     *      is 1000, then the receiver will receive $1000 worth of assets in either one or many\n     *      tokens).\n     * @param shares amount of shares to redeem\n     * @param receiver address that will receive withdrawn assets\n     * @param owner address that owns the shares being redeemed\n     * @return assets equivalent value of the assets withdrawn, denominated in the fund's asset\n     */\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    ) public override nonReentrant returns (uint256 assets) {\n        // the total supply is the equivalent of total shares after applying the performance and management fees\n        (uint256 _totalAssets, uint256 _totalSupply) = _collectFeesAndGetTotalAssetsAndTotalSupply(false);\n\n        if ((assets = _convertToAssets(shares, _totalAssets, _totalSupply)) == 0) revert Fund__ZeroAssets();\n\n        _exit(assets, shares, receiver, owner);\n    }\n\n    /**\n     * @notice Called at the beginning of `previewDeposit`, `previewMint`, `previewWithdraw` and `previewRedeem`.\n     * @return _totalAssets the virtual total assets in the fund after fees if any\n     * @return _totalSupply the virtual total supply of shares after fees if any\n     */\n    function _previewTotalAssetsAndTotalSupplyAfterFees(\n        bool _isEntering\n    ) internal view virtual returns (uint256, uint256) {\n        uint256 _totalAssets = _calculateTotalAssets();\n        uint256 _totalSupply = totalSupply;\n\n        if (isShutdown) {\n            return (_totalAssets, _totalSupply);\n        }\n\n        (uint16 _enterOrExitFeesRate, uint256 _feesAsShares) = FEES_MANAGER.previewApplyFeesBeforeJoinExit(\n            _totalAssets,\n            _totalSupply,\n            _isEntering\n        );\n\n        _totalAssets = _applyEnterOrExitFees(_totalAssets, _enterOrExitFeesRate, _isEntering);\n\n        return (_totalAssets, _totalSupply + _feesAsShares);\n    }\n\n    /**\n     * @notice Collect fees from the fund.\n     * @dev Callable by anyone (permissionless).\n     */\n    function collectFees() external nonReentrant {\n        _whenNotPaused();\n        _collectFeesAndGetTotalAssetsAndTotalSupply(false);\n    }\n\n    /**\n     * @notice Called at the beginning of `deposit`, `mint`, `withdraw` and `redeem`.\n     * @dev This function is called before the fund applies fees.\n     * @return _totalAssets the virtual total assets in the fund after enter or exit fees\n     * @return _totalSupply the total supply of shares after management and performance fees\n     */\n    function _collectFeesAndGetTotalAssetsAndTotalSupply(bool _isEntering) internal virtual returns (uint256, uint256) {\n        uint256 _totalAssets = _calculateTotalAssets();\n        uint256 _totalSupply = totalSupply;\n\n        if (isShutdown) {\n            return (_totalAssets, _totalSupply);\n        }\n\n        try FEES_MANAGER.applyFeesBeforeJoinExit(_totalAssets, _totalSupply, _isEntering) returns (\n            uint16 _enterOrExitFeesRate,\n            uint256 _feesAsShares\n        ) {\n            if (_feesAsShares > 0) {\n                _mint(address(FEES_MANAGER), _feesAsShares);\n                _totalSupply += _feesAsShares;\n            }\n\n            _totalAssets = _applyEnterOrExitFees(_totalAssets, _enterOrExitFeesRate, _isEntering);\n\n            return (_totalAssets, _totalSupply);\n        } catch {\n            // If fees fail to apply, return with 0 fees. (it should not happen in normal cases)\n            return (_totalAssets, _totalSupply);\n        }\n    }\n\n    /// @return virtualTotalAssets the virtual total assets after applying enter or exit fees\n    function _applyEnterOrExitFees(\n        uint256 _totalAssets,\n        uint16 _enterOrExitFeeRate,\n        bool _isEntering\n    ) internal pure returns (uint256) {\n        if (_enterOrExitFeeRate == 0) {\n            return _totalAssets;\n        }\n\n        if (_isEntering) {\n            return _totalAssets.mulDivUp(_BPS_ONE_HUNDRED_PER_CENT + _enterOrExitFeeRate, _BPS_ONE_HUNDRED_PER_CENT);\n        }\n\n        return _totalAssets.mulDivDown(_BPS_ONE_HUNDRED_PER_CENT - _enterOrExitFeeRate, _BPS_ONE_HUNDRED_PER_CENT);\n    }\n\n    /**\n     * @notice Struct used in `_withdrawInOrder` in order to hold multiple pricing values in a single variable.\n     * @dev Prevents stack too deep errors.\n     */\n    struct WithdrawPricing {\n        uint256 priceBaseUSD;\n        uint256 oneBase;\n        uint256 priceQuoteUSD;\n        uint256 oneQuote;\n    }\n\n    /**\n     * @notice Multipler used to insure calculations use very high precision.\n     */\n    uint256 private constant PRECISION_MULTIPLIER = 1e18;\n\n    /**\n     * @dev Withdraw from positions in the order defined by `positions`.\n     * @param assets the amount of assets to withdraw from fund\n     * @param receiver the address to sent withdrawn assets to\n     * @dev Only loop through credit array because debt can not be withdraw by users.\n     */\n    function _withdrawInOrder(uint256 assets, address receiver) internal {\n        // Save asset price in USD, and decimals to reduce external calls.\n        WithdrawPricing memory pricingInfo;\n        pricingInfo.priceQuoteUSD = priceRouter.getPriceInUSD(asset);\n        pricingInfo.oneQuote = 10 ** _ASSET_DECIMALS;\n        uint256 creditLength = creditPositions.length;\n        for (uint256 i; i < creditLength; ++i) {\n            uint32 position = creditPositions[i];\n            uint256 withdrawableBalance = _withdrawableFrom(position);\n            // Move on to next position if this one is empty.\n            if (withdrawableBalance == 0) continue;\n            ERC20 positionAsset = _assetOf(position);\n\n            pricingInfo.priceBaseUSD = priceRouter.getPriceInUSD(positionAsset);\n            pricingInfo.oneBase = 10 ** positionAsset.decimals();\n            uint256 totalWithdrawableBalanceInAssets;\n            {\n                uint256 withdrawableBalanceInUSD = (PRECISION_MULTIPLIER * withdrawableBalance).mulDivDown(\n                    pricingInfo.priceBaseUSD,\n                    pricingInfo.oneBase\n                );\n                totalWithdrawableBalanceInAssets = withdrawableBalanceInUSD.mulDivDown(\n                    pricingInfo.oneQuote,\n                    pricingInfo.priceQuoteUSD\n                );\n                totalWithdrawableBalanceInAssets = totalWithdrawableBalanceInAssets / PRECISION_MULTIPLIER;\n            }\n\n            // We want to pull as much as we can from this position, but no more than needed.\n            uint256 amount;\n\n            if (totalWithdrawableBalanceInAssets > assets) {\n                // Convert assets into position asset.\n                uint256 assetsInUSD = (PRECISION_MULTIPLIER * assets).mulDivDown(\n                    pricingInfo.priceQuoteUSD,\n                    pricingInfo.oneQuote\n                );\n                amount = assetsInUSD.mulDivDown(pricingInfo.oneBase, pricingInfo.priceBaseUSD);\n                amount = amount / PRECISION_MULTIPLIER;\n                assets = 0;\n            } else {\n                amount = withdrawableBalance;\n                assets = assets - totalWithdrawableBalanceInAssets;\n            }\n\n            // Withdraw from position.\n            _withdrawFrom(position, amount, receiver);\n\n            // Stop if no more assets to withdraw.\n            if (assets == 0) break;\n        }\n        // If withdraw did not remove all assets owed, revert.\n        if (assets > 0) revert Fund__IncompleteWithdraw(assets);\n    }\n\n    // ========================================= ACCOUNTING LOGIC =========================================\n\n    function _calculateTotalWithdrawableAssets() internal view returns (uint256 withdrawableAssets) {\n        uint256 numOfCreditPositions = creditPositions.length;\n        ERC20[] memory creditAssets = new ERC20[](numOfCreditPositions);\n        uint256[] memory creditBalances = new uint256[](numOfCreditPositions);\n\n        for (uint256 i; i < numOfCreditPositions; ++i) {\n            uint32 position = creditPositions[i];\n            // If the withdrawable balance is zero there is no point to query the asset since a zero balance has zero value.\n            if ((creditBalances[i] = _withdrawableFrom(position)) == 0) continue;\n            creditAssets[i] = _assetOf(position);\n        }\n\n        withdrawableAssets = priceRouter.getValues(creditAssets, creditBalances, asset);\n    }\n\n    function _calculateTotalAssets() internal view returns (uint256 assets) {\n        (ERC20[] memory creditAssets, uint256[] memory creditBalances) = _getCreditOrDebtPositionsData(false);\n        (ERC20[] memory debtAssets, uint256[] memory debtBalances) = _getCreditOrDebtPositionsData(true);\n\n        assets = priceRouter.getValuesDelta(creditAssets, creditBalances, debtAssets, debtBalances, asset);\n    }\n\n    /**\n     * @return _positionAssets the assets of the positions\n     * @return _positionBalances the balances of the positions\n     */\n    function _getCreditOrDebtPositionsData(bool _isDebt) internal view returns (ERC20[] memory, uint256[] memory) {\n        uint32[] memory _positions = _isDebt ? debtPositions : creditPositions;\n\n        uint256 numOfPositions = _positions.length;\n        ERC20[] memory _positionAssets = new ERC20[](numOfPositions);\n        uint256[] memory _positionBalances = new uint256[](numOfPositions);\n\n        for (uint256 i; i < numOfPositions; ++i) {\n            uint32 position = _positions[i];\n            // If the balance is zero there is no point to query the asset since a zero balance has zero value.\n            if ((_positionBalances[i] = _balanceOf(position)) == 0) continue;\n            _positionAssets[i] = _assetOf(position);\n        }\n\n        return (_positionAssets, _positionBalances);\n    }\n\n    /**\n     * @notice The total amount of assets in the fund.\n     * @dev EIP4626 states totalAssets needs to be inclusive of fees.\n     * Since performance fees mint shares, total assets remains unchanged,\n     * so this implementation is inclusive of fees even though it does not explicitly show it.\n     * @dev EIP4626 states totalAssets must not revert, but it is possible for `totalAssets` to revert\n     * so it does NOT conform to ERC4626 standards.\n     * @dev Run a re-entrancy check because totalAssets can be wrong if re-entering from deposit/withdraws.\n     */\n    function totalAssets() public view override returns (uint256 assets) {\n        _whenNotPaused();\n        _revertWhenReentrant();\n        assets = _calculateTotalAssets();\n    }\n\n    /**\n     * @notice The total amount of withdrawable assets in the fund.\n     * @dev Run a re-entrancy check because totalAssetsWithdrawable can be wrong if re-entering from deposit/withdraws.\n     */\n    function totalAssetsWithdrawable() public view returns (uint256 assets) {\n        _whenNotPaused();\n        _revertWhenReentrant();\n        assets = _calculateTotalWithdrawableAssets();\n    }\n\n    /**\n     * @notice The amount of assets that the fund would exchange for the amount of shares provided.\n     * @dev Use preview functions to get accurate assets.\n     * @dev Under estimates assets.\n     * @param shares amount of shares to convert\n     * @return assets the shares can be exchanged for\n     */\n    function convertToAssets(uint256 shares) public view override returns (uint256) {\n        return previewRedeem(shares);\n    }\n\n    /**\n     * @notice The amount of shares that the fund would exchange for the amount of assets provided.\n     * @dev Use preview functions to get accurate shares.\n     * @dev Under estimates shares.\n     * @param assets amount of assets to convert\n     * @return shares the assets can be exchanged for\n     */\n    function convertToShares(uint256 assets) public view override returns (uint256 shares) {\n        return previewDeposit(assets);\n    }\n\n    /**\n     * @notice Simulate the effects of minting shares at the current block, given current on-chain conditions.\n     * @param shares amount of shares to mint\n     * @return assets that will be deposited\n     */\n    function previewMint(uint256 shares) public view override returns (uint256 assets) {\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(true);\n        assets = _previewMint(shares, _totalAssets, _totalSupply);\n    }\n\n    /**\n     * @notice Simulate the effects of withdrawing assets at the current block, given current on-chain conditions.\n     * @param assets amount of assets to withdraw\n     * @return shares that will be redeemed\n     */\n    function previewWithdraw(uint256 assets) public view override returns (uint256 shares) {\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(false);\n        shares = _previewWithdraw(assets, _totalAssets, _totalSupply);\n    }\n\n    /**\n     * @notice Simulate the effects of depositing assets at the current block, given current on-chain conditions.\n     * @param assets amount of assets to deposit\n     * @return shares that will be minted\n     */\n    function previewDeposit(uint256 assets) public view override returns (uint256 shares) {\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(true);\n        shares = _convertToShares(assets, _totalAssets, _totalSupply);\n    }\n\n    /**\n     * @notice Simulate the effects of redeeming shares at the current block, given current on-chain conditions.\n     * @param shares amount of shares to redeem\n     * @return assets that will be returned\n     */\n    function previewRedeem(uint256 shares) public view override returns (uint256 assets) {\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(false);\n        assets = _convertToAssets(shares, _totalAssets, _totalSupply);\n    }\n\n    /**\n     * @notice Finds the max amount of value an `owner` can remove from the fund.\n     * @param owner address of the user to find max value.\n     * @param inShares if false, then returns value in terms of assets\n     *                 if true then returns value in terms of shares\n     */\n    function _findMax(address owner, bool inShares) internal view virtual returns (uint256 maxOut) {\n        _whenNotPaused();\n        // Get amount of assets to withdraw.\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(false);\n\n        uint256 assets = _convertToAssets(balanceOf[owner], _totalAssets, _totalSupply);\n\n        uint256 withdrawable = _calculateTotalWithdrawableAssets();\n        maxOut = assets <= withdrawable ? assets : withdrawable;\n\n        if (inShares) maxOut = _convertToShares(maxOut, _totalAssets, _totalSupply);\n        // else leave maxOut in terms of assets.\n    }\n\n    /**\n     * @notice Returns the max amount withdrawable by a user inclusive of performance fees\n     * @dev EIP4626 states maxWithdraw must not revert, but it is possible for `totalAssets` to revert\n     * so it does NOT conform to ERC4626 standards.\n     * @param owner address to check maxWithdraw of.\n     * @return the max amount of assets withdrawable by `owner`.\n     */\n    function maxWithdraw(address owner) public view override returns (uint256) {\n        _revertWhenReentrant();\n        return _findMax(owner, false);\n    }\n\n    /**\n     * @notice Returns the max amount shares redeemable by a user\n     * @dev EIP4626 states maxRedeem must not revert, but it is possible for `totalAssets` to revert\n     * so it does NOT conform to ERC4626 standards.\n     * @param owner address to check maxRedeem of.\n     * @return the max amount of shares redeemable by `owner`.\n     */\n    function maxRedeem(address owner) public view override returns (uint256) {\n        _revertWhenReentrant();\n        return _findMax(owner, true);\n    }\n\n    /**\n     * @dev Used to more efficiently convert amount of shares to assets using a stored `totalAssets` value.\n     */\n    function _convertToAssets(\n        uint256 shares,\n        uint256 _totalAssets,\n        uint256 _totalSupply\n    ) internal pure returns (uint256 assets) {\n        assets = shares.mulDivDown(_totalAssets, _totalSupply);\n    }\n\n    /**\n     * @dev Used to more efficiently convert amount of assets to shares using a stored `totalAssets` value.\n     */\n    function _convertToShares(\n        uint256 assets,\n        uint256 _totalAssets,\n        uint256 _totalSupply\n    ) internal pure returns (uint256 shares) {\n        shares = assets.mulDivDown(_totalSupply, _totalAssets);\n    }\n\n    /**\n     * @dev Used to more efficiently simulate minting shares using a stored `totalAssets` value.\n     */\n    function _previewMint(\n        uint256 shares,\n        uint256 _totalAssets,\n        uint256 _totalSupply\n    ) internal pure returns (uint256 assets) {\n        assets = shares.mulDivUp(_totalAssets, _totalSupply);\n    }\n\n    /**\n     * @dev Used to more efficiently simulate withdrawing assets using a stored `totalAssets` value.\n     */\n    function _previewWithdraw(\n        uint256 assets,\n        uint256 _totalAssets,\n        uint256 _totalSupply\n    ) internal pure returns (uint256 shares) {\n        shares = assets.mulDivUp(_totalSupply, _totalAssets);\n    }\n\n    //cap =========================================== AUTOMATION ACTIONS LOGIC ===========================================\n\n    /**\n     * Emitted when sender is not approved to call `callOnAdaptor`.\n     */\n    error Fund__CallerNotApprovedToRebalance();\n\n    /**\n     * @notice Emitted when `setAutomationActions` is called.\n     */\n    event Fund__AutomationActionsUpdated(address indexed newAutomationActions);\n\n    /**\n     * @notice The Automation Actions contract that can rebalance this Fund.\n     * @dev Set to zero address if not in use.\n     */\n    address public automationActions;\n\n    /**\n     * @notice Set the Automation Actions contract.\n     * @param _registryId Registry Id to get the automation action.\n     * @param _expectedAutomationActions The registry automation actions differed from the expected automation actions.\n     * @dev Callable by the Fund's owner.\n     */\n    function setAutomationActions(uint256 _registryId, address _expectedAutomationActions) external onlyOwner {\n        _checkRegistryAddressAgainstExpected(_registryId, _expectedAutomationActions);\n        automationActions = _expectedAutomationActions;\n        emit Fund__AutomationActionsUpdated(_expectedAutomationActions);\n    }\n\n    // =========================================== ADAPTOR LOGIC ===========================================\n\n    /**\n     * @notice Emitted on when the rebalance deviation is changed.\n     * @param oldDeviation the old rebalance deviation\n     * @param newDeviation the new rebalance deviation\n     */\n    event RebalanceDeviationChanged(uint256 oldDeviation, uint256 newDeviation);\n\n    /**\n     * @notice totalAssets deviated outside the range set by `allowedRebalanceDeviation`.\n     * @param assets the total assets in the fund\n     * @param min the minimum allowed assets\n     * @param max the maximum allowed assets\n     */\n    error Fund__TotalAssetDeviatedOutsideRange(uint256 assets, uint256 min, uint256 max);\n\n    /**\n     * @notice Total shares in a fund changed when they should stay constant.\n     * @param current the current amount of total shares\n     * @param expected the expected amount of total shares\n     */\n    error Fund__TotalSharesMustRemainConstant(uint256 current, uint256 expected);\n\n    /**\n     * @notice Total shares in a fund changed when they should stay constant.\n     * @param requested the requested rebalance  deviation\n     * @param max the max rebalance deviation.\n     */\n    error Fund__InvalidRebalanceDeviation(uint256 requested, uint256 max);\n\n    /**\n     * @notice CallOnAdaptor attempted to use an adaptor that is either paused or is not trusted by the Fund.\n     * @param adaptor the adaptor address that is paused or not trusted.\n     */\n    error Fund__CallToAdaptorNotAllowed(address adaptor);\n\n    /**\n     * @notice Stores the max possible rebalance deviation for this fund.\n     */\n    uint256 public constant MAX_REBALANCE_DEVIATION = 0.1e18;\n\n    /**\n     * @notice The percent the total assets of a fund may deviate during a `callOnAdaptor`(rebalance) call.\n     */\n    uint256 public allowedRebalanceDeviation = 0.0003e18;\n\n    /**\n     * @notice Allows owner to change this funds rebalance deviation.\n     * @param newDeviation the new rebalance deviation value.\n     * @dev Callable by the Fund's owner.\n     */\n    function setRebalanceDeviation(uint256 newDeviation) external onlyOwner {\n        if (newDeviation > MAX_REBALANCE_DEVIATION)\n            revert Fund__InvalidRebalanceDeviation(newDeviation, MAX_REBALANCE_DEVIATION);\n\n        uint256 oldDeviation = allowedRebalanceDeviation;\n        allowedRebalanceDeviation = newDeviation;\n\n        emit RebalanceDeviationChanged(oldDeviation, newDeviation);\n    }\n\n    /**\n     * @notice Struct used to make calls to adaptors.\n     * @param adaptor the address of the adaptor to make calls to\n     * @param the abi encoded function calls to make to the `adaptor`\n     */\n    struct AdaptorCall {\n        address adaptor;\n        bytes[] callData;\n    }\n\n    /**\n     * @notice Emitted when adaptor calls are made.\n     */\n    event AdaptorCalled(address indexed adaptor, bytes data);\n\n    /**\n     * @notice Internal helper function that accepts an Adaptor Call array, and makes calls to each adaptor.\n     */\n    function _makeAdaptorCalls(AdaptorCall[] memory data) internal {\n        for (uint256 i; i < data.length; ++i) {\n            address adaptor = data[i].adaptor;\n            // Revert if adaptor not in catalogue, or adaptor is paused.\n            if (!adaptorCatalogue[adaptor]) revert Fund__CallToAdaptorNotAllowed(adaptor);\n            for (uint256 j; j < data[i].callData.length; ++j) {\n                adaptor.functionDelegateCall(data[i].callData[j]);\n                emit AdaptorCalled(adaptor, data[i].callData[j]);\n            }\n        }\n    }\n\n    /**\n     * @notice Allows owner or Automation Actions to manage the Fund using arbitrary logic calls to trusted adaptors.\n     * @dev There are several safety checks in this function to prevent rebalancers from abusing it.\n     *      - `blockExternalReceiver`\n     *      - `totalAssets` must not change by much\n     *      - `totalShares` must remain constant\n     *      - adaptors must be set up to be used with this fund\n     * @dev Since `totalAssets` is allowed to deviate slightly, rebalancers could abuse this by sending\n     *      multiple `callOnAdaptor` calls rapidly, to gradually change the share price (for example when swapping unfairly).\n     *      To mitigate this, a Fund can be limited in the total volume that can be done in a period of time by the Registry.\n     * @dev Callable by the Fund's owner, and Automation Actions address.\n     */\n    function callOnAdaptor(AdaptorCall[] calldata data) external virtual nonReentrant {\n        if (msg.sender != owner() && msg.sender != automationActions) revert Fund__CallerNotApprovedToRebalance();\n        _whenNotShutdown();\n        _whenNotPaused();\n        blockExternalReceiver = true;\n\n        // Record `totalAssets` and `totalShares` before making any external calls.\n        uint256 minimumAllowedAssets;\n        uint256 maximumAllowedAssets;\n        uint256 totalShares;\n        {\n            uint256 assetsBeforeAdaptorCall = _calculateTotalAssets();\n            minimumAllowedAssets = assetsBeforeAdaptorCall.mulDivUp((1e18 - allowedRebalanceDeviation), 1e18);\n            maximumAllowedAssets = assetsBeforeAdaptorCall.mulDivUp((1e18 + allowedRebalanceDeviation), 1e18);\n            totalShares = totalSupply;\n        }\n\n        // Run all adaptor calls.\n        _makeAdaptorCalls(data);\n\n        // After making every external call, check that the totalAssets has not deviated significantly, and that totalShares is the same.\n        uint256 assets = _calculateTotalAssets();\n        if (assets < minimumAllowedAssets || assets > maximumAllowedAssets) {\n            revert Fund__TotalAssetDeviatedOutsideRange(assets, minimumAllowedAssets, maximumAllowedAssets);\n        }\n        if (totalShares != totalSupply) revert Fund__TotalSharesMustRemainConstant(totalSupply, totalShares);\n\n        blockExternalReceiver = false;\n    }\n\n    // ============================================ LIMITS LOGIC ============================================\n\n    /**\n     * @notice Attempted entry would raise totalSupply above Share Supply Cap.\n     */\n    error Fund__ShareSupplyCapExceeded();\n\n    event ShareSupplyCapChanged(uint192 newShareSupplyCap);\n\n    /**\n     * @notice Increases the share supply cap.\n     * @dev Callable by the Fund's owner.\n     */\n    function setShareSupplyCap(uint192 _newShareSupplyCap) public onlyOwner {\n        shareSupplyCap = _newShareSupplyCap;\n        emit ShareSupplyCapChanged(_newShareSupplyCap);\n    }\n\n    /**\n     * @notice Total amount of assets that can be deposited for a user.\n     * @return assets maximum amount of assets that can be deposited\n     */\n    function maxDeposit(address) public view override returns (uint256) {\n        if (isShutdown) return 0;\n\n        uint192 _cap = shareSupplyCap;\n\n        (uint256 _totalAssets, uint256 _totalSupply) = _previewTotalAssetsAndTotalSupplyAfterFees(true);\n\n        if (_totalSupply >= _cap) return 0;\n        else {\n            uint256 shareDelta = _cap - _totalSupply;\n            return _convertToAssets(shareDelta, _totalAssets, _totalSupply);\n        }\n    }\n\n    /**\n     * @notice Total amount of shares that can be minted for a user.\n     * @return shares maximum amount of shares that can be minted\n     */\n    function maxMint(address) public view override returns (uint256) {\n        if (isShutdown) return 0;\n\n        uint192 _cap = shareSupplyCap;\n        uint256 _totalSupply = totalSupply;\n\n        return _totalSupply >= _cap ? 0 : _cap - _totalSupply;\n    }\n\n    // ========================================== HELPER FUNCTIONS ==========================================\n\n    /**\n     * @dev Deposit into a position according to its position type and update related state.\n     * @param position address to deposit funds into\n     * @param assets the amount of assets to deposit into the position\n     */\n    function _depositTo(uint32 position, uint256 assets) internal {\n        address adaptor = getPositionData[position].adaptor;\n        adaptor.functionDelegateCall(\n            abi.encodeWithSelector(\n                BaseAdaptor.deposit.selector,\n                assets,\n                getPositionData[position].adaptorData,\n                getPositionData[position].configurationData\n            )\n        );\n    }\n\n    /**\n     * @dev Withdraw from a position according to its position type and update related state.\n     * @param position address to withdraw funds from\n     * @param assets the amount of assets to withdraw from the position\n     * @param receiver the address to sent withdrawn assets to\n     */\n    function _withdrawFrom(uint32 position, uint256 assets, address receiver) internal {\n        address adaptor = getPositionData[position].adaptor;\n        adaptor.functionDelegateCall(\n            abi.encodeWithSelector(\n                BaseAdaptor.withdraw.selector,\n                assets,\n                receiver,\n                getPositionData[position].adaptorData,\n                getPositionData[position].configurationData\n            )\n        );\n    }\n\n    /**\n     * @dev Get the withdrawable balance of a position according to its position type.\n     * @param position position to get the withdrawable balance of\n     */\n    function _withdrawableFrom(uint32 position) internal view returns (uint256) {\n        // Debt positions always return 0 for their withdrawable.\n        if (getPositionData[position].isDebt) return 0;\n        return\n            BaseAdaptor(getPositionData[position].adaptor).withdrawableFrom(\n                getPositionData[position].adaptorData,\n                getPositionData[position].configurationData\n            );\n    }\n\n    /**\n     * @dev Get the balance of a position according to its position type.\n     * @dev For ERC4626 position balances, this uses `previewRedeem` as opposed\n     *      to `convertToAssets` so that balanceOf ERC4626 positions includes fees taken on withdraw.\n     * @param position position to get the balance of\n     */\n    function _balanceOf(uint32 position) internal view returns (uint256) {\n        address adaptor = getPositionData[position].adaptor;\n        return BaseAdaptor(adaptor).balanceOf(getPositionData[position].adaptorData);\n    }\n\n    /**\n     * @dev Get the asset of a position according to its position type.\n     * @param position to get the asset of\n     */\n    function _assetOf(uint32 position) internal view returns (ERC20) {\n        address adaptor = getPositionData[position].adaptor;\n        return BaseAdaptor(adaptor).assetOf(getPositionData[position].adaptorData);\n    }\n\n    /**\n     * @notice Attempted to use an address from the registry, but address was not expected.\n     */\n    error Fund__ExpectedAddressDoesNotMatchActual();\n\n    /**\n     * @notice Attempted to set an address to registry Id 0.\n     */\n    error Fund__SettingValueToRegistryIdZeroIsProhibited();\n\n    /**\n     * @notice Verify that `_registryId` in registry corresponds to expected address.\n     */\n    function _checkRegistryAddressAgainstExpected(uint256 _registryId, address _expected) internal view {\n        if (_registryId == 0) revert Fund__SettingValueToRegistryIdZeroIsProhibited();\n        if (registry.getAddress(_registryId) != _expected) revert Fund__ExpectedAddressDoesNotMatchActual();\n    }\n\n    /**\n     * @notice View the amount of assets in each Fund Position.\n     */\n    function viewPositionBalances()\n        external\n        view\n        returns (ERC20[] memory assets, uint256[] memory balances, bool[] memory isDebt)\n    {\n        uint256 creditLen = creditPositions.length;\n        uint256 debtLen = debtPositions.length;\n        uint256 totalLen = creditLen + debtLen;\n        assets = new ERC20[](totalLen);\n        balances = new uint256[](totalLen);\n        isDebt = new bool[](totalLen);\n        for (uint256 i; i < creditLen; ++i) {\n            assets[i] = _assetOf(creditPositions[i]);\n            balances[i] = _balanceOf(creditPositions[i]);\n            isDebt[i] = false;\n        }\n\n        for (uint256 i; i < debtLen; ++i) {\n            // uint256 index;\n            uint256 index = i + creditLen;\n            assets[index] = _assetOf(debtPositions[i]);\n            balances[index] = _balanceOf(debtPositions[i]);\n            isDebt[index] = true;\n        }\n    }\n}\n"
      },
      "src/modules/price-router/PriceRouter.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { SafeTransferLib } from \"@solmate/utils/SafeTransferLib.sol\";\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IChainlinkAggregator } from \"src/interfaces/external/IChainlinkAggregator.sol\";\nimport { IChainlinkAggregatorProxy } from \"src/interfaces/external/IChainlinkAggregatorProxy.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"src/utils/Math.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\nimport { Extension } from \"src/modules/price-router/Extensions/Extension.sol\";\nimport { Registry } from \"src/Registry.sol\";\n\nimport { UniswapV3Pool } from \"src/interfaces/external/UniswapV3Pool.sol\";\nimport { OracleLibrary } from \"@uniswapV3P/libraries/OracleLibrary.sol\";\n\n/**\n * @title Swaap Price Router\n * @notice Provides a universal interface allowing Swaap contracts to retrieve secure pricing\n *         data from Chainlink.\n * @author crispymangoes\n */\ncontract PriceRouter is Ownable {\n    using SafeTransferLib for ERC20;\n    using SafeCast for int256;\n    using Math for uint256;\n    using Address for address;\n\n    event AddAsset(address indexed asset);\n\n    event IntentToEditAsset(\n        address indexed asset,\n        AssetSettings _settings,\n        bytes _storage,\n        bytes32 editHash,\n        uint256 assetEditableAt\n    );\n\n    event EditAssetCancelled(address indexed asset, bytes32 editHash);\n\n    event EditAssetComplete(address indexed asset, bytes32 editHash);\n\n    Registry public immutable registry;\n    ERC20 public immutable WETH;\n\n    constructor(address newOwner, Registry _registry, ERC20 _weth) {\n        registry = _registry;\n        WETH = _weth;\n        transferOwnership(newOwner);\n    }\n\n    // =========================================== ASSETS CONFIG ===========================================\n    /**\n     * @notice Bare minimum settings all derivatives support.\n     * @param derivative the derivative used to price the asset\n     * @param source the address used to price the asset\n     */\n    struct AssetSettings {\n        uint8 derivative;\n        address source;\n    }\n\n    /**\n     * @notice Mapping between an asset to price and its `AssetSettings`.\n     */\n    mapping(ERC20 => AssetSettings) public getAssetSettings;\n\n    // ======================================= OWNERSHIP TRANSISITION =======================================\n\n    /**\n     * @notice Emitted when an ownership transition is started.\n     */\n    event OwnerTransitionStarted(address indexed pendingOwner, uint256 startTime);\n\n    /**\n     * @notice Emitted when an ownership transition is cancelled.\n     */\n    event OwnerTransitionCancelled(address indexed pendingOwner);\n\n    /**\n     * @notice Emitted when an ownership transition is completed.\n     */\n    event OwnerTransitionComplete(address indexed newOwner);\n\n    /**\n     * @notice Attempted to call a function intended for Zero Id address.\n     */\n    error PriceRouter__OnlyCallableByZeroId();\n\n    /**\n     * @notice Attempted to transition owner to the zero address.\n     */\n    error PriceRouter__NewOwnerCanNotBeZero();\n\n    /**\n     * @notice Attempted to perform a restricted action while ownership transition is pending.\n     */\n    error PriceRouter__TransitionPending();\n\n    /**\n     * @notice Attempted to cancel or complete a transition when one is not active.\n     */\n    error PriceRouter__TransitionNotPending();\n\n    /**\n     * @notice Attempted to call `completeTransition` from an address that is not the pending owner.\n     */\n    error PriceRouter__OnlyCallableByPendingOwner();\n\n    /**\n     * @notice The amount of time it takes for an ownership transition to work.\n     */\n    uint256 public constant TRANSITION_PERIOD = 7 days;\n\n    /**\n     * @notice The Pending Owner, that becomes the owner after the transition period, and they call `completeTransition`.\n     */\n    address public pendingOwner;\n\n    /**\n     * @notice The starting time stamp of the transition.\n     */\n    uint256 public transitionStart;\n\n    /**\n     * @notice Allows Zero Id address to set a new owner, after the transition period is up.\n     */\n    function transitionOwner(address newOwner) external {\n        if (msg.sender != registry.getAddress(0)) revert PriceRouter__OnlyCallableByZeroId();\n        if (pendingOwner != address(0)) revert PriceRouter__TransitionPending();\n        if (newOwner == address(0)) revert PriceRouter__NewOwnerCanNotBeZero();\n\n        emit OwnerTransitionStarted(newOwner, transitionStart);\n\n        pendingOwner = newOwner;\n        transitionStart = block.timestamp;\n    }\n\n    /**\n     * @notice Allows Zero Id address to cancel an ongoing owner transition.\n     */\n    function cancelTransition() external {\n        address _pendingOwner = pendingOwner;\n\n        if (msg.sender != registry.getAddress(0)) revert PriceRouter__OnlyCallableByZeroId();\n        if (_pendingOwner == address(0)) revert PriceRouter__TransitionNotPending();\n\n        emit OwnerTransitionCancelled(_pendingOwner);\n\n        pendingOwner = address(0);\n        transitionStart = 0;\n    }\n\n    /**\n     * @notice Allows pending owner to complete the ownership transition.\n     */\n    function completeTransition() external {\n        address _pendingOwner = pendingOwner;\n\n        if (msg.sender != _pendingOwner) revert PriceRouter__OnlyCallableByPendingOwner();\n        if (block.timestamp < transitionStart + TRANSITION_PERIOD) revert PriceRouter__TransitionPending();\n\n        _transferOwnership(_pendingOwner);\n\n        emit OwnerTransitionComplete(_pendingOwner);\n\n        pendingOwner = address(0);\n        transitionStart = 0;\n    }\n\n    /**\n     * @notice Extends OZ Ownable `_checkOwner` function to block owner calls, if there is an ongoing transition.\n     */\n    function _checkOwner() internal view override {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        if (transitionStart != 0) revert PriceRouter__TransitionPending();\n    }\n\n    // ======================================= ASSET OPERATIONS =======================================\n\n    /**\n     * @notice Attempted to set a minimum price below the Chainlink minimum price (with buffer).\n     * @param minPrice minimum price attempted to set\n     * @param bufferedMinPrice minimum price that can be set including buffer\n     */\n    error PriceRouter__InvalidMinPrice(uint256 minPrice, uint256 bufferedMinPrice);\n\n    /**\n     * @notice Attempted to set a maximum price above the Chainlink maximum price (with buffer).\n     * @param maxPrice maximum price attempted to set\n     * @param bufferedMaxPrice maximum price that can be set including buffer\n     */\n    error PriceRouter__InvalidMaxPrice(uint256 maxPrice, uint256 bufferedMaxPrice);\n\n    /**\n     * @notice Attempted to add an invalid asset.\n     * @param asset address of the invalid asset\n     */\n    error PriceRouter__InvalidAsset(address asset);\n\n    /**\n     * @notice Attempted to add an asset that is already supported.\n     */\n    error PriceRouter__AssetAlreadyAdded(address asset);\n\n    /**\n     * @notice Attempted to edit an asset that is not supported.\n     */\n    error PriceRouter__AssetNotAdded(address asset);\n\n    /**\n     * @notice Attempted to edit an asset that is not editable.\n     */\n    error PriceRouter__AssetNotEditable(address asset);\n\n    /**\n     * @notice Attempted to edit an asset that is pending edit.\n     */\n    error PriceRouter__AssetPendingEdit(address asset);\n\n    /**\n     * @notice Attempted to cancel the editing of an asset that is not pending edit.\n     */\n    error PriceRouter__AssetNotPendingEdit(address asset);\n\n    /**\n     * @notice Attempted to add an asset, but actual answer was outside range of expectedAnswer.\n     */\n    error PriceRouter__BadAnswer(uint256 answer, uint256 expectedAnswer);\n\n    /**\n     * @notice Attempted to perform an operation using an unknown derivative.\n     */\n    error PriceRouter__UnknownDerivative(uint8 unknownDerivative);\n\n    /**\n     * @notice Attempted to add an asset with invalid min/max prices.\n     * @param min price\n     * @param max price\n     */\n    error PriceRouter__MinPriceGreaterThanMaxPrice(uint256 min, uint256 max);\n\n    /// @dev The price buffer for Chainlink aggregator minimum price.\n    uint256 internal constant _CHAINLINK_MIN_PRICE_BUFFER = 1.1e18; // 10%\n\n    /// @dev The price buffer for Chainlink aggregator maximum price.\n    uint256 internal constant _CHAINLINK_MAX_PRICE_BUFFER = 0.9e18; // 10%\n\n    /**\n     * @notice The allowed deviation between the expected answer vs the actual answer.\n     */\n    uint256 public constant EXPECTED_ANSWER_DEVIATION = 0.02e18;\n\n    /**\n     * @notice The amount of time that must pass between owner calling `startEditAsset`, and `completeEditAsset`.\n     */\n    uint64 public constant EDIT_ASSET_DELAY = 7 days;\n\n    /**\n     * @notice Stores the info that an asset can be editted to.\n     */\n    mapping(ERC20 => bytes32) public assetEditableHash;\n\n    /**\n     * @notice Allows caller to call multiple functions in a single TX.\n     * @dev Does NOT return the function return values.\n     */\n    function multicall(bytes[] calldata data) external {\n        for (uint256 i; i < data.length; ++i) address(this).functionDelegateCall(data[i]);\n    }\n\n    /**\n     * @notice Allows owner to add assets to the price router.\n     * @dev Performs a sanity check by comparing the price router computed price to\n     * a user input `_expectedAnswer`.\n     * @param _asset the asset to add to the pricing router\n     * @param _settings the settings for `_asset`\n     *        @dev The `derivative` value in settings MUST be non zero.\n     * @param _storage arbitrary bytes data used to configure `_asset` pricing\n     * @param _expectedAnswer the expected answer for the asset from  `_getPriceInUSD`\n     */\n    function addAsset(\n        ERC20 _asset,\n        AssetSettings memory _settings,\n        bytes memory _storage,\n        uint256 _expectedAnswer\n    ) external onlyOwner {\n        // Check that asset is not already added.\n        if (getAssetSettings[_asset].derivative > 0) revert PriceRouter__AssetAlreadyAdded(address(_asset));\n\n        _updateAsset(_asset, _settings, _storage, _expectedAnswer);\n\n        emit AddAsset(address(_asset));\n    }\n\n    /**\n     * @notice Allows owner to start the edit asset process.\n     * @dev Saves a hash of the inputs, and maps it to the timestamp when `_asset` is editable.\n     * @param _asset the asset to edit in the pricing router\n     * @param _settings the settings for `_asset`\n     *        @dev The `derivative` value in settings MUST be non zero.\n     * @param _storage arbitrary bytes data used to configure `_asset` pricing\n     */\n    function startEditAsset(ERC20 _asset, AssetSettings memory _settings, bytes memory _storage) external onlyOwner {\n        // Make sure the asset does not have a pending edit.\n        if (assetEditableHash[_asset] != bytes32(0)) revert PriceRouter__AssetPendingEdit(address(_asset));\n\n        // Make sure the asset has been added.\n        if (getAssetSettings[_asset].derivative == 0) revert PriceRouter__AssetNotAdded(address(_asset));\n\n        uint256 assetEditableAt = block.timestamp + EDIT_ASSET_DELAY;\n        bytes32 editHash = keccak256(abi.encode(_asset, _settings, _storage, assetEditableAt));\n\n        assetEditableHash[_asset] = editHash;\n\n        emit IntentToEditAsset(address(_asset), _settings, _storage, editHash, assetEditableAt);\n    }\n\n    /**\n     * @notice Once `EDIT_ASSET_DELAY` has passed, `_asset` is now editable using the\n     *         same inputs given to `startEditAsset`.\n     * @param _asset the asset to finish editing in the pricing router\n     * @param _settings the settings for `_asset`\n     *        @dev The `derivative` value in settings MUST be non zero.\n     * @param _storage arbitrary bytes data used to configure `_asset` pricing\n     * @param assetEditableAt the timestamp after which `_asset` is editable\n     * @param _expectedAnswer the expected answer for the asset from  `_getPriceInUSD`\n     */\n    function completeEditAsset(\n        ERC20 _asset,\n        AssetSettings memory _settings,\n        bytes memory _storage,\n        uint256 assetEditableAt,\n        uint256 _expectedAnswer\n    ) external onlyOwner {\n        bytes32 expectedEditHash = keccak256(abi.encode(_asset, _settings, _storage, assetEditableAt));\n\n        // Make sure asset can be edited.\n        bytes32 editHash = assetEditableHash[_asset];\n        if (editHash == bytes32(0) || editHash != expectedEditHash || block.timestamp < assetEditableAt)\n            revert PriceRouter__AssetNotEditable(address(_asset));\n\n        // Reset asset editable inputs.\n        delete assetEditableHash[_asset];\n\n        // Edit the asset.\n        _updateAsset(_asset, _settings, _storage, _expectedAnswer);\n\n        emit EditAssetComplete(address(_asset), editHash);\n    }\n\n    /**\n     * @notice Cancel a pending edit for `_asset`.\n     * @param _asset the asset to cancel editing of in the pricing router\n     */\n    function cancelEditAsset(ERC20 _asset) external onlyOwner {\n        bytes32 editHash = assetEditableHash[_asset];\n\n        // make sure the asset was pending to edit.\n        if (editHash == bytes32(0)) revert PriceRouter__AssetNotPendingEdit(address(_asset));\n\n        delete assetEditableHash[_asset];\n\n        emit EditAssetCancelled(address(_asset), editHash);\n    }\n\n    /**\n     * @notice Helper function to update an `_asset`s configuration.\n     * @param _asset the asset to update in the pricing router\n     * @param _settings the settings for `_asset`\n     *        @dev The `derivative` value in settings MUST be non zero.\n     * @param _storage arbitrary bytes data used to configure `_asset` pricing\n     * @param _expectedAnswer the expected answer for the asset from  `_getPriceInUSD`\n     */\n    function _updateAsset(\n        ERC20 _asset,\n        AssetSettings memory _settings,\n        bytes memory _storage,\n        uint256 _expectedAnswer\n    ) internal {\n        if (address(_asset) == address(0)) revert PriceRouter__InvalidAsset(address(_asset));\n\n        // Zero is an invalid derivative.\n        if (_settings.derivative == 0) revert PriceRouter__UnknownDerivative(_settings.derivative);\n\n        // Call setup function for appropriate derivative.\n        if (_settings.derivative == 1) {\n            _setupPriceForChainlinkDerivative(_asset, _settings.source, _storage);\n        } else if (_settings.derivative == 2) {\n            _setupPriceForTwapDerivative(_asset, _settings.source, _storage);\n        } else if (_settings.derivative == 3) {\n            Extension(_settings.source).setupSource(_asset, _storage);\n        } else revert PriceRouter__UnknownDerivative(_settings.derivative);\n\n        // Check `_getPriceInUSD` against `_expectedAnswer`.\n        uint256 minAnswer = _expectedAnswer.mulWadDown((Math.WAD - EXPECTED_ANSWER_DEVIATION));\n        uint256 maxAnswer = _expectedAnswer.mulWadDown((Math.WAD + EXPECTED_ANSWER_DEVIATION));\n\n        getAssetSettings[_asset] = _settings;\n        uint256 answer = _getPriceInUSD(_asset, _settings);\n        if (answer < minAnswer || answer > maxAnswer) revert PriceRouter__BadAnswer(answer, _expectedAnswer);\n    }\n\n    /**\n     * @notice return bool indicating whether or not an asset has been set up.\n     * @dev Since `addAsset` enforces the derivative is non zero, checking if the stored setting\n     *      is nonzero is sufficient to see if the asset is set up.\n     */\n    function isSupported(ERC20 asset) external view returns (bool) {\n        return getAssetSettings[asset].derivative > 0;\n    }\n\n    // ======================================= PRICING OPERATIONS =======================================\n\n    /**\n     * @notice Get `asset` price in USD.\n     * @dev Returns price in USD with 8 decimals.\n     */\n    function getPriceInUSD(ERC20 asset) external view returns (uint256) {\n        AssetSettings memory assetSettings = getAssetSettings[asset];\n        return _getPriceInUSD(asset, assetSettings);\n    }\n\n    /**\n     * @notice Get multiple `asset` prices in USD.\n     * @dev Returns array of prices in USD with 8 decimals.\n     */\n    function getPricesInUSD(ERC20[] calldata assets) external view returns (uint256[] memory prices) {\n        prices = new uint256[](assets.length);\n        for (uint256 i; i < assets.length; ++i) {\n            AssetSettings memory assetSettings = getAssetSettings[assets[i]];\n            prices[i] = _getPriceInUSD(assets[i], assetSettings);\n        }\n    }\n\n    /**\n     * @notice Get the value of an asset in terms of another asset.\n     * @param baseAsset address of the asset to get the price of in terms of the quote asset\n     * @param amount amount of the base asset to price\n     * @param quoteAsset address of the asset that the base asset is priced in terms of\n     * @return value value of the amount of base assets specified in terms of the quote asset\n     */\n    function getValue(ERC20 baseAsset, uint256 amount, ERC20 quoteAsset) external view returns (uint256 value) {\n        AssetSettings memory baseSettings = getAssetSettings[baseAsset];\n        AssetSettings memory quoteSettings = getAssetSettings[quoteAsset];\n        uint256 priceBaseUSD = _getPriceInUSD(baseAsset, baseSettings);\n        uint256 priceQuoteUSD = _getPriceInUSD(quoteAsset, quoteSettings);\n        value = _getValueInQuote(priceBaseUSD, priceQuoteUSD, baseAsset.decimals(), quoteAsset.decimals(), amount);\n    }\n\n    /**\n     * @notice Helper function that compares `_getValues` between input 0 and input 1.\n     */\n    function getValuesDelta(\n        ERC20[] calldata baseAssets0,\n        uint256[] calldata amounts0,\n        ERC20[] calldata baseAssets1,\n        uint256[] calldata amounts1,\n        ERC20 quoteAsset\n    ) external view returns (uint256) {\n        uint256 value0 = _getValues(baseAssets0, amounts0, quoteAsset);\n        uint256 value1 = _getValues(baseAssets1, amounts1, quoteAsset);\n        return value0 - value1;\n    }\n\n    /**\n     * @notice Helper function that determines the value of assets using `_getValues`.\n     */\n    function getValues(\n        ERC20[] calldata baseAssets,\n        uint256[] calldata amounts,\n        ERC20 quoteAsset\n    ) external view returns (uint256) {\n        return _getValues(baseAssets, amounts, quoteAsset);\n    }\n\n    /**\n     * @notice Get the exchange rate between two assets.\n     * @param baseAsset address of the asset to get the exchange rate of in terms of the quote asset\n     * @param quoteAsset address of the asset that the base asset is exchanged for\n     * @return exchangeRate rate of exchange between the base asset and the quote asset\n     */\n    function getExchangeRate(ERC20 baseAsset, ERC20 quoteAsset) public view returns (uint256 exchangeRate) {\n        AssetSettings memory baseSettings = getAssetSettings[baseAsset];\n        AssetSettings memory quoteSettings = getAssetSettings[quoteAsset];\n\n        exchangeRate = _getExchangeRate(baseAsset, baseSettings, quoteAsset, quoteSettings, quoteAsset.decimals());\n    }\n\n    /**\n     * @notice Get the exchange rates between multiple assets and another asset.\n     * @param baseAssets addresses of the assets to get the exchange rates of in terms of the quote asset\n     * @param quoteAsset address of the asset that the base assets are exchanged for\n     * @return exchangeRates rate of exchange between the base assets and the quote asset\n     */\n    function getExchangeRates(\n        ERC20[] memory baseAssets,\n        ERC20 quoteAsset\n    ) external view returns (uint256[] memory exchangeRates) {\n        uint8 quoteAssetDecimals = quoteAsset.decimals();\n        AssetSettings memory quoteSettings = getAssetSettings[quoteAsset];\n\n        uint256 numOfAssets = baseAssets.length;\n        exchangeRates = new uint256[](numOfAssets);\n        for (uint256 i; i < numOfAssets; ++i) {\n            AssetSettings memory baseSettings = getAssetSettings[baseAssets[i]];\n            exchangeRates[i] = _getExchangeRate(\n                baseAssets[i],\n                baseSettings,\n                quoteAsset,\n                quoteSettings,\n                quoteAssetDecimals\n            );\n        }\n    }\n\n    // =========================================== HELPER FUNCTIONS ===========================================\n\n    /**\n     * @notice Attempted to update the asset to one that is not supported by the platform.\n     * @param asset address of the unsupported asset\n     */\n    error PriceRouter__UnsupportedAsset(address asset);\n\n    /**\n     * @notice Gets the exchange rate between a base and a quote asset\n     * @param baseAsset the asset to convert into quoteAsset\n     * @param quoteAsset the asset base asset is converted into\n     * @return exchangeRate value of base asset in terms of quote asset\n     */\n    function _getExchangeRate(\n        ERC20 baseAsset,\n        AssetSettings memory baseSettings,\n        ERC20 quoteAsset,\n        AssetSettings memory quoteSettings,\n        uint8 quoteAssetDecimals\n    ) internal view returns (uint256) {\n        uint256 basePrice = _getPriceInUSD(baseAsset, baseSettings);\n        uint256 quotePrice = _getPriceInUSD(quoteAsset, quoteSettings);\n        uint256 exchangeRate = basePrice.mulDivDown(10 ** quoteAssetDecimals, quotePrice);\n        return exchangeRate;\n    }\n\n    /**\n     * @notice Helper function to get an assets price in USD.\n     * @dev Returns price in USD with 8 decimals.\n     */\n    function _getPriceInUSD(ERC20 asset, AssetSettings memory settings) internal view returns (uint256) {\n        _runPreFlightCheck();\n        // Call get price function using appropriate derivative.\n        uint256 price;\n        if (settings.derivative == 1) {\n            price = _getPriceForChainlinkDerivative(asset, settings.source);\n        } else if (settings.derivative == 2) {\n            price = _getPriceForTwapDerivative(asset, settings.source);\n        } else if (settings.derivative == 3) {\n            price = Extension(settings.source).getPriceInUSD(asset);\n        } else if (settings.derivative == 0) {\n            revert PriceRouter__UnsupportedAsset(address(asset));\n        } else revert PriceRouter__UnknownDerivative(settings.derivative);\n\n        return price;\n    }\n\n    /**\n     * @notice If any safety checks needs to be run before pricing operations, they should be added here.\n     */\n    function _runPreFlightCheck() internal view virtual {}\n\n    /**\n     * @notice math function that preserves precision by multiplying the amountBase before dividing.\n     * @param priceBaseUSD the base asset price in USD\n     * @param priceQuoteUSD the quote asset price in USD\n     * @param baseDecimals the base asset decimals\n     * @param quoteDecimals the quote asset decimals\n     * @param amountBase the amount of base asset\n     */\n    function _getValueInQuote(\n        uint256 priceBaseUSD,\n        uint256 priceQuoteUSD,\n        uint8 baseDecimals,\n        uint8 quoteDecimals,\n        uint256 amountBase\n    ) internal pure returns (uint256 valueInQuote) {\n        // Get value in quote asset, but maintain as much precision as possible.\n        // Cleaner equations below.\n        // baseToUSD = amountBase * priceBaseUSD / 10**baseDecimals.\n        // valueInQuote = baseToUSD * 10**quoteDecimals / priceQuoteUSD\n        valueInQuote = amountBase.mulDivDown(\n            (priceBaseUSD * 10 ** quoteDecimals),\n            (10 ** baseDecimals * priceQuoteUSD)\n        );\n    }\n\n    /**\n     * @notice Attempted an operation with arrays of unequal lengths that were expected to be equal length.\n     */\n    error PriceRouter__LengthMismatch();\n\n    /**\n     * @notice Get the total value of multiple assets in terms of another asset.\n     * @param baseAssets addresses of the assets to get the price of in terms of the quote asset\n     * @param amounts amounts of each base asset to price\n     * @param quoteAsset address of the assets that the base asset is priced in terms of\n     * @return value total value of the amounts of each base assets specified in terms of the quote asset\n     */\n    function _getValues(\n        ERC20[] calldata baseAssets,\n        uint256[] calldata amounts,\n        ERC20 quoteAsset\n    ) internal view returns (uint256) {\n        if (baseAssets.length != amounts.length) revert PriceRouter__LengthMismatch();\n        uint256 quotePrice;\n        {\n            AssetSettings memory quoteSettings = getAssetSettings[quoteAsset];\n            quotePrice = _getPriceInUSD(quoteAsset, quoteSettings);\n        }\n        uint256 valueInQuote;\n        uint8 quoteDecimals = quoteAsset.decimals();\n\n        for (uint256 i; i < baseAssets.length; ++i) {\n            // Skip zero amount values.\n            if (amounts[i] == 0) continue;\n            ERC20 baseAsset = baseAssets[i];\n            if (baseAsset == quoteAsset) valueInQuote += amounts[i];\n            else {\n                uint256 basePrice;\n                {\n                    AssetSettings memory baseSettings = getAssetSettings[baseAsset];\n                    basePrice = _getPriceInUSD(baseAsset, baseSettings);\n                }\n                valueInQuote += _getValueInQuote(\n                    basePrice,\n                    quotePrice,\n                    baseAsset.decimals(),\n                    quoteDecimals,\n                    amounts[i]\n                );\n            }\n        }\n        return valueInQuote;\n    }\n\n    // =========================================== CHAINLINK PRICE DERIVATIVE ===========================================\\\n    /**\n     * @notice Stores data for Chainlink derivative assets.\n     * @param max the max valid price of the asset\n     * @param min the min valid price of the asset\n     * @param heartbeat the max amount of time between price updates\n     * @param inETH bool indicating whether the price feed is\n     *        denominated in ETH(true) or USD(false)\n     */\n    struct ChainlinkDerivativeStorage {\n        uint144 max;\n        uint80 min;\n        uint24 heartbeat;\n        bool inETH;\n    }\n\n    /**\n     * @notice Buffered min price exceedes 80 bits of data.\n     */\n    error PriceRouter__BufferedMinOverflow();\n\n    /**\n     * @notice Aggregator price feed is not in the expected decimals. (8 when in USD or 18 when in ETH)\n     */\n    error PriceRouter__InvalidPriceDecimals();\n\n    /**\n     * @notice Returns Chainlink Derivative Storage\n     */\n    mapping(ERC20 => ChainlinkDerivativeStorage) public getChainlinkDerivativeStorage;\n\n    /**\n     * @notice If zero is specified for a Chainlink asset heartbeat, this value is used instead.\n     */\n    uint24 public constant DEFAULT_HEART_BEAT = 1 days;\n\n    /**\n     * @notice Setup function for pricing Chainlink derivative assets.\n     * @dev _source The address of the Chainlink Data feed.\n     * @dev _storage A ChainlinkDerivativeStorage value defining valid prices.\n     */\n    function _setupPriceForChainlinkDerivative(ERC20 _asset, address _source, bytes memory _storage) internal {\n        ChainlinkDerivativeStorage memory parameters = abi.decode(_storage, (ChainlinkDerivativeStorage));\n\n        // Use Chainlink to get the min and max of the asset.\n        IChainlinkAggregator aggregator = IChainlinkAggregator(IChainlinkAggregatorProxy(_source).aggregator());\n        uint256 minFromChainklink = uint256(uint192(aggregator.minAnswer()));\n        uint256 maxFromChainlink = uint256(uint192(aggregator.maxAnswer()));\n\n        // Add a ~10% buffer to minimum and maximum price from Chainlink because Chainlink can stop updating\n        // its price before/above the min/max price.\n        uint256 bufferedMinPrice = (minFromChainklink * _CHAINLINK_MIN_PRICE_BUFFER) / Math.WAD;\n        uint256 bufferedMaxPrice = (maxFromChainlink * _CHAINLINK_MAX_PRICE_BUFFER) / Math.WAD;\n\n        if (parameters.min == 0) {\n            // Revert if bufferedMinPrice overflows because uint80 is too small to hold the minimum price,\n            // and lowering it to uint80 is not safe because the price feed can stop being updated before\n            // it actually gets to that lower price.\n            if (bufferedMinPrice > type(uint80).max) revert PriceRouter__BufferedMinOverflow();\n            parameters.min = uint80(bufferedMinPrice);\n        } else {\n            if (parameters.min < bufferedMinPrice)\n                revert PriceRouter__InvalidMinPrice(parameters.min, bufferedMinPrice);\n        }\n\n        if (parameters.max == 0) {\n            //Do not revert even if bufferedMaxPrice is greater than uint144, because lowering it to uint144 max is more conservative.\n            parameters.max = bufferedMaxPrice > type(uint144).max ? type(uint144).max : uint144(bufferedMaxPrice);\n        } else {\n            if (parameters.max > bufferedMaxPrice)\n                revert PriceRouter__InvalidMaxPrice(parameters.max, bufferedMaxPrice);\n        }\n\n        if (parameters.min >= parameters.max)\n            revert PriceRouter__MinPriceGreaterThanMaxPrice(parameters.min, parameters.max);\n\n        if (parameters.inETH) {\n            if (aggregator.decimals() != 18) revert PriceRouter__InvalidPriceDecimals();\n        } else {\n            if (aggregator.decimals() != 8) revert PriceRouter__InvalidPriceDecimals();\n        }\n\n        parameters.heartbeat = parameters.heartbeat != 0 ? parameters.heartbeat : DEFAULT_HEART_BEAT;\n\n        getChainlinkDerivativeStorage[_asset] = parameters;\n    }\n\n    /**\n     * @notice Get the price of a Chainlink derivative in terms of USD.\n     */\n    function _getPriceForChainlinkDerivative(ERC20 _asset, address _source) internal view returns (uint256) {\n        ChainlinkDerivativeStorage memory parameters = getChainlinkDerivativeStorage[_asset];\n        IChainlinkAggregator aggregator = IChainlinkAggregator(_source);\n        (, int256 _price, , uint256 _timestamp, ) = aggregator.latestRoundData();\n        uint256 price = _price.toUint256();\n        _checkPriceFeed(address(_asset), price, _timestamp, parameters.max, parameters.min, parameters.heartbeat);\n        // If price is in ETH, then convert price into USD.\n        if (parameters.inETH) {\n            uint256 _ethToUsd = _getPriceInUSD(WETH, getAssetSettings[WETH]);\n            price = price.mulWadDown(_ethToUsd);\n        }\n        return price;\n    }\n\n    /**\n     * @notice Attempted an operation to price an asset that under its minimum valid price.\n     * @param asset address of the asset that is under its minimum valid price\n     * @param price price of the asset\n     * @param minPrice minimum valid price of the asset\n     */\n    error PriceRouter__AssetBelowMinPrice(address asset, uint256 price, uint256 minPrice);\n\n    /**\n     * @notice Attempted an operation to price an asset that under its maximum valid price.\n     * @param asset address of the asset that is under its maximum valid price\n     * @param price price of the asset\n     * @param maxPrice maximum valid price of the asset\n     */\n    error PriceRouter__AssetAboveMaxPrice(address asset, uint256 price, uint256 maxPrice);\n\n    /**\n     * @notice Attempted to fetch a price for an asset that has not been updated in too long.\n     * @param asset address of the asset thats price is stale\n     * @param timeSinceLastUpdate seconds since the last price update\n     * @param heartbeat maximum allowed time between price updates\n     */\n    error PriceRouter__StalePrice(address asset, uint256 timeSinceLastUpdate, uint256 heartbeat);\n\n    /**\n     * @notice helper function to validate a price feed is safe to use.\n     * @param asset ERC20 asset price feed data is for.\n     * @param value the price value the price feed gave.\n     * @param timestamp the last timestamp the price feed was updated.\n     * @param max the upper price bound\n     * @param min the lower price bound\n     * @param heartbeat the max amount of time between price updates\n     */\n    function _checkPriceFeed(\n        address asset,\n        uint256 value,\n        uint256 timestamp,\n        uint144 max,\n        uint88 min,\n        uint24 heartbeat\n    ) internal view {\n        if (value < min) revert PriceRouter__AssetBelowMinPrice(address(asset), value, min);\n\n        if (value > max) revert PriceRouter__AssetAboveMaxPrice(address(asset), value, max);\n\n        uint256 timeSinceLastUpdate = block.timestamp - timestamp;\n        if (timeSinceLastUpdate > heartbeat)\n            revert PriceRouter__StalePrice(address(asset), timeSinceLastUpdate, heartbeat);\n    }\n\n    // =========================================== TWAP PRICE DERIVATIVE ===========================================\n\n    /**\n     * @notice Stores data for Twap derivative assets.\n     * @param secondsAgo the twap duration\n     * @param baseDecimals the base assets decimals\n     * @param quoteDecimals the quote assets decimals\n     * @param quoteToken the asset the twap quotes in\n     */\n    struct TwapDerivativeStorage {\n        uint32 secondsAgo;\n        uint8 baseDecimals;\n        uint8 quoteDecimals;\n        ERC20 quoteToken;\n    }\n\n    /**\n     * @notice Tried setting up a Twap for an asset where the underlying pools does not use the asset.\n     */\n    error PriceRouter__TwapAssetNotInPool();\n\n    /**\n     * @notice Provided secondsAgo does not meet minimum,\n     */\n    error PriceRouter__SecondsAgoDoesNotMeetMinimum();\n\n    /**\n     * @notice Returns Twap Derivative Storage\n     */\n    mapping(ERC20 => TwapDerivativeStorage) public getTwapDerivativeStorage;\n\n    /**\n     * @notice The smallest possible TWAP that can be used.\n     */\n    uint32 public constant MINIMUM_SECONDS_AGO = 900;\n\n    /**\n     * @notice Setup function for pricing Twap derivative assets.\n     * @dev Make sure that TWAP assets have sufficient observations, and increase them if not before adding.\n     * @dev _source The address of the Uniswap V3 pool.\n     * @dev _storage A TwapDerivativeStorage value defining valid prices.\n     */\n    function _setupPriceForTwapDerivative(ERC20 _asset, address _source, bytes memory _storage) internal {\n        TwapDerivativeStorage memory parameters = abi.decode(_storage, (TwapDerivativeStorage));\n\n        // Verify seconds ago is reasonable.\n        if (parameters.secondsAgo < MINIMUM_SECONDS_AGO) revert PriceRouter__SecondsAgoDoesNotMeetMinimum();\n\n        UniswapV3Pool pool = UniswapV3Pool(_source);\n\n        ERC20 token0 = ERC20(pool.token0());\n        ERC20 token1 = ERC20(pool.token1());\n        if (token0 == _asset) {\n            parameters.baseDecimals = _asset.decimals();\n            parameters.quoteDecimals = token1.decimals();\n            parameters.quoteToken = token1;\n        } else if (token1 == _asset) {\n            parameters.baseDecimals = _asset.decimals();\n            parameters.quoteDecimals = token0.decimals();\n            parameters.quoteToken = token0;\n        } else revert PriceRouter__TwapAssetNotInPool();\n\n        getTwapDerivativeStorage[_asset] = parameters;\n    }\n\n    /**\n     * @notice Get the price of a Twap derivative in terms of USD.\n     */\n    function _getPriceForTwapDerivative(ERC20 asset, address _source) internal view returns (uint256) {\n        TwapDerivativeStorage memory parameters = getTwapDerivativeStorage[asset];\n        (int24 arithmeticMeanTick, ) = OracleLibrary.consult(_source, parameters.secondsAgo);\n        // Get the amount of quote token each base token is worth.\n        uint256 quoteAmount = OracleLibrary.getQuoteAtTick(\n            arithmeticMeanTick,\n            uint128(10 ** parameters.baseDecimals),\n            address(asset),\n            address(parameters.quoteToken)\n        );\n        uint256 quotePrice = _getPriceInUSD(parameters.quoteToken, getAssetSettings[parameters.quoteToken]);\n        return quoteAmount.mulDivDown(quotePrice, 10 ** parameters.quoteDecimals);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\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 amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` 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 amount) 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 `amount` as the allowance of `spender` over the 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 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` 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(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Create2.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Create2.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.\n * `CREATE2` can be used to compute in advance the address where a smart\n * contract will be deployed, which allows for interesting new mechanisms known\n * as 'counterfactual interactions'.\n *\n * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more\n * information.\n */\nlibrary Create2 {\n    /**\n     * @dev Deploys a contract using `CREATE2`. The address where the contract\n     * will be deployed can be known in advance via {computeAddress}.\n     *\n     * The bytecode for a contract can be obtained from Solidity with\n     * `type(contractName).creationCode`.\n     *\n     * Requirements:\n     *\n     * - `bytecode` must not be empty.\n     * - `salt` must have not been used for `bytecode` already.\n     * - the factory must have a balance of at least `amount`.\n     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.\n     */\n    function deploy(\n        uint256 amount,\n        bytes32 salt,\n        bytes memory bytecode\n    ) internal returns (address addr) {\n        require(address(this).balance >= amount, \"Create2: insufficient balance\");\n        require(bytecode.length != 0, \"Create2: bytecode length is zero\");\n        /// @solidity memory-safe-assembly\n        assembly {\n            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)\n        }\n        require(addr != address(0), \"Create2: Failed on deploy\");\n    }\n\n    /**\n     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the\n     * `bytecodeHash` or `salt` will result in a new destination address.\n     */\n    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {\n        return computeAddress(salt, bytecodeHash, address(this));\n    }\n\n    /**\n     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at\n     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.\n     */\n    function computeAddress(\n        bytes32 salt,\n        bytes32 bytecodeHash,\n        address deployer\n    ) internal pure returns (address addr) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let ptr := mload(0x40) // Get free memory pointer\n\n            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |\n            // |-------------------|---------------------------------------------------------------------------|\n            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |\n            // | salt              |                                      BBBBBBBBBBBBB...BB                   |\n            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |\n            // | 0xFF              |            FF                                                             |\n            // |-------------------|---------------------------------------------------------------------------|\n            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |\n            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |\n\n            mstore(add(ptr, 0x40), bytecodeHash)\n            mstore(add(ptr, 0x20), salt)\n            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes\n            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff\n            mstore8(start, 0xff)\n            addr := keccak256(start, 85)\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\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, it is bubbled up by this\n     * function (like regular Solidity function calls).\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     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\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     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);\n        }\n    }\n}\n"
      },
      "src/modules/adaptors/Aave/V3/AaveV3ATokenAdaptor.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { BaseAdaptor, ERC20, SafeTransferLib, Fund, PriceRouter, Math } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { IPoolV3 } from \"src/interfaces/external/IPoolV3.sol\";\nimport { IAaveToken } from \"src/interfaces/external/IAaveToken.sol\";\nimport { IAaveOracle } from \"src/interfaces/external/IAaveOracle.sol\";\n\n/**\n * @title Aave aToken Adaptor\n * @notice Allows Funds to interact with Aave aToken positions.\n * @author crispymangoes\n */\ncontract AaveV3ATokenAdaptor is BaseAdaptor {\n    using SafeTransferLib for ERC20;\n    using Math for uint256;\n\n    //==================== Adaptor Data Specification ====================\n    // adaptorData = abi.encode(address aToken)\n    // Where:\n    // `aToken` is the aToken address position this adaptor is working with\n    //================= Configuration Data Specification =================\n    // configurationData = abi.encode(minimumHealthFactor uint256)\n    // Where:\n    // `minimumHealthFactor` dictates how much assets can be taken from this position\n    // If zero:\n    //      position returns ZERO for `withdrawableFrom`\n    // else:\n    //      position calculates `withdrawableFrom` based off minimum specified\n    //      position reverts if a user withdraw lowers health factor below minimum\n    //\n    // **************************** IMPORTANT ****************************\n    // Funds with multiple aToken positions MUST only specify minimum\n    // health factor on ONE of the positions. Failing to do so will result\n    // in user withdraws temporarily being blocked.\n    //====================================================================\n\n    /**\n     @notice Attempted withdraw would lower Fund health factor too low.\n     */\n    error AaveV3ATokenAdaptor__HealthFactorTooLow();\n\n    /**\n     * @notice Aave V3 oracle uses a different base asset than this contract was designed for.\n     */\n    error AaveV3ATokenAdaptor__OracleUsesDifferentBase();\n\n    /**\n     * @notice The Aave V3 Pool contract on current network.\n     * @dev For mainnet use 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2.\n     */\n    IPoolV3 public immutable pool;\n\n    /**\n     * @notice The Aave V3 Oracle on current network.\n     * @dev For mainnet use 0x54586bE62E3c3580375aE3723C145253060Ca0C2.\n     */\n    IAaveOracle public immutable aaveOracle;\n\n    /**\n     * @notice Minimum Health Factor enforced after every aToken withdraw.\n     * @notice Overwrites strategist set minimums if they are lower.\n     */\n    uint256 public immutable minimumHealthFactor;\n\n    constructor(address v3Pool, address v3Oracle, uint256 minHealthFactor) {\n        _verifyConstructorMinimumHealthFactor(minHealthFactor);\n        pool = IPoolV3(v3Pool);\n        aaveOracle = IAaveOracle(v3Oracle);\n        minimumHealthFactor = minHealthFactor;\n    }\n\n    //============================================ Global Functions ===========================================\n    /**\n     * @dev Identifier unique to this adaptor for a shared registry.\n     * Normally the identifier would just be the address of this contract, but this\n     * Identifier is needed during Fund Delegate Call Operations, so getting the address\n     * of the adaptor is more difficult.\n     */\n    function identifier() public pure override returns (bytes32) {\n        return keccak256(abi.encode(\"Aave V3 aToken Adaptor V 1.2\"));\n    }\n\n    //============================================ Implement Base Functions ===========================================\n    /**\n     * @notice Fund must approve Pool to spend its assets, then call deposit to lend its assets.\n     * @param assets the amount of assets to lend on Aave\n     * @param adaptorData adaptor data containining the abi encoded aToken\n     * @dev configurationData is NOT used because this action will only increase the health factor\n     */\n    function deposit(uint256 assets, bytes memory adaptorData, bytes memory) public override {\n        // Deposit assets to Aave.\n        IAaveToken aToken = IAaveToken(abi.decode(adaptorData, (address)));\n        ERC20 token = ERC20(aToken.UNDERLYING_ASSET_ADDRESS());\n        token.safeApprove(address(pool), assets);\n        pool.supply(address(token), assets, address(this), 0);\n\n        // Zero out approvals if necessary.\n        _revokeExternalApproval(token, address(pool));\n    }\n\n    /**\n     @notice Funds must withdraw from Aave, check if a minimum health factor is specified\n     *       then transfer assets to receiver.\n     * @dev Important to verify that external receivers are allowed if receiver is not Fund address.\n     * @param assets the amount of assets to withdraw from Aave\n     * @param receiver the address to send withdrawn assets to\n     * @param adaptorData adaptor data containining the abi encoded aToken\n     * @param configData abi encoded minimum health factor, if zero user withdraws are not allowed.\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        bytes memory adaptorData,\n        bytes memory configData\n    ) public override {\n        // Run external receiver check.\n        _externalReceiverCheck(receiver);\n\n        // Withdraw assets from Aave.\n        IAaveToken token = IAaveToken(abi.decode(adaptorData, (address)));\n        pool.withdraw(token.UNDERLYING_ASSET_ADDRESS(), assets, address(this));\n\n        (, uint256 totalDebtBase, , , , uint256 healthFactor) = pool.getUserAccountData(address(this));\n        if (totalDebtBase > 0) {\n            // If fund has entered an EMode, and has debt, user withdraws are not allowed.\n            if (pool.getUserEMode(msg.sender) != 0) revert BaseAdaptor__UserWithdrawsNotAllowed();\n\n            // Run minimum health factor checks.\n            uint256 minHealthFactor = abi.decode(configData, (uint256));\n            if (minHealthFactor == 0) {\n                revert BaseAdaptor__UserWithdrawsNotAllowed();\n            }\n            // Check if adaptor minimum health factor is more conservative than strategist set.\n            if (minHealthFactor < minimumHealthFactor) minHealthFactor = minimumHealthFactor;\n            if (healthFactor < minHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n        }\n\n        // Transfer assets to receiver.\n        ERC20(token.UNDERLYING_ASSET_ADDRESS()).safeTransfer(receiver, assets);\n    }\n\n    /**\n     * @notice Uses configurartion data minimum health factor to calculate withdrawable assets from Aave.\n     * @dev Applies a `cushion` value to the health factor checks and calculation.\n     *      The goal of this is to minimize scenarios where users are withdrawing a very small amount of\n     *      assets from Aave. This function returns zero if\n     *      -minimum health factor is NOT set.\n     *      -the current health factor is less than the minimum health factor + 2x `cushion`\n     *      Otherwise this function calculates the withdrawable amount using\n     *      minimum health factor + `cushion` for its calcualtions.\n     * @dev It is possible for the math below to lose a small amount of precision since it is only\n     *      maintaining 18 decimals during the calculation, but this is desired since\n     *      doing so lowers the withdrawable from amount which in turn raises the health factor.\n     */\n    function withdrawableFrom(\n        bytes memory adaptorData,\n        bytes memory configData\n    ) public view override returns (uint256) {\n        IAaveToken token = IAaveToken(abi.decode(adaptorData, (address)));\n\n        (\n            uint256 totalCollateralBase,\n            uint256 totalDebtBase,\n            ,\n            uint256 currentLiquidationThreshold,\n            ,\n            uint256 healthFactor\n        ) = pool.getUserAccountData(msg.sender);\n\n        // If Fund has no Aave debt, then return the funds balance of the aToken.\n        if (totalDebtBase == 0) return ERC20(address(token)).balanceOf(msg.sender);\n\n        // Fund has Aave debt, so if fund is entered into a non zero emode, return 0.\n        if (pool.getUserEMode(msg.sender) != 0) return 0;\n\n        // Otherwise we need to look at minimum health factor.\n        uint256 minHealthFactor = abi.decode(configData, (uint256));\n        // Check if minimum health factor is set.\n        // If not the strategist does not want users to withdraw from this position.\n        if (minHealthFactor == 0) return 0;\n        // Check if adaptor minimum health factor is more conservative than strategist set.\n        if (minHealthFactor < minimumHealthFactor) minHealthFactor = minimumHealthFactor;\n\n        uint256 maxBorrowableWithMin;\n\n        // Choose 0.01 for cushion value. Value can be adjusted based off testing results.\n        uint256 cushion = 0.01e18;\n\n        // Add cushion to min health factor.\n        minHealthFactor += cushion;\n\n        // If current health factor is less than the minHealthFactor + 2X cushion, return 0.\n        if (healthFactor < (minHealthFactor + cushion)) return 0;\n        // Calculate max amount withdrawable while preserving minimum health factor.\n        else {\n            maxBorrowableWithMin =\n                totalCollateralBase -\n                minHealthFactor.mulDivDown(totalDebtBase, (currentLiquidationThreshold * 1e14));\n        }\n        /// @dev We want right side of \"-\" to have 8 decimals, so we need to dviide by 18 decimals.\n        // `currentLiquidationThreshold` has 4 decimals, so multiply by 1e14 to get 18 decimals on denominator.\n\n        // Need to convert Base into position underlying.\n        ERC20 underlying = ERC20(token.UNDERLYING_ASSET_ADDRESS());\n\n        // Convert `maxBorrowableWithMin` from Base to position underlying asset.\n        PriceRouter priceRouter = Fund(msg.sender).priceRouter();\n        uint256 underlyingToUSD = priceRouter.getPriceInUSD(underlying);\n        uint256 withdrawable = maxBorrowableWithMin.mulDivDown(10 ** underlying.decimals(), underlyingToUSD);\n        uint256 balance = ERC20(address(token)).balanceOf(msg.sender);\n        // Check if withdrawable is greater than the position balance and if so return the balance instead of withdrawable.\n        return withdrawable > balance ? balance : withdrawable;\n    }\n\n    /**\n     * @notice Returns the funds balance of the positions aToken.\n     */\n    function balanceOf(bytes memory adaptorData) public view override returns (uint256) {\n        address token = abi.decode(adaptorData, (address));\n        return ERC20(token).balanceOf(msg.sender);\n    }\n\n    /**\n     * @notice Returns the positions aToken underlying asset.\n     */\n    function assetOf(bytes memory adaptorData) public view override returns (ERC20) {\n        IAaveToken token = IAaveToken(abi.decode(adaptorData, (address)));\n        return ERC20(token.UNDERLYING_ASSET_ADDRESS());\n    }\n\n    function assetsUsed(bytes memory adaptorData) public view override returns (ERC20[] memory assets) {\n        assets = new ERC20[](1);\n        assets[0] = assetOf(adaptorData);\n\n        // Make sure Aave Oracle uses USD base asset with 8 decimals.\n        // BASE_CURRENCY and BASE_CURRENCY_UNIT are both immutable values, so only check this on initial position setup.\n        if (aaveOracle.BASE_CURRENCY() != address(0) || aaveOracle.BASE_CURRENCY_UNIT() != 1e8)\n            revert AaveV3ATokenAdaptor__OracleUsesDifferentBase();\n    }\n\n    /**\n     * @notice This adaptor returns collateral, and not debt.\n     */\n    function isDebt() public pure override returns (bool) {\n        return false;\n    }\n\n    //============================================ Strategist Functions ===========================================\n    /**\n     * @notice Allows strategists to lend assets on Aave.\n     * @dev Uses `_maxAvailable` helper function, see BaseAdaptor.sol\n     * @param tokenToDeposit the token to lend on Aave\n     * @param amountToDeposit the amount of `tokenToDeposit` to lend on Aave.\n     */\n    function depositToAave(ERC20 tokenToDeposit, uint256 amountToDeposit) public {\n        amountToDeposit = _maxAvailable(tokenToDeposit, amountToDeposit);\n        tokenToDeposit.safeApprove(address(pool), amountToDeposit);\n        pool.supply(address(tokenToDeposit), amountToDeposit, address(this), 0);\n\n        // Zero out approvals if necessary.\n        _revokeExternalApproval(tokenToDeposit, address(pool));\n    }\n\n    /**\n     * @notice Allows strategists to withdraw assets from Aave.\n     * @param tokenToWithdraw the token to withdraw from Aave.\n     * @param amountToWithdraw the amount of `tokenToWithdraw` to withdraw from Aave\n     */\n    function withdrawFromAave(ERC20 tokenToWithdraw, uint256 amountToWithdraw) public {\n        pool.withdraw(address(tokenToWithdraw), amountToWithdraw, address(this));\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(address(this));\n        if (healthFactor < minimumHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategists to adjust an asset's isolation mode.\n     */\n    function adjustIsolationModeAssetAsCollateral(ERC20 asset, bool useAsCollateral) public {\n        pool.setUserUseReserveAsCollateral(address(asset), useAsCollateral);\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(address(this));\n        if (healthFactor < minimumHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategists to enter different EModes.\n     */\n    function changeEMode(uint8 categoryId) public {\n        pool.setUserEMode(categoryId);\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(address(this));\n        if (healthFactor < minimumHealthFactor) revert AaveV3ATokenAdaptor__HealthFactorTooLow();\n    }\n}\n"
      },
      "src/modules/adaptors/Aave/V3/AaveV3DebtTokenAdaptor.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { BaseAdaptor, ERC20, SafeTransferLib, Fund, Registry } from \"src/modules/adaptors/BaseAdaptor.sol\";\nimport { IPoolV3 } from \"src/interfaces/external/IPoolV3.sol\";\nimport { IAaveToken } from \"src/interfaces/external/IAaveToken.sol\";\n\n/**\n * @title Aave debtToken Adaptor\n * @notice Allows Funds to interact with Aave debtToken positions.\n * @author crispymangoes\n */\ncontract AaveV3DebtTokenAdaptor is BaseAdaptor {\n    using SafeTransferLib for ERC20;\n\n    //==================== Adaptor Data Specification ====================\n    // adaptorData = abi.encode(address debtToken)\n    // Where:\n    // `debtToken` is the debtToken address position this adaptor is working with\n    //================= Configuration Data Specification =================\n    // NOT USED\n    //====================================================================\n\n    /**\n     @notice Attempted borrow would lower Fund health factor too low.\n     */\n    error AaveV3DebtTokenAdaptor__HealthFactorTooLow();\n\n    /**\n     * @notice Strategist attempted to open an untracked Aave loan.\n     * @param untrackedDebtPosition the address of the untracked loan\n     */\n    error AaveV3DebtTokenAdaptor__DebtPositionsMustBeTracked(address untrackedDebtPosition);\n\n    /**\n     * @notice The Aave V3 Pool contract on current network.\n     * @dev For mainnet use 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2.\n     */\n    IPoolV3 public immutable pool;\n\n    /**\n     * @notice Minimum Health Factor enforced after every borrow.\n     * @notice Overwrites strategist set minimums if they are lower.\n     */\n    uint256 public immutable minimumHealthFactor;\n\n    constructor(address v3Pool, uint256 minHealthFactor) {\n        _verifyConstructorMinimumHealthFactor(minHealthFactor);\n        pool = IPoolV3(v3Pool);\n        minimumHealthFactor = minHealthFactor;\n    }\n\n    //============================================ Global Functions ===========================================\n    /**\n     * @dev Identifier unique to this adaptor for a shared registry.\n     * Normally the identifier would just be the address of this contract, but this\n     * Identifier is needed during Fund Delegate Call Operations, so getting the address\n     * of the adaptor is more difficult.\n     */\n    function identifier() public pure override returns (bytes32) {\n        return keccak256(abi.encode(\"Aave V3 debtToken Adaptor V 1.1\"));\n    }\n\n    //============================================ Implement Base Functions ===========================================\n\n    /**\n     * @notice User deposits are NOT allowed into this position.\n     */\n    function deposit(uint256, bytes memory, bytes memory) public pure override {\n        revert BaseAdaptor__UserDepositsNotAllowed();\n    }\n\n    /**\n     * @notice User withdraws are NOT allowed from this position.\n     */\n    function withdraw(uint256, address, bytes memory, bytes memory) public pure override {\n        revert BaseAdaptor__UserWithdrawsNotAllowed();\n    }\n\n    /**\n     * @notice This position is a debt position, and user withdraws are not allowed so\n     *         this position must return 0 for withdrawableFrom.\n     */\n    function withdrawableFrom(bytes memory, bytes memory) public pure override returns (uint256) {\n        return 0;\n    }\n\n    /**\n     * @notice Returns the funds balance of the positions debtToken.\n     */\n    function balanceOf(bytes memory adaptorData) public view override returns (uint256) {\n        address token = abi.decode(adaptorData, (address));\n        return ERC20(token).balanceOf(msg.sender);\n    }\n\n    /**\n     * @notice Returns the positions debtToken underlying asset.\n     */\n    function assetOf(bytes memory adaptorData) public view override returns (ERC20) {\n        IAaveToken token = IAaveToken(abi.decode(adaptorData, (address)));\n        return ERC20(token.UNDERLYING_ASSET_ADDRESS());\n    }\n\n    /**\n     * @notice This adaptor reports values in terms of debt.\n     */\n    function isDebt() public pure override returns (bool) {\n        return true;\n    }\n\n    //============================================ Strategist Functions ===========================================\n\n    /**\n     * @notice Allows strategists to borrow assets from Aave.\n     * @notice `debtTokenToBorrow` must be the debtToken, NOT the underlying ERC20.\n     * @param debtTokenToBorrow the debtToken to borrow on Aave\n     * @param amountToBorrow the amount of `debtTokenToBorrow` to borrow on Aave.\n     */\n    function borrowFromAave(ERC20 debtTokenToBorrow, uint256 amountToBorrow) public {\n        // Check that debt position is properly set up to be tracked in the Fund.\n        bytes32 positionHash = keccak256(abi.encode(identifier(), true, abi.encode(address(debtTokenToBorrow))));\n        uint32 positionId = Fund(address(this)).registry().getPositionHashToPositionId(positionHash);\n        if (!Fund(address(this)).isPositionUsed(positionId))\n            revert AaveV3DebtTokenAdaptor__DebtPositionsMustBeTracked(address(debtTokenToBorrow));\n\n        // Open up new variable debt position on Aave.\n        pool.borrow(\n            IAaveToken(address(debtTokenToBorrow)).UNDERLYING_ASSET_ADDRESS(),\n            amountToBorrow,\n            2,\n            0,\n            address(this)\n        ); // 2 is the interest rate mode, either 1 for stable or 2 for variable\n\n        // Check that health factor is above adaptor minimum.\n        (, , , , , uint256 healthFactor) = pool.getUserAccountData(address(this));\n        if (healthFactor < minimumHealthFactor) revert AaveV3DebtTokenAdaptor__HealthFactorTooLow();\n    }\n\n    /**\n     * @notice Allows strategists to repay loan debt on Aave.\n     * @dev Uses `_maxAvailable` helper function, see BaseAdaptor.sol\n     * @param tokenToRepay the underlying ERC20 token you want to repay, NOT the debtToken.\n     * @param amountToRepay the amount of `tokenToRepay` to repay with.\n     */\n    function repayAaveDebt(ERC20 tokenToRepay, uint256 amountToRepay) public {\n        tokenToRepay.safeApprove(address(pool), amountToRepay);\n        pool.repay(address(tokenToRepay), amountToRepay, 2, address(this)); // 2 is the interest rate mode,  either 1 for stable or 2 for variable\n\n        // Zero out approvals if necessary.\n        _revokeExternalApproval(tokenToRepay, address(pool));\n    }\n\n    /**\n     * @notice Allows strategist to use aTokens to repay debt tokens with the same underlying.\n     */\n    function repayWithATokens(ERC20 underlying, uint256 amount) public {\n        pool.repayWithATokens(address(underlying), amount, 2);\n    }\n\n    /**\n     * @notice allows strategist to have Funds take out flash loans.\n     * @param loanToken address array of tokens to take out loans\n     * @param loanAmount uint256 array of loan amounts for each `loanToken`\n     * @dev `modes` is always a zero array meaning that this flash loan can NOT take on new debt positions, it must be paid in full.\n     */\n    function flashLoan(address[] memory loanToken, uint256[] memory loanAmount, bytes memory params) public {\n        require(loanToken.length == loanAmount.length, \"Input length mismatch.\");\n        uint256[] memory modes = new uint256[](loanToken.length);\n        pool.flashLoan(address(this), loanToken, loanAmount, modes, address(this), params, 0);\n    }\n}\n"
      },
      "src/interfaces/external/IPoolV3.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity ^0.8.0;\n\nimport { IPoolAddressesProvider } from \"./IPoolAddressesProvider.sol\";\nimport { DataTypes } from \"./DataTypes.sol\";\n\n/**\n * @title IPool\n * @author Aave\n * @notice Defines the basic interface for an Aave Pool.\n */\ninterface IPoolV3 {\n    /**\n     * @dev Emitted on mintUnbacked()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address initiating the supply\n     * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens\n     * @param amount The amount of supplied assets\n     * @param referralCode The referral code used\n     */\n    event MintUnbacked(\n        address indexed reserve,\n        address user,\n        address indexed onBehalfOf,\n        uint256 amount,\n        uint16 indexed referralCode\n    );\n\n    /**\n     * @dev Emitted on backUnbacked()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param backer The address paying for the backing\n     * @param amount The amount added as backing\n     * @param fee The amount paid in fees\n     */\n    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);\n\n    /**\n     * @dev Emitted on supply()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address initiating the supply\n     * @param onBehalfOf The beneficiary of the supply, receiving the aTokens\n     * @param amount The amount supplied\n     * @param referralCode The referral code used\n     */\n    event Supply(\n        address indexed reserve,\n        address user,\n        address indexed onBehalfOf,\n        uint256 amount,\n        uint16 indexed referralCode\n    );\n\n    /**\n     * @dev Emitted on withdraw()\n     * @param reserve The address of the underlying asset being withdrawn\n     * @param user The address initiating the withdrawal, owner of aTokens\n     * @param to The address that will receive the underlying\n     * @param amount The amount to be withdrawn\n     */\n    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);\n\n    /**\n     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened\n     * @param reserve The address of the underlying asset being borrowed\n     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just\n     * initiator of the transaction on flashLoan()\n     * @param onBehalfOf The address that will be getting the debt\n     * @param amount The amount borrowed out\n     * @param interestRateMode The rate mode: 1 for Stable, 2 for Variable\n     * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray\n     * @param referralCode The referral code used\n     */\n    event Borrow(\n        address indexed reserve,\n        address user,\n        address indexed onBehalfOf,\n        uint256 amount,\n        DataTypes.InterestRateMode interestRateMode,\n        uint256 borrowRate,\n        uint16 indexed referralCode\n    );\n\n    /**\n     * @dev Emitted on repay()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The beneficiary of the repayment, getting his debt reduced\n     * @param repayer The address of the user initiating the repay(), providing the funds\n     * @param amount The amount repaid\n     * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly\n     */\n    event Repay(\n        address indexed reserve,\n        address indexed user,\n        address indexed repayer,\n        uint256 amount,\n        bool useATokens\n    );\n\n    /**\n     * @dev Emitted on swapBorrowRateMode()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address of the user swapping his rate mode\n     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable\n     */\n    event SwapBorrowRateMode(\n        address indexed reserve,\n        address indexed user,\n        DataTypes.InterestRateMode interestRateMode\n    );\n\n    /**\n     * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets\n     * @param asset The address of the underlying asset of the reserve\n     * @param totalDebt The total isolation mode debt for the reserve\n     */\n    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);\n\n    /**\n     * @dev Emitted when the user selects a certain asset category for eMode\n     * @param user The address of the user\n     * @param categoryId The category id\n     */\n    event UserEModeSet(address indexed user, uint8 categoryId);\n\n    /**\n     * @dev Emitted on setUserUseReserveAsCollateral()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address of the user enabling the usage as collateral\n     */\n    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);\n\n    /**\n     * @dev Emitted on setUserUseReserveAsCollateral()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address of the user enabling the usage as collateral\n     */\n    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);\n\n    /**\n     * @dev Emitted on rebalanceStableBorrowRate()\n     * @param reserve The address of the underlying asset of the reserve\n     * @param user The address of the user for which the rebalance has been executed\n     */\n    event RebalanceStableBorrowRate(address indexed reserve, address indexed user);\n\n    /**\n     * @dev Emitted on flashLoan()\n     * @param target The address of the flash loan receiver contract\n     * @param initiator The address initiating the flash loan\n     * @param asset The address of the asset being flash borrowed\n     * @param amount The amount flash borrowed\n     * @param interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt\n     * @param premium The fee flash borrowed\n     * @param referralCode The referral code used\n     */\n    event FlashLoan(\n        address indexed target,\n        address initiator,\n        address indexed asset,\n        uint256 amount,\n        DataTypes.InterestRateMode interestRateMode,\n        uint256 premium,\n        uint16 indexed referralCode\n    );\n\n    /**\n     * @dev Emitted when a borrower is liquidated.\n     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation\n     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation\n     * @param user The address of the borrower getting liquidated\n     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover\n     * @param liquidatedCollateralAmount The amount of collateral received by the liquidator\n     * @param liquidator The address of the liquidator\n     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants\n     * to receive the underlying collateral asset directly\n     */\n    event LiquidationCall(\n        address indexed collateralAsset,\n        address indexed debtAsset,\n        address indexed user,\n        uint256 debtToCover,\n        uint256 liquidatedCollateralAmount,\n        address liquidator,\n        bool receiveAToken\n    );\n\n    /**\n     * @dev Emitted when the state of a reserve is updated.\n     * @param reserve The address of the underlying asset of the reserve\n     * @param liquidityRate The next liquidity rate\n     * @param stableBorrowRate The next stable borrow rate\n     * @param variableBorrowRate The next variable borrow rate\n     * @param liquidityIndex The next liquidity index\n     * @param variableBorrowIndex The next variable borrow index\n     */\n    event ReserveDataUpdated(\n        address indexed reserve,\n        uint256 liquidityRate,\n        uint256 stableBorrowRate,\n        uint256 variableBorrowRate,\n        uint256 liquidityIndex,\n        uint256 variableBorrowIndex\n    );\n\n    /**\n     * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.\n     * @param reserve The address of the reserve\n     * @param amountMinted The amount minted to the treasury\n     */\n    event MintedToTreasury(address indexed reserve, uint256 amountMinted);\n\n    /**\n     * @notice Mints an `amount` of aTokens to the `onBehalfOf`\n     * @param asset The address of the underlying asset to mint\n     * @param amount The amount to mint\n     * @param onBehalfOf The address that will receive the aTokens\n     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     */\n    function mintUnbacked(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;\n\n    /**\n     * @notice Back the current unbacked underlying with `amount` and pay `fee`.\n     * @param asset The address of the underlying asset to back\n     * @param amount The amount to back\n     * @param fee The amount paid in fees\n     * @return The backed amount\n     */\n    function backUnbacked(address asset, uint256 amount, uint256 fee) external returns (uint256);\n\n    /**\n     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC\n     * @param asset The address of the underlying asset to supply\n     * @param amount The amount to be supplied\n     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n     *   is a different wallet\n     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     */\n    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;\n\n    /**\n     * @notice Supply with transfer approval of asset to be supplied done via permit function\n     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713\n     * @param asset The address of the underlying asset to supply\n     * @param amount The amount to be supplied\n     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n     *   is a different wallet\n     * @param deadline The deadline timestamp that the permit is valid\n     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     * @param permitV The V parameter of ERC712 permit sig\n     * @param permitR The R parameter of ERC712 permit sig\n     * @param permitS The S parameter of ERC712 permit sig\n     */\n    function supplyWithPermit(\n        address asset,\n        uint256 amount,\n        address onBehalfOf,\n        uint16 referralCode,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external;\n\n    /**\n     * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n     * @param asset The address of the underlying asset to withdraw\n     * @param amount The underlying amount to be withdrawn\n     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance\n     * @param to The address that will receive the underlying, same as msg.sender if the user\n     *   wants to receive it on his own wallet, or a different address if the beneficiary is a\n     *   different wallet\n     * @return The final amount withdrawn\n     */\n    function withdraw(address asset, uint256 amount, address to) external returns (uint256);\n\n    /**\n     * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower\n     * already supplied enough collateral, or he was given enough allowance by a credit delegator on the\n     * corresponding debt token (StableDebtToken or VariableDebtToken)\n     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet\n     *   and 100 stable/variable debt tokens, depending on the `interestRateMode`\n     * @param asset The address of the underlying asset to borrow\n     * @param amount The amount to be borrowed\n     * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable\n     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself\n     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator\n     * if he has been given credit delegation allowance\n     */\n    function borrow(\n        address asset,\n        uint256 amount,\n        uint256 interestRateMode,\n        uint16 referralCode,\n        address onBehalfOf\n    ) external;\n\n    /**\n     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned\n     * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address\n     * @param asset The address of the borrowed underlying asset previously borrowed\n     * @param amount The amount to repay\n     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\n     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable\n     * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the\n     * user calling the function if he wants to reduce/remove his own debt, or the address of any other\n     * other borrower whose debt should be removed\n     * @return The final amount repaid\n     */\n    function repay(\n        address asset,\n        uint256 amount,\n        uint256 interestRateMode,\n        address onBehalfOf\n    ) external returns (uint256);\n\n    /**\n     * @notice Repay with transfer approval of asset to be repaid done via permit function\n     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713\n     * @param asset The address of the borrowed underlying asset previously borrowed\n     * @param amount The amount to repay\n     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\n     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable\n     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the\n     * user calling the function if he wants to reduce/remove his own debt, or the address of any other\n     * other borrower whose debt should be removed\n     * @param deadline The deadline timestamp that the permit is valid\n     * @param permitV The V parameter of ERC712 permit sig\n     * @param permitR The R parameter of ERC712 permit sig\n     * @param permitS The S parameter of ERC712 permit sig\n     * @return The final amount repaid\n     */\n    function repayWithPermit(\n        address asset,\n        uint256 amount,\n        uint256 interestRateMode,\n        address onBehalfOf,\n        uint256 deadline,\n        uint8 permitV,\n        bytes32 permitR,\n        bytes32 permitS\n    ) external returns (uint256);\n\n    /**\n     * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the\n     * equivalent debt tokens\n     * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens\n     * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken\n     * balance is not enough to cover the whole debt\n     * @param asset The address of the borrowed underlying asset previously borrowed\n     * @param amount The amount to repay\n     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\n     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable\n     * @return The final amount repaid\n     */\n    function repayWithATokens(address asset, uint256 amount, uint256 interestRateMode) external returns (uint256);\n\n    /**\n     * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa\n     * @param asset The address of the underlying asset borrowed\n     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable\n     */\n    function swapBorrowRateMode(address asset, uint256 interestRateMode) external;\n\n    /**\n     * @notice Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.\n     * - Users can be rebalanced if the following conditions are satisfied:\n     *     1. Usage ratio is above 95%\n     *     2. the current supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too\n     *        much has been borrowed at a stable rate and suppliers are not earning enough\n     * @param asset The address of the underlying asset borrowed\n     * @param user The address of the user to be rebalanced\n     */\n    function rebalanceStableBorrowRate(address asset, address user) external;\n\n    /**\n     * @notice Allows suppliers to enable/disable a specific supplied asset as collateral\n     * @param asset The address of the underlying asset supplied\n     * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise\n     */\n    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;\n\n    /**\n     * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1\n     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives\n     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk\n     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation\n     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation\n     * @param user The address of the borrower getting liquidated\n     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover\n     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants\n     * to receive the underlying collateral asset directly\n     */\n    function liquidationCall(\n        address collateralAsset,\n        address debtAsset,\n        address user,\n        uint256 debtToCover,\n        bool receiveAToken\n    ) external;\n\n    /**\n     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,\n     * as long as the amount taken plus a fee is returned.\n     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept\n     * into consideration. For further details please visit https://docs.aave.com/developers/\n     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface\n     * @param assets The addresses of the assets being flash-borrowed\n     * @param amounts The amounts of the assets being flash-borrowed\n     * @param interestRateModes Types of the debt to open if the flash loan is not returned:\n     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver\n     *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address\n     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address\n     * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2\n     * @param params Variadic packed params to pass to the receiver as extra information\n     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     */\n    function flashLoan(\n        address receiverAddress,\n        address[] calldata assets,\n        uint256[] calldata amounts,\n        uint256[] calldata interestRateModes,\n        address onBehalfOf,\n        bytes calldata params,\n        uint16 referralCode\n    ) external;\n\n    /**\n     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,\n     * as long as the amount taken plus a fee is returned.\n     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept\n     * into consideration. For further details please visit https://docs.aave.com/developers/\n     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface\n     * @param asset The address of the asset being flash-borrowed\n     * @param amount The amount of the asset being flash-borrowed\n     * @param params Variadic packed params to pass to the receiver as extra information\n     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     */\n    function flashLoanSimple(\n        address receiverAddress,\n        address asset,\n        uint256 amount,\n        bytes calldata params,\n        uint16 referralCode\n    ) external;\n\n    /**\n     * @notice Returns the user account data across all the reserves\n     * @param user The address of the user\n     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed\n     * @return totalDebtBase The total debt of the user in the base currency used by the price feed\n     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed\n     * @return currentLiquidationThreshold The liquidation threshold of the user\n     * @return ltv The loan to value of The user\n     * @return healthFactor The current health factor of the user\n     */\n    function getUserAccountData(\n        address user\n    )\n        external\n        view\n        returns (\n            uint256 totalCollateralBase,\n            uint256 totalDebtBase,\n            uint256 availableBorrowsBase,\n            uint256 currentLiquidationThreshold,\n            uint256 ltv,\n            uint256 healthFactor\n        );\n\n    /**\n     * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an\n     * interest rate strategy\n     * @dev Only callable by the PoolConfigurator contract\n     * @param asset The address of the underlying asset of the reserve\n     * @param aTokenAddress The address of the aToken that will be assigned to the reserve\n     * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve\n     * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve\n     * @param interestRateStrategyAddress The address of the interest rate strategy contract\n     */\n    function initReserve(\n        address asset,\n        address aTokenAddress,\n        address stableDebtAddress,\n        address variableDebtAddress,\n        address interestRateStrategyAddress\n    ) external;\n\n    /**\n     * @notice Drop a reserve\n     * @dev Only callable by the PoolConfigurator contract\n     * @param asset The address of the underlying asset of the reserve\n     */\n    function dropReserve(address asset) external;\n\n    /**\n     * @notice Updates the address of the interest rate strategy contract\n     * @dev Only callable by the PoolConfigurator contract\n     * @param asset The address of the underlying asset of the reserve\n     * @param rateStrategyAddress The address of the interest rate strategy contract\n     */\n    function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) external;\n\n    /**\n     * @notice Sets the configuration bitmap of the reserve as a whole\n     * @dev Only callable by the PoolConfigurator contract\n     * @param asset The address of the underlying asset of the reserve\n     * @param configuration The new configuration bitmap\n     */\n    function setConfiguration(address asset, DataTypes.ReserveConfigurationMap calldata configuration) external;\n\n    /**\n     * @notice Returns the configuration of the reserve\n     * @param asset The address of the underlying asset of the reserve\n     * @return The configuration of the reserve\n     */\n    function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory);\n\n    /**\n     * @notice Returns the configuration of the user across all the reserves\n     * @param user The user address\n     * @return The configuration of the user\n     */\n    function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory);\n\n    /**\n     * @notice Returns the normalized income of the reserve\n     * @param asset The address of the underlying asset of the reserve\n     * @return The reserve's normalized income\n     */\n    function getReserveNormalizedIncome(address asset) external view returns (uint256);\n\n    /**\n     * @notice Returns the normalized variable debt per unit of asset\n     * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a\n     * \"dynamic\" variable index based on time, current stored index and virtual rate at the current\n     * moment (approx. a borrower would get if opening a position). This means that is always used in\n     * combination with variable debt supply/balances.\n     * If using this function externally, consider that is possible to have an increasing normalized\n     * variable debt that is not equivalent to how the variable debt index would be updated in storage\n     * (e.g. only updates with non-zero variable debt supply)\n     * @param asset The address of the underlying asset of the reserve\n     * @return The reserve normalized variable debt\n     */\n    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);\n\n    /**\n     * @notice Returns the state and configuration of the reserve\n     * @param asset The address of the underlying asset of the reserve\n     * @return The state and configuration data of the reserve\n     */\n    function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);\n\n    /**\n     * @notice Validates and finalizes an aToken transfer\n     * @dev Only callable by the overlying aToken of the `asset`\n     * @param asset The address of the underlying asset of the aToken\n     * @param from The user from which the aTokens are transferred\n     * @param to The user receiving the aTokens\n     * @param amount The amount being transferred/withdrawn\n     * @param balanceFromBefore The aToken balance of the `from` user before the transfer\n     * @param balanceToBefore The aToken balance of the `to` user before the transfer\n     */\n    function finalizeTransfer(\n        address asset,\n        address from,\n        address to,\n        uint256 amount,\n        uint256 balanceFromBefore,\n        uint256 balanceToBefore\n    ) external;\n\n    /**\n     * @notice Returns the list of the underlying assets of all the initialized reserves\n     * @dev It does not include dropped reserves\n     * @return The addresses of the underlying assets of the initialized reserves\n     */\n    function getReservesList() external view returns (address[] memory);\n\n    /**\n     * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct\n     * @param id The id of the reserve as stored in the DataTypes.ReserveData struct\n     * @return The address of the reserve associated with id\n     */\n    function getReserveAddressById(uint16 id) external view returns (address);\n\n    /**\n     * @notice Returns the PoolAddressesProvider connected to this contract\n     * @return The address of the PoolAddressesProvider\n     */\n    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);\n\n    /**\n     * @notice Updates the protocol fee on the bridging\n     * @param bridgeProtocolFee The part of the premium sent to the protocol treasury\n     */\n    function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;\n\n    /**\n     * @notice Updates flash loan premiums. Flash loan premium consists of two parts:\n     * - A part is sent to aToken holders as extra, one time accumulated interest\n     * - A part is collected by the protocol treasury\n     * @dev The total premium is calculated on the total borrowed amount\n     * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`\n     * @dev Only callable by the PoolConfigurator contract\n     * @param flashLoanPremiumTotal The total premium, expressed in bps\n     * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps\n     */\n    function updateFlashloanPremiums(uint128 flashLoanPremiumTotal, uint128 flashLoanPremiumToProtocol) external;\n\n    /**\n     * @notice Configures a new category for the eMode.\n     * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.\n     * The category 0 is reserved as it's the default for volatile assets\n     * @param id The id of the category\n     * @param config The configuration of the category\n     */\n    function configureEModeCategory(uint8 id, DataTypes.EModeCategory memory config) external;\n\n    /**\n     * @notice Returns the data of an eMode category\n     * @param id The id of the category\n     * @return The configuration data of the category\n     */\n    function getEModeCategoryData(uint8 id) external view returns (DataTypes.EModeCategory memory);\n\n    /**\n     * @notice Allows a user to use the protocol in eMode\n     * @param categoryId The id of the category\n     */\n    function setUserEMode(uint8 categoryId) external;\n\n    /**\n     * @notice Returns the eMode the user is using\n     * @param user The address of the user\n     * @return The eMode id\n     */\n    function getUserEMode(address user) external view returns (uint256);\n\n    /**\n     * @notice Resets the isolation mode total debt of the given asset to zero\n     * @dev It requires the given asset has zero debt ceiling\n     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt\n     */\n    function resetIsolationModeTotalDebt(address asset) external;\n\n    /**\n     * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate\n     * @return The percentage of available liquidity to borrow, expressed in bps\n     */\n    function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256);\n\n    /**\n     * @notice Returns the total fee on flash loans\n     * @return The total fee on flashloans\n     */\n    function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);\n\n    /**\n     * @notice Returns the part of the bridge fees sent to protocol\n     * @return The bridge fee sent to the protocol treasury\n     */\n    function BRIDGE_PROTOCOL_FEE() external view returns (uint256);\n\n    /**\n     * @notice Returns the part of the flashloan fees sent to protocol\n     * @return The flashloan fee sent to the protocol treasury\n     */\n    function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);\n\n    /**\n     * @notice Returns the maximum number of reserves supported to be listed in this Pool\n     * @return The maximum number of reserves supported\n     */\n    function MAX_NUMBER_RESERVES() external view returns (uint16);\n\n    /**\n     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens\n     * @param assets The list of reserves for which the minting needs to be executed\n     */\n    function mintToTreasury(address[] calldata assets) external;\n\n    /**\n     * @notice Rescue and transfer tokens locked in this contract\n     * @param token The address of the token\n     * @param to The address of the recipient\n     * @param amount The amount of token to transfer\n     */\n    function rescueTokens(address token, address to, uint256 amount) external;\n\n    /**\n     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC\n     * @dev Deprecated: Use the `supply` function instead\n     * @param asset The address of the underlying asset to supply\n     * @param amount The amount to be supplied\n     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n     *   is a different wallet\n     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n     *   0 if the action is executed directly by the user, without any middle-man\n     */\n    function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;\n}\n"
      },
      "src/interfaces/external/ICreditDelegationToken.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity ^0.8.0;\n\n/**\n * @title ICreditDelegationToken\n * @author Aave\n * @notice Defines the basic interface for a token supporting credit delegation.\n */\ninterface ICreditDelegationToken {\n    /**\n     * @dev Emitted on `approveDelegation` and `borrowAllowance\n     * @param fromUser The address of the delegator\n     * @param toUser The address of the delegatee\n     * @param asset The address of the delegated asset\n     * @param amount The amount being delegated\n     */\n    event BorrowAllowanceDelegated(\n        address indexed fromUser,\n        address indexed toUser,\n        address indexed asset,\n        uint256 amount\n    );\n\n    /**\n     * @notice Delegates borrowing power to a user on the specific debt token.\n     * Delegation will still respect the liquidation constraints (even if delegated, a\n     * delegatee cannot force a delegator HF to go below 1)\n     * @param delegatee The address receiving the delegated borrowing power\n     * @param amount The maximum amount being delegated.\n     */\n    function approveDelegation(address delegatee, uint256 amount) external;\n\n    /**\n     * @notice Returns the borrow allowance of the user\n     * @param fromUser The user to giving allowance\n     * @param toUser The user to give allowance to\n     * @return The current allowance of `toUser`\n     */\n    function borrowAllowance(address fromUser, address toUser) external view returns (uint256);\n\n    /**\n     * @notice Delegates borrowing power to a user on the specific debt token via ERC712 signature\n     * @param delegator The delegator of the credit\n     * @param delegatee The delegatee that can use the credit\n     * @param value The amount to be delegated\n     * @param deadline The deadline timestamp, type(uint256).max for max deadline\n     * @param v The V signature param\n     * @param s The S signature param\n     * @param r The R signature param\n     */\n    function delegationWithSig(\n        address delegator,\n        address delegatee,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../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 * By default, the owner account will be the one that deploys the contract. This\n * can 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    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\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        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing 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        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\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"
      },
      "src/modules/fees/FeesManager.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n                                      s███\n                                    ██████\n                                   @██████\n                              ,s███`\n                           ,██████████████\n                          █████████^@█████_\n                         ██████████_ 7@███_            \"██████████M\n                        @██████████_     `_              \"@█████b\n                        ^^^^^^^^^^\"                         ^\"`\n                         \n                        ████████████████████p   _█████████████████████\n                        @████████████████████   @███████████WT@██████b\n                         ████████████████████   @███████████  ,██████\n                         @███████████████████   @███████████████████b\n                          @██████████████████   @██████████████████b\n                           \"█████████████████   @█████████████████b\n                             @███████████████   @████████████████\n                               %█████████████   @██████████████`\n                                 ^%██████████   @███████████\"\n                                     ████████   @██████W\"`\n                                     1███████\n                                      \"@█████\n                                         7W@█\n*/\n\npragma solidity 0.8.21;\n\nimport { ManagementFeesLib } from \"src/modules/fees/ManagementFeesLib.sol\";\nimport { PerformanceFeesLib } from \"src/modules/fees/PerformanceFeesLib.sol\";\nimport { Fund } from \"src/base/Fund.sol\";\nimport { Registry } from \"src/Registry.sol\";\nimport { Math } from \"src/utils/Math.sol\";\nimport { SafeTransferLib, ERC20 } from \"@solmate/utils/SafeTransferLib.sol\";\n\n/**\n * @title Handles Fees collection and distribution for Funds\n */\ncontract FeesManager {\n    using Math for uint256;\n    using SafeTransferLib for ERC20;\n\n    // =============================================== EVENTS ===============================================\n    /**\n     * @notice Emitted when strategist platform fee cut is changed.\n     * @param fund the fund that had the strategist platform fee cut changed\n     * @param newPlatformCut value strategist platform fee cut was changed to\n     */\n    event StrategistPlatformCutChanged(address indexed fund, uint64 newPlatformCut);\n\n    /**\n     * @notice Emitted when strategists payout address is changed.\n     * @param fund the fund that had the strategist payout address changed\n     * @param newPayoutAddress value strategists payout address was changed to\n     */\n    event StrategistPayoutAddressChanged(address indexed fund, address indexed newPayoutAddress);\n\n    /**\n     * @notice Emitted when protocol payout address is changed.\n     * @param newPayoutAddress value protocol payout address was changed to\n     */\n    event ProtocolPayoutAddressChanged(address indexed newPayoutAddress);\n\n    /**\n     * @notice Emitted when a fund's fees are paid out.\n     * @param fund the fund that had fees paid out\n     * @param strategistPayoutAddress the address that the strategist's fees were paid to\n     * @param protocolPayoutAddress the address that the protocol's fees were paid to\n     * @param strategistPayout the amount of fees paid to the strategist\n     * @param protocolPayout the amount of fees paid to the protocol\n     */\n    event Payout(\n        address indexed fund,\n        address indexed strategistPayoutAddress,\n        uint256 strategistPayout,\n        address indexed protocolPayoutAddress,\n        uint256 protocolPayout\n    );\n\n    /**\n     * @notice Emitted when management fees are claimed.\n     * @param fund the fund that had management fees claimed\n     * @param fees the amount of management fees claimed\n     */\n    event ManagementFeesClaimed(address indexed fund, uint256 fees);\n\n    /**\n     * @notice Emitted when management fees rate is updated.\n     * @param fund the fund that had management fees rate updated\n     * @param managementFeesPerYear the new management fees yearly fees (1e18 = 100% per year)\n     * @param managementFeesRate the new management fees rate\n     */\n    event ManagementFeesRateUpdated(address indexed fund, uint256 managementFeesPerYear, uint256 managementFeesRate);\n\n    /**\n     * @notice Emitted when performance fees are claimed.\n     * @param fund the fund that had performance fees claimed\n     * @param fees the amount of performance fees claimed\n     */\n    event PerformanceFeesClaimed(address indexed fund, uint256 fees, uint256 highWaterMarkPrice);\n\n    /**\n     * @notice Emitted when performance fees rate is updated.\n     * @param fund the fund that had performance fees rate updated\n     * @param performanceFeesRate the new performance fees rate\n     * @param highWaterMarkPrice the high-water mark price at the time of the update\n     */\n    event PerformanceFeesRateUpdated(address indexed fund, uint256 performanceFeesRate, uint256 highWaterMarkPrice);\n\n    /**\n     * @notice Emitted when enter fees are updated.\n     * @param fund the fund that had enter fees updated\n     * @param enterFeesRate the new enter fees rate\n     */\n    event EnterFeesUpdated(address indexed fund, uint16 enterFeesRate);\n\n    /**\n     * @notice Emitted when exit fees are updated.\n     * @param fund the fund that had exit fees updated\n     * @param exitFeesRate the new exit fees rate\n     */\n    event ExitFeesUpdated(address indexed fund, uint16 exitFeesRate);\n\n    // =============================================== ERRORS ===============================================\n\n    /// @notice Throws when the caller is not the fund owner.\n    error FeesManager__OnlyFundOwner();\n\n    /// @notice Throws when the caller is not the registry owner.\n    error FeesManager__OnlyRegistryOwner();\n\n    /// @notice Throws when the fee cut is above the authorized limit.\n    error FeesManager__InvalidFeesCut();\n\n    /// @notice Throws when the fees are above authorized limit.\n    error FeesManager__InvalidFeesRate();\n\n    /// @notice Throws when the protocol payout address is invalid.\n    error FeesManager__InvalidProtocolPayoutAddress();\n\n    /// @notice Throws when the high-water mark has not yet expired.\n    error FeesManager__HighWaterMarkNotYetExpired();\n\n    /// @notice Throws when the high-water mark price overflows. (unlikely scenario)\n    error FeesManager__WaterMarkPriceOverflow();\n\n    // =============================================== CONSTANTS ===============================================\n\n    /// @notice Sets the max possible fee cut for funds.\n    uint256 public constant MAX_FEE_CUT = 1e18;\n\n    /// @notice Sets the max possible management fees for funds.\n    uint256 public constant MAX_MANAGEMENT_FEES = 50e16; // 50%\n\n    /// @notice Sets the max possible performance fees for funds.\n    uint256 public constant MAX_PERFORMANCE_FEES = 50e16; // 50%\n\n    // Enter and exit fees are expressed in basis points (1e4 = 100%)\n    uint256 internal constant _BPS_ONE_HUNDRED_PER_CENT = 1e4;\n\n    /// @notice Sets the max possible enter fees for funds.\n    uint256 public constant MAX_ENTER_FEES = _BPS_ONE_HUNDRED_PER_CENT / 10; // 10%\n\n    /// @notice Sets the max possible exit fees for funds.\n    uint256 public constant MAX_EXIT_FEES = _BPS_ONE_HUNDRED_PER_CENT / 10; // 10%\n\n    /// @notice Sets the high-water mark reset interval for funds.\n    uint256 public constant HIGH_WATERMARK_RESET_INTERVAL = 3 * 30 days; // 3 months\n\n    /// @notice Sets the high-water mark reset interval for funds.\n    uint256 public constant HIGH_WATERMARK_RESET_ASSET_THRESHOLD = Math.WAD + Math.WAD / 2; // 50%\n\n    // =============================================== MODIFIERS ===============================================\n\n    modifier onlyFundOwner(address fund) {\n        if (msg.sender != Fund(fund).owner()) {\n            revert FeesManager__OnlyFundOwner();\n        }\n        _;\n    }\n\n    modifier onlyRegistryOwner() {\n        if (msg.sender != registry.owner()) {\n            revert FeesManager__OnlyRegistryOwner();\n        }\n        _;\n    }\n\n    // =============================================== STATE VARIABLES ===============================================\n\n    /**\n     * @notice Address of the platform's protocol payout address. Used to send protocol fees.\n     */\n    address public protocolPayoutAddress;\n\n    /**\n     * @notice Address of the platform's registry contract. Used to get the latest address of modules.\n     */\n    Registry public immutable registry;\n\n    constructor(address _registry, address _protocolPayoutAddress) {\n        registry = Registry(_registry);\n        _setProtocolPayoutAddress(_protocolPayoutAddress);\n    }\n\n    struct FeesData {\n        uint16 enterFeesRate; // in bps (max value = 10000)\n        uint16 exitFeesRate; // in bps (max value = 10000)\n        uint40 previousManagementFeesClaimTime; // last management fees claim time\n        uint48 managementFeesRate; // in 18 decimals\n        uint64 performanceFeesRate; // in 18 decimals (100% corresponds to 1e18)\n        uint72 highWaterMarkPrice; // the high-water mark price\n        uint40 highWaterMarkResetTime; // the owner can choose to reset the high-water mark (at most every HIGH_WATERMARK_RESET_INTERVAL)\n        uint256 highWaterMarkResetAssets; // the owner can choose to reset the high-water mark (at most every HIGH_WATERMARK_RESET_ASSETS_TOLERANCE)\n        uint64 strategistPlatformCut; // the platform cut for the strategist in 18 decimals\n        address strategistPayoutAddress; // the address to send the strategist's fees to\n    }\n\n    mapping(address => FeesData) internal fundFeesData;\n\n    function getFundFeesData(address fund) external view returns (FeesData memory) {\n        return fundFeesData[fund];\n    }\n\n    /**\n     * @notice Called by funds to compute the fees to apply before depositing assets (or minting shares).\n     * @param totalAssets total assets in the fund\n     * @param totalSupply total shares in the fund\n     * @return enterOrExitFeesRate enter or exit fees rate\n     * @return mintFeesAsShares minted shares to be used as fees\n     */\n    function previewApplyFeesBeforeJoinExit(\n        uint256 totalAssets,\n        uint256 totalSupply,\n        bool isEntering\n    ) external view returns (uint16, uint256) {\n        (\n            uint16 enterOrExitFeesRate,\n            uint256 performanceFees,\n            uint256 managementFees,\n            ,\n\n        ) = _previewApplyFeesBeforeJoinExit(totalAssets, totalSupply, isEntering);\n        return (enterOrExitFeesRate, performanceFees + managementFees);\n    }\n\n    function _previewApplyFeesBeforeJoinExit(\n        uint256 totalAssets,\n        uint256 totalSupply,\n        bool isEntering\n    ) internal view returns (uint16, uint256, uint256, uint256, FeesData storage) {\n        FeesData storage feeData = fundFeesData[msg.sender];\n\n        (uint256 managementFees, uint256 performanceFees, uint256 highWaterMarkPrice) = _getUnclaimedFees(\n            feeData,\n            totalAssets,\n            totalSupply\n        );\n\n        uint16 enterOrExitFeesRate = isEntering ? feeData.enterFeesRate : feeData.exitFeesRate;\n\n        return (enterOrExitFeesRate, performanceFees, managementFees, highWaterMarkPrice, feeData);\n    }\n\n    /**\n     * @notice Called by funds to compute the fees to apply before depositing assets (or minting shares).\n     * @param totalAssets total assets in the fund\n     * @param totalSupply total shares in the fund\n     * @return enterOrExitFeesRate enter or exit fees rate\n     * @return mintFeesAsShares minted shares to be used as fees\n     */\n    function applyFeesBeforeJoinExit(\n        uint256 totalAssets,\n        uint256 totalSupply,\n        bool isEntering\n    ) external returns (uint16, uint256) {\n        (\n            uint16 enterOrExitFeesRate,\n            uint256 performanceFees,\n            uint256 managementFees,\n            uint256 highWaterMarkPrice,\n            FeesData storage feeData\n        ) = _previewApplyFeesBeforeJoinExit(totalAssets, totalSupply, isEntering);\n\n        if (managementFees > 0) {\n            feeData.previousManagementFeesClaimTime = uint40(block.timestamp);\n            emit ManagementFeesClaimed(msg.sender, managementFees);\n        }\n\n        if (performanceFees > 0) {\n            if (highWaterMarkPrice > type(uint72).max) revert FeesManager__WaterMarkPriceOverflow();\n\n            feeData.highWaterMarkPrice = uint72(highWaterMarkPrice);\n            emit PerformanceFeesClaimed(msg.sender, performanceFees, highWaterMarkPrice);\n        }\n\n        return (enterOrExitFeesRate, performanceFees + managementFees);\n    }\n\n    /**\n     * @notice Get total supply after applying unclaimed fees.\n     */\n    function getTotalSupplyAfterFees(\n        address fund,\n        uint256 totalAssets,\n        uint256 totalSupply\n    ) external view returns (uint256) {\n        FeesData storage feeData = fundFeesData[fund];\n\n        (uint256 managementFees, uint256 performanceFees, ) = _getUnclaimedFees(feeData, totalAssets, totalSupply);\n\n        return (totalSupply + managementFees + performanceFees);\n    }\n\n    function _getUnclaimedFees(\n        FeesData storage feeData,\n        uint256 totalAssets,\n        uint256 totalSupply\n    ) internal view returns (uint256, uint256, uint256) {\n        // management fees\n        uint256 managementFees = ManagementFeesLib._calcAccumulatedManagementFees(\n            block.timestamp,\n            feeData.previousManagementFeesClaimTime,\n            feeData.managementFeesRate,\n            totalSupply\n        );\n\n        // performance fees\n        (uint256 performanceFees, uint256 highWaterMarkPrice) = PerformanceFeesLib._calcPerformanceFees(\n            totalAssets,\n            totalSupply + managementFees,\n            feeData.highWaterMarkPrice,\n            feeData.performanceFeesRate // performanceFees\n        );\n\n        return (managementFees, performanceFees, highWaterMarkPrice);\n    }\n\n    // =============================================== PAYOUT FUNCTIONS ===============================================\n\n    /**\n     * @notice Payout the fees to the protocol and the strategist (permissionless, anyone can call it)\n     * @param fund the fund to payout the fees for\n     */\n    function payoutFees(address fund) public {\n        Fund(fund).collectFees();\n        uint256 totalFees = ERC20(fund).balanceOf(address(this));\n\n        if (totalFees == 0) {\n            return;\n        }\n\n        FeesData storage feeData = fundFeesData[fund];\n\n        // if the strategist payout address is not set, the strategist doesn't get any fees\n        address strategistPayoutAddress = feeData.strategistPayoutAddress;\n        uint256 strategistPayout = strategistPayoutAddress == address(0)\n            ? 0\n            : (totalFees.mulDivUp(feeData.strategistPlatformCut, Math.WAD));\n\n        strategistPayout = strategistPayout > totalFees ? totalFees : strategistPayout;\n\n        // Send the strategist's cut\n        if (strategistPayout > 0) {\n            ERC20(fund).safeTransfer(strategistPayoutAddress, strategistPayout);\n        }\n\n        // Send the protocol's cut\n        uint256 protocolPayout = totalFees - strategistPayout;\n        if (protocolPayout > 0) {\n            ERC20(fund).safeTransfer(protocolPayoutAddress, protocolPayout);\n        }\n\n        emit Payout(fund, strategistPayoutAddress, strategistPayout, protocolPayoutAddress, protocolPayout);\n    }\n\n    /**\n     * @notice Sets the protocol payout address\n     * @param newPayoutAddress the new protocol payout address\n     */\n    function setProtocolPayoutAddress(address newPayoutAddress) external onlyRegistryOwner {\n        _setProtocolPayoutAddress(newPayoutAddress);\n    }\n\n    function _setProtocolPayoutAddress(address newPayoutAddress) internal {\n        if (newPayoutAddress == address(0)) revert FeesManager__InvalidProtocolPayoutAddress();\n        emit ProtocolPayoutAddressChanged(newPayoutAddress);\n        protocolPayoutAddress = newPayoutAddress;\n    }\n\n    /**\n     * @notice Sets the Strategists payout address\n     * @param newPayoutAddress the new strategist payout address\n     * @dev Callable by the Fund's owner.\n     */\n    function setStrategistPayoutAddress(address fund, address newPayoutAddress) external onlyFundOwner(fund) {\n        FeesData storage feeData = fundFeesData[fund];\n\n        // collect fees and payout the old strategist (if any) before changing the address\n        payoutFees(fund);\n\n        emit StrategistPayoutAddressChanged(fund, newPayoutAddress);\n        // no need to check if the address is not valid, the owner can set it to any address\n        feeData.strategistPayoutAddress = newPayoutAddress;\n    }\n\n    // =============================================== FEES CONFIG ===============================================\n\n    /**\n     * @notice Sets the Strategists cut of platform fees\n     * @param cut the platform cut for the strategist\n     * @dev Callable by Registry owner.\n     */\n    function setStrategistPlatformCut(address fund, uint64 cut) external onlyRegistryOwner {\n        if (cut > MAX_FEE_CUT) revert FeesManager__InvalidFeesCut();\n\n        payoutFees(fund);\n\n        FeesData storage feeData = fundFeesData[fund];\n\n        emit StrategistPlatformCutChanged(fund, cut);\n        feeData.strategistPlatformCut = cut;\n    }\n\n    /**\n     * @notice Sets the management fees per year for this fund.\n     * @param fund the fund to set the management fees for\n     * @param managementFeesPerYear the management fees per year (1e18 = 100% per year)\n     */\n    function setManagementFeesPerYear(address fund, uint256 managementFeesPerYear) external onlyFundOwner(fund) {\n        if (managementFeesPerYear > MAX_MANAGEMENT_FEES) revert FeesManager__InvalidFeesRate();\n\n        Fund(fund).collectFees(); // collectFees is nonReetrant, which makes setManagementFeesPerYear nonReetrant\n\n        FeesData storage feeData = fundFeesData[fund];\n        uint256 managementFeesRate = ManagementFeesLib._calcYearlyRate(managementFeesPerYear);\n        feeData.managementFeesRate = uint48(managementFeesRate);\n\n        // the management fees time is not guaranteed to be updated when collecting fees if the fees are 0\n        // so we update it here to make sure it's always up to date when changing the rate\n        feeData.previousManagementFeesClaimTime = uint40(block.timestamp);\n\n        emit ManagementFeesRateUpdated(fund, managementFeesPerYear, managementFeesRate);\n    }\n\n    /**\n     * @notice Sets the performance fees for this fund.\n     * @param fund the fund to set the performance fees for\n     * @param performanceFeesRate the performance fees (1e18 = 100%)\n     */\n    function setPerformanceFees(address fund, uint256 performanceFeesRate) external onlyFundOwner(fund) {\n        if (performanceFeesRate > MAX_PERFORMANCE_FEES) revert FeesManager__InvalidFeesRate();\n\n        FeesData storage feeData = fundFeesData[fund];\n\n        // if the high-water mark is not set, set it and do not collect fees potential pending management fees\n        // as the function is most likely called during the setup of the fund.\n        if (feeData.highWaterMarkPrice == 0) {\n            feeData.performanceFeesRate = uint64(performanceFeesRate);\n            // initialize the high-water mark\n            // note that the fund will revert if we are calling totalAssets() when it's locked (nonReentrantView)\n            uint256 totalAssets = Fund(fund).totalAssets();\n            uint256 highWaterMarkPrice = PerformanceFeesLib._calcSharePrice(totalAssets, Fund(fund).totalSupply());\n            if (highWaterMarkPrice > type(uint72).max) revert FeesManager__WaterMarkPriceOverflow();\n\n            fundFeesData[fund].highWaterMarkPrice = uint72(highWaterMarkPrice);\n            fundFeesData[fund].highWaterMarkResetTime = uint40(block.timestamp);\n            fundFeesData[fund].highWaterMarkResetAssets = uint256(totalAssets);\n\n            emit PerformanceFeesRateUpdated(fund, performanceFeesRate, highWaterMarkPrice);\n            return;\n        }\n\n        // collect fees before updating the rate\n        Fund(fund).collectFees(); // collectFees is nonReetrant, which makes setPerformanceFees nonReetrant\n\n        emit PerformanceFeesRateUpdated(fund, performanceFeesRate, feeData.highWaterMarkPrice);\n        feeData.performanceFeesRate = uint64(performanceFeesRate);\n    }\n\n    /**\n     * @notice Sets the enter fees for this fund.\n     * @param fund the fund to set the performance fees for\n     * @param enterFeesRate the enter fees (10000 = 100%)\n     */\n    function setEnterFees(address fund, uint16 enterFeesRate) external onlyFundOwner(fund) {\n        if (enterFeesRate > MAX_ENTER_FEES) {\n            revert FeesManager__InvalidFeesRate();\n        }\n\n        emit EnterFeesUpdated(fund, enterFeesRate);\n        fundFeesData[fund].enterFeesRate = enterFeesRate;\n    }\n\n    /**\n     * @notice Sets the exit fees for this fund.\n     * @param fund the fund to set the performance fees for\n     * @param exitFeesRate the exit fees (10000 = 100%)\n     */\n    function setExitFees(address fund, uint16 exitFeesRate) external onlyFundOwner(fund) {\n        if (exitFeesRate > MAX_EXIT_FEES) {\n            revert FeesManager__InvalidFeesRate();\n        }\n\n        emit ExitFeesUpdated(fund, exitFeesRate);\n        fundFeesData[fund].exitFeesRate = exitFeesRate;\n    }\n\n    /**\n     * @notice Resets the high-water mark for this fund.\n     * @param fund the fund to reset the high-water mark state for\n     */\n    function resetHighWaterMark(address fund) external onlyRegistryOwner {\n        Fund c = Fund(fund);\n        c.collectFees();\n\n        FeesData storage feeData = fundFeesData[fund];\n\n        uint256 totalAssets = c.totalAssets();\n\n        // checks high-water mark reset conditions\n        if (\n            (feeData.highWaterMarkPrice > 0) && // unset condition\n            (block.timestamp < feeData.highWaterMarkResetTime + HIGH_WATERMARK_RESET_INTERVAL) && // time condition\n            (totalAssets < (feeData.highWaterMarkResetAssets * HIGH_WATERMARK_RESET_ASSET_THRESHOLD) / Math.WAD) // assets condition\n        ) {\n            revert FeesManager__HighWaterMarkNotYetExpired();\n        }\n\n        // calculates the new high-water mark\n        uint256 highWaterMarkPrice = PerformanceFeesLib._calcSharePrice(totalAssets, c.totalSupply());\n        if (highWaterMarkPrice > type(uint72).max) revert FeesManager__WaterMarkPriceOverflow();\n\n        // updates the high-water mark state\n        feeData.highWaterMarkPrice = uint72(highWaterMarkPrice);\n        feeData.highWaterMarkResetTime = uint40(block.timestamp);\n        feeData.highWaterMarkResetAssets = uint256(totalAssets);\n    }\n}\n"
      },
      "src/base/ERC4626.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\n\n/**\n * @title ERC4626\n * @notice An interface for a contract that represents a pool of assets and shares.\n */\nabstract contract ERC4626 is ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed caller,\n        address indexed receiver,\n        address indexed owner,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /*//////////////////////////////////////////////////////////////\n                               IMMUTABLES\n    //////////////////////////////////////////////////////////////*/\n\n    ERC20 public immutable asset;\n\n    constructor(ERC20 _asset) {\n        asset = _asset;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        DEPOSIT/WITHDRAWAL LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares);\n\n    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets);\n\n    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256 shares);\n\n    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256 assets);\n\n    /*//////////////////////////////////////////////////////////////\n                            ACCOUNTING LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function totalAssets() public view virtual returns (uint256);\n\n    function convertToShares(uint256 assets) public view virtual returns (uint256);\n\n    function convertToAssets(uint256 shares) public view virtual returns (uint256);\n\n    function previewDeposit(uint256 assets) public view virtual returns (uint256);\n\n    function previewMint(uint256 shares) public view virtual returns (uint256);\n\n    function previewWithdraw(uint256 assets) public view virtual returns (uint256);\n\n    function previewRedeem(uint256 shares) public view virtual returns (uint256);\n\n    /*//////////////////////////////////////////////////////////////\n                     DEPOSIT/WITHDRAWAL LIMIT LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function maxDeposit(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    function maxMint(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    function maxWithdraw(address owner) public view virtual returns (uint256) {\n        return convertToAssets(balanceOf[owner]);\n    }\n\n    function maxRedeem(address owner) public view virtual returns (uint256) {\n        return balanceOf[owner];\n    }\n}\n"
      },
      "src/utils/Uint32Array.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\n/**\n * @notice A library to extend the uint32 array data type.\n */\nlibrary Uint32Array {\n    // =========================================== ADDRESS STORAGE ===========================================\n\n    /**\n     * @notice Add an uint32 to the array at a given index.\n     * @param array uint32 array to add the uint32 to\n     * @param index index to add the uint32 at\n     * @param value uint32 to add to the array\n     */\n    function add(uint32[] storage array, uint32 index, uint32 value) internal {\n        uint256 len = array.length;\n\n        if (len > 0) {\n            array.push(array[len - 1]);\n\n            for (uint256 i = len - 1; i > index; i--) array[i] = array[i - 1];\n\n            array[index] = value;\n        } else {\n            array.push(value);\n        }\n    }\n\n    /**\n     * @notice Remove a uint32 from the array at a given index.\n     * @param array uint32 array to remove the uint32 from\n     * @param index index to remove the uint32 at\n     */\n    function remove(uint32[] storage array, uint32 index) internal {\n        uint256 len = array.length;\n\n        require(index < len, \"Index out of bounds\");\n\n        for (uint256 i = index; i < len - 1; i++) array[i] = array[i + 1];\n\n        array.pop();\n    }\n\n    /**\n     * @notice Check whether an array contains an uint32.\n     * @param array uint32 array to check\n     * @param value uint32 to check for\n     */\n    function contains(uint32[] storage array, uint32 value) internal view returns (bool) {\n        for (uint256 i; i < array.length; i++) if (value == array[i]) return true;\n\n        return false;\n    }\n}\n"
      },
      "lib/solmate/src/auth/Owned.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event OwnershipTransferred(address indexed user, address indexed newOwner);\n\n    /*//////////////////////////////////////////////////////////////\n                            OWNERSHIP STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    address public owner;\n\n    modifier onlyOwner() virtual {\n        require(msg.sender == owner, \"UNAUTHORIZED\");\n\n        _;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(address _owner) {\n        owner = _owner;\n\n        emit OwnershipTransferred(address(0), _owner);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             OWNERSHIP LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        owner = newOwner;\n\n        emit OwnershipTransferred(msg.sender, newOwner);\n    }\n}\n"
      },
      "src/interfaces/external/IChainlinkAggregator.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol\";\n\ninterface IChainlinkAggregator is AggregatorV2V3Interface {\n    function maxAnswer() external view returns (int192);\n\n    function minAnswer() external view returns (int192);\n}\n"
      },
      "src/interfaces/external/IChainlinkAggregatorProxy.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol\";\n\ninterface IChainlinkAggregatorProxy is AggregatorV2V3Interface {\n    function aggregator() external view returns (address);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\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 *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\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     * _Available since v4.7._\n     */\n    function toUint248(uint256 value) internal pure returns (uint248) {\n        require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\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     * _Available since v4.7._\n     */\n    function toUint240(uint256 value) internal pure returns (uint240) {\n        require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\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     * _Available since v4.7._\n     */\n    function toUint232(uint256 value) internal pure returns (uint232) {\n        require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\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     * _Available since v4.2._\n     */\n    function toUint224(uint256 value) internal pure returns (uint224) {\n        require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\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     * _Available since v4.7._\n     */\n    function toUint216(uint256 value) internal pure returns (uint216) {\n        require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\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     * _Available since v4.7._\n     */\n    function toUint208(uint256 value) internal pure returns (uint208) {\n        require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\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     * _Available since v4.7._\n     */\n    function toUint200(uint256 value) internal pure returns (uint200) {\n        require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\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     * _Available since v4.7._\n     */\n    function toUint192(uint256 value) internal pure returns (uint192) {\n        require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\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     * _Available since v4.7._\n     */\n    function toUint184(uint256 value) internal pure returns (uint184) {\n        require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\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     * _Available since v4.7._\n     */\n    function toUint176(uint256 value) internal pure returns (uint176) {\n        require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\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     * _Available since v4.7._\n     */\n    function toUint168(uint256 value) internal pure returns (uint168) {\n        require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\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     * _Available since v4.7._\n     */\n    function toUint160(uint256 value) internal pure returns (uint160) {\n        require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\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     * _Available since v4.7._\n     */\n    function toUint152(uint256 value) internal pure returns (uint152) {\n        require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\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     * _Available since v4.7._\n     */\n    function toUint144(uint256 value) internal pure returns (uint144) {\n        require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\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     * _Available since v4.7._\n     */\n    function toUint136(uint256 value) internal pure returns (uint136) {\n        require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\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     * _Available since v2.5._\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\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     * _Available since v4.7._\n     */\n    function toUint120(uint256 value) internal pure returns (uint120) {\n        require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\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     * _Available since v4.7._\n     */\n    function toUint112(uint256 value) internal pure returns (uint112) {\n        require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\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     * _Available since v4.7._\n     */\n    function toUint104(uint256 value) internal pure returns (uint104) {\n        require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\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     * _Available since v4.2._\n     */\n    function toUint96(uint256 value) internal pure returns (uint96) {\n        require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\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     * _Available since v4.7._\n     */\n    function toUint88(uint256 value) internal pure returns (uint88) {\n        require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\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     * _Available since v4.7._\n     */\n    function toUint80(uint256 value) internal pure returns (uint80) {\n        require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\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     * _Available since v4.7._\n     */\n    function toUint72(uint256 value) internal pure returns (uint72) {\n        require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\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     * _Available since v2.5._\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\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     * _Available since v4.7._\n     */\n    function toUint56(uint256 value) internal pure returns (uint56) {\n        require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\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     * _Available since v4.7._\n     */\n    function toUint48(uint256 value) internal pure returns (uint48) {\n        require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\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     * _Available since v4.7._\n     */\n    function toUint40(uint256 value) internal pure returns (uint40) {\n        require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\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     * _Available since v2.5._\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\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     * _Available since v4.7._\n     */\n    function toUint24(uint256 value) internal pure returns (uint24) {\n        require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\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     * _Available since v2.5._\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\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     * _Available since v2.5._\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\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     * _Available since v3.0._\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        require(value >= 0, \"SafeCast: value must be positive\");\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     * _Available since v4.7._\n     */\n    function toInt248(int256 value) internal pure returns (int248 downcasted) {\n        downcasted = int248(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\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     * _Available since v4.7._\n     */\n    function toInt240(int256 value) internal pure returns (int240 downcasted) {\n        downcasted = int240(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\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     * _Available since v4.7._\n     */\n    function toInt232(int256 value) internal pure returns (int232 downcasted) {\n        downcasted = int232(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\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     * _Available since v4.7._\n     */\n    function toInt224(int256 value) internal pure returns (int224 downcasted) {\n        downcasted = int224(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\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     * _Available since v4.7._\n     */\n    function toInt216(int256 value) internal pure returns (int216 downcasted) {\n        downcasted = int216(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\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     * _Available since v4.7._\n     */\n    function toInt208(int256 value) internal pure returns (int208 downcasted) {\n        downcasted = int208(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\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     * _Available since v4.7._\n     */\n    function toInt200(int256 value) internal pure returns (int200 downcasted) {\n        downcasted = int200(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\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     * _Available since v4.7._\n     */\n    function toInt192(int256 value) internal pure returns (int192 downcasted) {\n        downcasted = int192(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\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     * _Available since v4.7._\n     */\n    function toInt184(int256 value) internal pure returns (int184 downcasted) {\n        downcasted = int184(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\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     * _Available since v4.7._\n     */\n    function toInt176(int256 value) internal pure returns (int176 downcasted) {\n        downcasted = int176(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\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     * _Available since v4.7._\n     */\n    function toInt168(int256 value) internal pure returns (int168 downcasted) {\n        downcasted = int168(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\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     * _Available since v4.7._\n     */\n    function toInt160(int256 value) internal pure returns (int160 downcasted) {\n        downcasted = int160(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\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     * _Available since v4.7._\n     */\n    function toInt152(int256 value) internal pure returns (int152 downcasted) {\n        downcasted = int152(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\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     * _Available since v4.7._\n     */\n    function toInt144(int256 value) internal pure returns (int144 downcasted) {\n        downcasted = int144(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\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     * _Available since v4.7._\n     */\n    function toInt136(int256 value) internal pure returns (int136 downcasted) {\n        downcasted = int136(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\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     * _Available since v3.1._\n     */\n    function toInt128(int256 value) internal pure returns (int128 downcasted) {\n        downcasted = int128(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\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     * _Available since v4.7._\n     */\n    function toInt120(int256 value) internal pure returns (int120 downcasted) {\n        downcasted = int120(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\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     * _Available since v4.7._\n     */\n    function toInt112(int256 value) internal pure returns (int112 downcasted) {\n        downcasted = int112(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\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     * _Available since v4.7._\n     */\n    function toInt104(int256 value) internal pure returns (int104 downcasted) {\n        downcasted = int104(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\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     * _Available since v4.7._\n     */\n    function toInt96(int256 value) internal pure returns (int96 downcasted) {\n        downcasted = int96(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\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     * _Available since v4.7._\n     */\n    function toInt88(int256 value) internal pure returns (int88 downcasted) {\n        downcasted = int88(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\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     * _Available since v4.7._\n     */\n    function toInt80(int256 value) internal pure returns (int80 downcasted) {\n        downcasted = int80(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\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     * _Available since v4.7._\n     */\n    function toInt72(int256 value) internal pure returns (int72 downcasted) {\n        downcasted = int72(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\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     * _Available since v3.1._\n     */\n    function toInt64(int256 value) internal pure returns (int64 downcasted) {\n        downcasted = int64(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\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     * _Available since v4.7._\n     */\n    function toInt56(int256 value) internal pure returns (int56 downcasted) {\n        downcasted = int56(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\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     * _Available since v4.7._\n     */\n    function toInt48(int256 value) internal pure returns (int48 downcasted) {\n        downcasted = int48(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\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     * _Available since v4.7._\n     */\n    function toInt40(int256 value) internal pure returns (int40 downcasted) {\n        downcasted = int40(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\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     * _Available since v3.1._\n     */\n    function toInt32(int256 value) internal pure returns (int32 downcasted) {\n        downcasted = int32(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\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     * _Available since v4.7._\n     */\n    function toInt24(int256 value) internal pure returns (int24 downcasted) {\n        downcasted = int24(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\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     * _Available since v3.1._\n     */\n    function toInt16(int256 value) internal pure returns (int16 downcasted) {\n        downcasted = int16(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\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     * _Available since v3.1._\n     */\n    function toInt8(int256 value) internal pure returns (int8 downcasted) {\n        downcasted = int8(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\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     * _Available since v3.0._\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        require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n        return int256(value);\n    }\n}\n"
      },
      "src/modules/price-router/Extensions/Extension.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\nimport { PriceRouter } from \"src/modules/price-router/PriceRouter.sol\";\nimport { Math } from \"src/utils/Math.sol\";\n\n/**\n * @title Swaap Price Router Extension abstract contract.\n * @notice Provides shared logic between Extensions.\n * @author crispymangoes\n */\nabstract contract Extension {\n    /**\n     * @notice Attempted to call a function only callable by the price router.\n     */\n    error Extension__OnlyPriceRouter();\n\n    /**\n     * @notice Prevents non price router contracts from calling a function.\n     */\n    modifier onlyPriceRouter() {\n        if (msg.sender != address(priceRouter)) revert Extension__OnlyPriceRouter();\n        _;\n    }\n\n    /**\n     * @notice The Swaap PriceRouter contract.\n     */\n    PriceRouter public immutable priceRouter;\n\n    constructor(PriceRouter _priceRouter) {\n        priceRouter = _priceRouter;\n    }\n\n    /**\n     * @notice Setup function is called when an asset is added/edited.\n     */\n    function setupSource(ERC20 asset, bytes memory sourceData) external virtual;\n\n    /**\n     * @notice Returns the price of an asset in USD.\n     */\n    function getPriceInUSD(ERC20 asset) external view virtual returns (uint256);\n}\n"
      },
      "src/interfaces/external/UniswapV3Pool.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity ^0.8.10;\n\ninterface UniswapV3Pool {\n    event Burn(\n        address indexed owner,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount,\n        uint256 amount0,\n        uint256 amount1\n    );\n    event Collect(\n        address indexed owner,\n        address recipient,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount0,\n        uint128 amount1\n    );\n    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);\n    event Flash(\n        address indexed sender,\n        address indexed recipient,\n        uint256 amount0,\n        uint256 amount1,\n        uint256 paid0,\n        uint256 paid1\n    );\n    event IncreaseObservationCardinalityNext(\n        uint16 observationCardinalityNextOld,\n        uint16 observationCardinalityNextNew\n    );\n    event Initialize(uint160 sqrtPriceX96, int24 tick);\n    event Mint(\n        address sender,\n        address indexed owner,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount,\n        uint256 amount0,\n        uint256 amount1\n    );\n    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);\n    event Swap(\n        address indexed sender,\n        address indexed recipient,\n        int256 amount0,\n        int256 amount1,\n        uint160 sqrtPriceX96,\n        uint128 liquidity,\n        int24 tick\n    );\n\n    function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint256 amount0, uint256 amount1);\n\n    function collect(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external returns (uint128 amount0, uint128 amount1);\n\n    function collectProtocol(\n        address recipient,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external returns (uint128 amount0, uint128 amount1);\n\n    function factory() external view returns (address);\n\n    function fee() external view returns (uint24);\n\n    function feeGrowthGlobal0X128() external view returns (uint256);\n\n    function feeGrowthGlobal1X128() external view returns (uint256);\n\n    function flash(address recipient, uint256 amount0, uint256 amount1, bytes memory data) external;\n\n    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;\n\n    function initialize(uint160 sqrtPriceX96) external;\n\n    function liquidity() external view returns (uint128);\n\n    function maxLiquidityPerTick() external view returns (uint128);\n\n    function mint(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount,\n        bytes memory data\n    ) external returns (uint256 amount0, uint256 amount1);\n\n    function observations(\n        uint256\n    )\n        external\n        view\n        returns (\n            uint32 blockTimestamp,\n            int56 tickCumulative,\n            uint160 secondsPerLiquidityCumulativeX128,\n            bool initialized\n        );\n\n    function observe(\n        uint32[] memory secondsAgos\n    ) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n\n    function positions(\n        bytes32\n    )\n        external\n        view\n        returns (\n            uint128 liquidity,\n            uint256 feeGrowthInside0LastX128,\n            uint256 feeGrowthInside1LastX128,\n            uint128 tokensOwed0,\n            uint128 tokensOwed1\n        );\n\n    function protocolFees() external view returns (uint128 token0, uint128 token1);\n\n    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;\n\n    function slot0()\n        external\n        view\n        returns (\n            uint160 sqrtPriceX96,\n            int24 tick,\n            uint16 observationIndex,\n            uint16 observationCardinality,\n            uint16 observationCardinalityNext,\n            uint8 feeProtocol,\n            bool unlocked\n        );\n\n    function snapshotCumulativesInside(\n        int24 tickLower,\n        int24 tickUpper\n    ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);\n\n    function swap(\n        address recipient,\n        bool zeroForOne,\n        int256 amountSpecified,\n        uint160 sqrtPriceLimitX96,\n        bytes memory data\n    ) external returns (int256 amount0, int256 amount1);\n\n    function tickBitmap(int16) external view returns (uint256);\n\n    function tickSpacing() external view returns (int24);\n\n    function ticks(\n        int24\n    )\n        external\n        view\n        returns (\n            uint128 liquidityGross,\n            int128 liquidityNet,\n            uint256 feeGrowthOutside0X128,\n            uint256 feeGrowthOutside1X128,\n            int56 tickCumulativeOutside,\n            uint160 secondsPerLiquidityOutsideX128,\n            uint32 secondsOutside,\n            bool initialized\n        );\n\n    function token0() external view returns (address);\n\n    function token1() external view returns (address);\n}\n"
      },
      "lib/v3-periphery/contracts/libraries/OracleLibrary.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0 <0.9.0;\n\nimport '@uniswap/v3-core/contracts/libraries/FullMath.sol';\nimport '@uniswap/v3-core/contracts/libraries/TickMath.sol';\nimport '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';\n\n/// @title Oracle library\n/// @notice Provides functions to integrate with V3 pool oracle\nlibrary OracleLibrary {\n    /// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool\n    /// @param pool Address of the pool that we want to observe\n    /// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means\n    /// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp\n    /// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp\n    function consult(address pool, uint32 secondsAgo)\n        internal\n        view\n        returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity)\n    {\n        require(secondsAgo != 0, 'BP');\n\n        uint32[] memory secondsAgos = new uint32[](2);\n        secondsAgos[0] = secondsAgo;\n        secondsAgos[1] = 0;\n\n        (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = IUniswapV3Pool(pool)\n            .observe(secondsAgos);\n\n        int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];\n        uint160 secondsPerLiquidityCumulativesDelta = secondsPerLiquidityCumulativeX128s[1] -\n            secondsPerLiquidityCumulativeX128s[0];\n\n        arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(secondsAgo)));\n        // Always round to negative infinity\n        if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(uint56(secondsAgo)) != 0)) arithmeticMeanTick--;\n\n        // We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128\n        uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max;\n        harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32));\n    }\n\n    /// @notice Given a tick and a token amount, calculates the amount of token received in exchange\n    /// @param tick Tick value used to calculate the quote\n    /// @param baseAmount Amount of token to be converted\n    /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination\n    /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination\n    /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken\n    function getQuoteAtTick(\n        int24 tick,\n        uint128 baseAmount,\n        address baseToken,\n        address quoteToken\n    ) internal pure returns (uint256 quoteAmount) {\n        uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);\n\n        // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself\n        if (sqrtRatioX96 <= type(uint128).max) {\n            uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;\n            quoteAmount = baseToken < quoteToken\n                ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192)\n                : FullMath.mulDiv(1 << 192, baseAmount, ratioX192);\n        } else {\n            uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);\n            quoteAmount = baseToken < quoteToken\n                ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128)\n                : FullMath.mulDiv(1 << 128, baseAmount, ratioX128);\n        }\n    }\n\n    /// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation\n    /// @param pool Address of Uniswap V3 pool that we want to observe\n    /// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool\n    function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) {\n        (, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0();\n        require(observationCardinality > 0, 'NI');\n\n        (uint32 observationTimestamp, , , bool initialized) = IUniswapV3Pool(pool).observations(\n            (observationIndex + 1) % observationCardinality\n        );\n\n        // The next index might not be initialized if the cardinality is in the process of increasing\n        // In this case the oldest observation is always in index 0\n        if (!initialized) {\n            (observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0);\n        }\n\n        unchecked {\n            secondsAgo = uint32(block.timestamp) - observationTimestamp;\n        }\n    }\n\n    /// @notice Given a pool, it returns the tick value as of the start of the current block\n    /// @param pool Address of Uniswap V3 pool\n    /// @return The tick that the pool was in at the start of the current block\n    function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) {\n        (, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0();\n\n        // 2 observations are needed to reliably calculate the block starting tick\n        require(observationCardinality > 1, 'NEO');\n\n        // If the latest observation occurred in the past, then no tick-changing trades have happened in this block\n        // therefore the tick in `slot0` is the same as at the beginning of the current block.\n        // We don't need to check if this observation is initialized - it is guaranteed to be.\n        (\n            uint32 observationTimestamp,\n            int56 tickCumulative,\n            uint160 secondsPerLiquidityCumulativeX128,\n\n        ) = IUniswapV3Pool(pool).observations(observationIndex);\n        if (observationTimestamp != uint32(block.timestamp)) {\n            return (tick, IUniswapV3Pool(pool).liquidity());\n        }\n\n        uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality;\n        (\n            uint32 prevObservationTimestamp,\n            int56 prevTickCumulative,\n            uint160 prevSecondsPerLiquidityCumulativeX128,\n            bool prevInitialized\n        ) = IUniswapV3Pool(pool).observations(prevIndex);\n\n        require(prevInitialized, 'ONI');\n\n        uint32 delta = observationTimestamp - prevObservationTimestamp;\n        tick = int24((tickCumulative - int56(uint56(prevTickCumulative))) / int56(uint56(delta)));\n        uint128 liquidity = uint128(\n            (uint192(delta) * type(uint160).max) /\n                (uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32)\n        );\n        return (tick, liquidity);\n    }\n\n    /// @notice Information for calculating a weighted arithmetic mean tick\n    struct WeightedTickData {\n        int24 tick;\n        uint128 weight;\n    }\n\n    /// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick\n    /// @param weightedTickData An array of ticks and weights\n    /// @return weightedArithmeticMeanTick The weighted arithmetic mean tick\n    /// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool tokens. If they do not,\n    /// extreme care must be taken to ensure that ticks are comparable (including decimal differences).\n    /// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price.\n    function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData)\n        internal\n        pure\n        returns (int24 weightedArithmeticMeanTick)\n    {\n        // Accumulates the sum of products between each tick and its weight\n        int256 numerator;\n\n        // Accumulates the sum of the weights\n        uint256 denominator;\n\n        // Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic\n        for (uint256 i; i < weightedTickData.length; i++) {\n            numerator += weightedTickData[i].tick * int256(uint256(weightedTickData[i].weight));\n            denominator += weightedTickData[i].weight;\n        }\n\n        weightedArithmeticMeanTick = int24(numerator / int256(denominator));\n        // Always round to negative infinity\n        if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--;\n    }\n\n    /// @notice Returns the \"synthetic\" tick which represents the price of the first entry in `tokens` in terms of the last\n    /// @dev Useful for calculating relative prices along routes.\n    /// @dev There must be one tick for each pairwise set of tokens.\n    /// @param tokens The token contract addresses\n    /// @param ticks The ticks, representing the price of each token pair in `tokens`\n    /// @return syntheticTick The synthetic tick, representing the relative price of the outermost tokens in `tokens`\n    function getChainedPrice(address[] memory tokens, int24[] memory ticks)\n        internal\n        pure\n        returns (int256 syntheticTick)\n    {\n        require(tokens.length - 1 == ticks.length, 'DL');\n        for (uint256 i = 1; i <= ticks.length; i++) {\n            // check the tokens for address sort order, then accumulate the\n            // ticks into the running synthetic tick, ensuring that intermediate tokens \"cancel out\"\n            tokens[i - 1] < tokens[i] ? syntheticTick += ticks[i - 1] : syntheticTick -= ticks[i - 1];\n        }\n    }\n}\n"
      },
      "src/interfaces/external/IPoolAddressesProvider.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0\npragma solidity ^0.8.0;\n\n/**\n * @title IPoolAddressesProvider\n * @author Aave\n * @notice Defines the basic interface for a Pool Addresses Provider.\n */\ninterface IPoolAddressesProvider {\n    /**\n     * @dev Emitted when the market identifier is updated.\n     * @param oldMarketId The old id of the market\n     * @param newMarketId The new id of the market\n     */\n    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);\n\n    /**\n     * @dev Emitted when the pool is updated.\n     * @param oldAddress The old address of the Pool\n     * @param newAddress The new address of the Pool\n     */\n    event PoolUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the pool configurator is updated.\n     * @param oldAddress The old address of the PoolConfigurator\n     * @param newAddress The new address of the PoolConfigurator\n     */\n    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the price oracle is updated.\n     * @param oldAddress The old address of the PriceOracle\n     * @param newAddress The new address of the PriceOracle\n     */\n    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the ACL manager is updated.\n     * @param oldAddress The old address of the ACLManager\n     * @param newAddress The new address of the ACLManager\n     */\n    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the ACL admin is updated.\n     * @param oldAddress The old address of the ACLAdmin\n     * @param newAddress The new address of the ACLAdmin\n     */\n    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the price oracle sentinel is updated.\n     * @param oldAddress The old address of the PriceOracleSentinel\n     * @param newAddress The new address of the PriceOracleSentinel\n     */\n    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the pool data provider is updated.\n     * @param oldAddress The old address of the PoolDataProvider\n     * @param newAddress The new address of the PoolDataProvider\n     */\n    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when a new proxy is created.\n     * @param id The identifier of the proxy\n     * @param proxyAddress The address of the created proxy contract\n     * @param implementationAddress The address of the implementation contract\n     */\n    event ProxyCreated(bytes32 indexed id, address indexed proxyAddress, address indexed implementationAddress);\n\n    /**\n     * @dev Emitted when a new non-proxied contract address is registered.\n     * @param id The identifier of the contract\n     * @param oldAddress The address of the old contract\n     * @param newAddress The address of the new contract\n     */\n    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);\n\n    /**\n     * @dev Emitted when the implementation of the proxy registered with id is updated\n     * @param id The identifier of the contract\n     * @param proxyAddress The address of the proxy contract\n     * @param oldImplementationAddress The address of the old implementation contract\n     * @param newImplementationAddress The address of the new implementation contract\n     */\n    event AddressSetAsProxy(\n        bytes32 indexed id,\n        address indexed proxyAddress,\n        address oldImplementationAddress,\n        address indexed newImplementationAddress\n    );\n\n    /**\n     * @notice Returns the id of the Aave market to which this contract points to.\n     * @return The market id\n     */\n    function getMarketId() external view returns (string memory);\n\n    /**\n     * @notice Associates an id with a specific PoolAddressesProvider.\n     * @dev This can be used to create an onchain registry of PoolAddressesProviders to\n     * identify and validate multiple Aave markets.\n     * @param newMarketId The market id\n     */\n    function setMarketId(string calldata newMarketId) external;\n\n    /**\n     * @notice Returns an address by its identifier.\n     * @dev The returned address might be an EOA or a contract, potentially proxied\n     * @dev It returns ZERO if there is no registered address with the given id\n     * @param id The id\n     * @return The address of the registered for the specified id\n     */\n    function getAddress(bytes32 id) external view returns (address);\n\n    /**\n     * @notice General function to update the implementation of a proxy registered with\n     * certain `id`. If there is no proxy registered, it will instantiate one and\n     * set as implementation the `newImplementationAddress`.\n     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit\n     * setter function, in order to avoid unexpected consequences\n     * @param id The id\n     * @param newImplementationAddress The address of the new implementation\n     */\n    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;\n\n    /**\n     * @notice Sets an address for an id replacing the address saved in the addresses map.\n     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement\n     * @param id The id\n     * @param newAddress The address to set\n     */\n    function setAddress(bytes32 id, address newAddress) external;\n\n    /**\n     * @notice Returns the address of the Pool proxy.\n     * @return The Pool proxy address\n     */\n    function getPool() external view returns (address);\n\n    /**\n     * @notice Updates the implementation of the Pool, or creates a proxy\n     * setting the new `pool` implementation when the function is called for the first time.\n     * @param newPoolImpl The new Pool implementation\n     */\n    function setPoolImpl(address newPoolImpl) external;\n\n    /**\n     * @notice Returns the address of the PoolConfigurator proxy.\n     * @return The PoolConfigurator proxy address\n     */\n    function getPoolConfigurator() external view returns (address);\n\n    /**\n     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy\n     * setting the new `PoolConfigurator` implementation when the function is called for the first time.\n     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation\n     */\n    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;\n\n    /**\n     * @notice Returns the address of the price oracle.\n     * @return The address of the PriceOracle\n     */\n    function getPriceOracle() external view returns (address);\n\n    /**\n     * @notice Updates the address of the price oracle.\n     * @param newPriceOracle The address of the new PriceOracle\n     */\n    function setPriceOracle(address newPriceOracle) external;\n\n    /**\n     * @notice Returns the address of the ACL manager.\n     * @return The address of the ACLManager\n     */\n    function getACLManager() external view returns (address);\n\n    /**\n     * @notice Updates the address of the ACL manager.\n     * @param newAclManager The address of the new ACLManager\n     */\n    function setACLManager(address newAclManager) external;\n\n    /**\n     * @notice Returns the address of the ACL admin.\n     * @return The address of the ACL admin\n     */\n    function getACLAdmin() external view returns (address);\n\n    /**\n     * @notice Updates the address of the ACL admin.\n     * @param newAclAdmin The address of the new ACL admin\n     */\n    function setACLAdmin(address newAclAdmin) external;\n\n    /**\n     * @notice Returns the address of the price oracle sentinel.\n     * @return The address of the PriceOracleSentinel\n     */\n    function getPriceOracleSentinel() external view returns (address);\n\n    /**\n     * @notice Updates the address of the price oracle sentinel.\n     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel\n     */\n    function setPriceOracleSentinel(address newPriceOracleSentinel) external;\n\n    /**\n     * @notice Returns the address of the data provider.\n     * @return The address of the DataProvider\n     */\n    function getPoolDataProvider() external view returns (address);\n\n    /**\n     * @notice Updates the address of the data provider.\n     * @param newDataProvider The address of the new DataProvider\n     */\n    function setPoolDataProvider(address newDataProvider) external;\n}\n"
      },
      "src/interfaces/external/DataTypes.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary DataTypes {\n    struct ReserveData {\n        //stores the reserve configuration\n        ReserveConfigurationMap configuration;\n        //the liquidity index. Expressed in ray\n        uint128 liquidityIndex;\n        //the current supply rate. Expressed in ray\n        uint128 currentLiquidityRate;\n        //variable borrow index. Expressed in ray\n        uint128 variableBorrowIndex;\n        //the current variable borrow rate. Expressed in ray\n        uint128 currentVariableBorrowRate;\n        //the current stable borrow rate. Expressed in ray\n        uint128 currentStableBorrowRate;\n        //timestamp of last update\n        uint40 lastUpdateTimestamp;\n        //the id of the reserve. Represents the position in the list of the active reserves\n        uint16 id;\n        //aToken address\n        address aTokenAddress;\n        //stableDebtToken address\n        address stableDebtTokenAddress;\n        //variableDebtToken address\n        address variableDebtTokenAddress;\n        //address of the interest rate strategy\n        address interestRateStrategyAddress;\n        //the current treasury balance, scaled\n        uint128 accruedToTreasury;\n        //the outstanding unbacked aTokens minted through the bridging feature\n        uint128 unbacked;\n        //the outstanding debt borrowed against this asset in isolation mode\n        uint128 isolationModeTotalDebt;\n    }\n\n    struct ReserveConfigurationMap {\n        //bit 0-15: LTV\n        //bit 16-31: Liq. threshold\n        //bit 32-47: Liq. bonus\n        //bit 48-55: Decimals\n        //bit 56: reserve is active\n        //bit 57: reserve is frozen\n        //bit 58: borrowing is enabled\n        //bit 59: stable rate borrowing enabled\n        //bit 60: asset is paused\n        //bit 61: borrowing in isolation mode is enabled\n        //bit 62-63: reserved\n        //bit 64-79: reserve factor\n        //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap\n        //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap\n        //bit 152-167 liquidation protocol fee\n        //bit 168-175 eMode category\n        //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled\n        //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals\n        //bit 252-255 unused\n\n        uint256 data;\n    }\n\n    struct UserConfigurationMap {\n        /**\n         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.\n         * The first bit indicates if an asset is used as collateral by the user, the second whether an\n         * asset is borrowed by the user.\n         */\n        uint256 data;\n    }\n\n    struct EModeCategory {\n        // each eMode category has a custom ltv and liquidation threshold\n        uint16 ltv;\n        uint16 liquidationThreshold;\n        uint16 liquidationBonus;\n        // each eMode category may or may not have a custom oracle to override the individual assets price oracles\n        address priceSource;\n        string label;\n    }\n\n    enum InterestRateMode {\n        NONE,\n        STABLE,\n        VARIABLE\n    }\n\n    struct ReserveCache {\n        uint256 currScaledVariableDebt;\n        uint256 nextScaledVariableDebt;\n        uint256 currPrincipalStableDebt;\n        uint256 currAvgStableBorrowRate;\n        uint256 currTotalStableDebt;\n        uint256 nextAvgStableBorrowRate;\n        uint256 nextTotalStableDebt;\n        uint256 currLiquidityIndex;\n        uint256 nextLiquidityIndex;\n        uint256 currVariableBorrowIndex;\n        uint256 nextVariableBorrowIndex;\n        uint256 currLiquidityRate;\n        uint256 currVariableBorrowRate;\n        uint256 reserveFactor;\n        ReserveConfigurationMap reserveConfiguration;\n        address aTokenAddress;\n        address stableDebtTokenAddress;\n        address variableDebtTokenAddress;\n        uint40 reserveLastUpdateTimestamp;\n        uint40 stableDebtLastUpdateTimestamp;\n    }\n\n    struct ExecuteLiquidationCallParams {\n        uint256 reservesCount;\n        uint256 debtToCover;\n        address collateralAsset;\n        address debtAsset;\n        address user;\n        bool receiveAToken;\n        address priceOracle;\n        uint8 userEModeCategory;\n        address priceOracleSentinel;\n    }\n\n    struct ExecuteSupplyParams {\n        address asset;\n        uint256 amount;\n        address onBehalfOf;\n        uint16 referralCode;\n    }\n\n    struct ExecuteBorrowParams {\n        address asset;\n        address user;\n        address onBehalfOf;\n        uint256 amount;\n        InterestRateMode interestRateMode;\n        uint16 referralCode;\n        bool releaseUnderlying;\n        uint256 maxStableRateBorrowSizePercent;\n        uint256 reservesCount;\n        address oracle;\n        uint8 userEModeCategory;\n        address priceOracleSentinel;\n    }\n\n    struct ExecuteRepayParams {\n        address asset;\n        uint256 amount;\n        InterestRateMode interestRateMode;\n        address onBehalfOf;\n        bool useATokens;\n    }\n\n    struct ExecuteWithdrawParams {\n        address asset;\n        uint256 amount;\n        address to;\n        uint256 reservesCount;\n        address oracle;\n        uint8 userEModeCategory;\n    }\n\n    struct ExecuteSetUserEModeParams {\n        uint256 reservesCount;\n        address oracle;\n        uint8 categoryId;\n    }\n\n    struct FinalizeTransferParams {\n        address asset;\n        address from;\n        address to;\n        uint256 amount;\n        uint256 balanceFromBefore;\n        uint256 balanceToBefore;\n        uint256 reservesCount;\n        address oracle;\n        uint8 fromEModeCategory;\n    }\n\n    struct FlashloanParams {\n        address receiverAddress;\n        address[] assets;\n        uint256[] amounts;\n        uint256[] interestRateModes;\n        address onBehalfOf;\n        bytes params;\n        uint16 referralCode;\n        uint256 flashLoanPremiumToProtocol;\n        uint256 flashLoanPremiumTotal;\n        uint256 maxStableRateBorrowSizePercent;\n        uint256 reservesCount;\n        address addressesProvider;\n        uint8 userEModeCategory;\n        bool isAuthorizedFlashBorrower;\n    }\n\n    struct FlashloanSimpleParams {\n        address receiverAddress;\n        address asset;\n        uint256 amount;\n        bytes params;\n        uint16 referralCode;\n        uint256 flashLoanPremiumToProtocol;\n        uint256 flashLoanPremiumTotal;\n    }\n\n    struct FlashLoanRepaymentParams {\n        uint256 amount;\n        uint256 totalPremium;\n        uint256 flashLoanPremiumToProtocol;\n        address asset;\n        address receiverAddress;\n        uint16 referralCode;\n    }\n\n    struct CalculateUserAccountDataParams {\n        UserConfigurationMap userConfig;\n        uint256 reservesCount;\n        address user;\n        address oracle;\n        uint8 userEModeCategory;\n    }\n\n    struct ValidateBorrowParams {\n        ReserveCache reserveCache;\n        UserConfigurationMap userConfig;\n        address asset;\n        address userAddress;\n        uint256 amount;\n        InterestRateMode interestRateMode;\n        uint256 maxStableLoanPercent;\n        uint256 reservesCount;\n        address oracle;\n        uint8 userEModeCategory;\n        address priceOracleSentinel;\n        bool isolationModeActive;\n        address isolationModeCollateralAddress;\n        uint256 isolationModeDebtCeiling;\n    }\n\n    struct ValidateLiquidationCallParams {\n        ReserveCache debtReserveCache;\n        uint256 totalDebt;\n        uint256 healthFactor;\n        address priceOracleSentinel;\n    }\n\n    struct CalculateInterestRatesParams {\n        uint256 unbacked;\n        uint256 liquidityAdded;\n        uint256 liquidityTaken;\n        uint256 totalStableDebt;\n        uint256 totalVariableDebt;\n        uint256 averageStableBorrowRate;\n        uint256 reserveFactor;\n        address reserve;\n        address aToken;\n    }\n\n    struct InitReserveParams {\n        address asset;\n        address aTokenAddress;\n        address stableDebtAddress;\n        address variableDebtAddress;\n        address interestRateStrategyAddress;\n        uint16 reservesCount;\n        uint16 maxNumberReserves;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\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"
      },
      "src/modules/fees/ManagementFeesLib.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport \"src/utils/LogExpMathv08.sol\";\nimport \"src/utils/Math.sol\";\n\nlibrary ManagementFeesLib {\n    using Math for uint256;\n\n    uint256 internal constant _ONE_YEAR = 365 days;\n\n    error ManagementFees__ExponentInputInvalid();\n\n    /**********************************************************************************************\n    // f = yearly management fees percentage          /  ln(1 - f) \\                             //\n    // 1y = 1 year                             a = - | ------------ |                            //\n    // a = yearly rate constant                       \\     1y     /                             //\n    **********************************************************************************************/\n    function _calcYearlyRate(uint256 yearlyFees) internal pure returns (uint256) {\n        uint256 logInput = Math.WAD - yearlyFees;\n        // Since 0 < logInput <= 1 => logResult <= 0\n        int256 logResult = LogExpMathv08.ln(int256(logInput));\n        return (uint256(-logResult) / _ONE_YEAR);\n    }\n\n    /**********************************************************************************************\n    // SF = shares to be minted as fees                                                          //\n    // TS = total supply                                   SF = TS * (e^(a*dT) -1)               //\n    // a = fees rate                                                                             //\n    // dT = elapsed time between the previous and current claim                                  //\n    **********************************************************************************************/\n    function _calcAccumulatedManagementFees(\n        uint256 currentTime,\n        uint256 lastClaim,\n        uint256 feesRate,\n        uint256 currentSupply\n    ) internal pure returns (uint256) {\n        if (feesRate == 0) {\n            return 0;\n        }\n\n        if (currentTime == lastClaim) {\n            return 0;\n        }\n\n        uint256 elapsedTime = currentTime - lastClaim;\n\n        uint256 expInput = feesRate * elapsedTime;\n\n        if (expInput > uint256(type(int256).max)) {\n            // it should never happen but just in case\n            revert ManagementFees__ExponentInputInvalid();\n        }\n\n        uint256 expResult = uint256(LogExpMathv08.exp(int256(expInput)));\n        return currentSupply.mulDivDown(expResult - Math.WAD, Math.WAD);\n    }\n}\n"
      },
      "src/modules/fees/PerformanceFeesLib.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.21;\n\nimport { Math } from \"src/utils/Math.sol\";\n\nlibrary PerformanceFeesLib {\n    using Math for uint256;\n\n    function _calcSharePrice(uint256 totalAssets, uint256 totalSupply) internal pure returns (uint256) {\n        return totalAssets.mulDivDown(Math.WAD, totalSupply);\n    }\n\n    /**\n     * @param totalAssets The current total assets\n     * @param totalSupply The total supply of shares\n     * @param highWatermarkPrice The high-water mark share price (in 18 decimals)\n     * @param performanceFeesRate The performance fees rate (in 18 decimals)\n     * @return feesAsShares The shares that should be minted as fees\n     * @return highWatermarkPrice The up-to-date high-water mark price\n     */\n    function _calcPerformanceFees(\n        uint256 totalAssets,\n        uint256 totalSupply,\n        uint256 highWatermarkPrice,\n        uint256 performanceFeesRate\n    ) internal pure returns (uint256, uint256) {\n        uint256 currentSharePrice = _calcSharePrice(totalAssets, totalSupply);\n\n        if (highWatermarkPrice == 0) {\n            // the first time the high-water mark is set\n            return (0, currentSharePrice);\n        }\n\n        if (performanceFeesRate == 0) {\n            return (0, currentSharePrice);\n        }\n\n        // Calculate the high-water mark total assets (in asset decimals)\n        uint256 highWatermarkTotalAssets = totalSupply.mulDivDown(highWatermarkPrice, Math.WAD);\n\n        if (totalAssets <= highWatermarkTotalAssets) {\n            // no positive performance\n            return (0, highWatermarkPrice);\n        }\n\n        // Calculate the increase in totalAssets (in asset decimals)\n        uint256 assetsIncrease;\n        unchecked {\n            assetsIncrease = totalAssets - highWatermarkTotalAssets;\n        }\n\n        // NB: performanceFeesRate * assetsIncrease = feesAsShares / (totalSupply + feesAsShares) * totalAssets\n        uint256 foo = performanceFeesRate * assetsIncrease;\n        uint256 feesAsShares = foo.mulDivDown(totalSupply, totalAssets * Math.WAD - foo);\n\n        // it can return feesAsShares = 0 if the price increase is too small\n        return (feesAsShares, currentSharePrice);\n    }\n}\n"
      },
      "lib/chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./AggregatorInterface.sol\";\nimport \"./AggregatorV3Interface.sol\";\n\ninterface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}\n"
      },
      "lib/v3-core/contracts/libraries/FullMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n    /// @param a The multiplicand\n    /// @param b The multiplier\n    /// @param denominator The divisor\n    /// @return result The 256-bit result\n    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n    function mulDiv(\n        uint256 a,\n        uint256 b,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = a * b\n            // Compute the product mod 2**256 and mod 2**256 - 1\n            // then use the Chinese Remainder Theorem to reconstruct\n            // the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2**256 + prod0\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(a, b, not(0))\n                prod0 := mul(a, b)\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                require(denominator > 0);\n                assembly {\n                    result := div(prod0, denominator)\n                }\n                return result;\n            }\n\n            // Make sure the result is less than 2**256.\n            // Also prevents denominator == 0\n            require(denominator > prod1);\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0]\n            // Compute remainder using mulmod\n            uint256 remainder;\n            assembly {\n                remainder := mulmod(a, b, denominator)\n            }\n            // Subtract 256 bit number from 512 bit number\n            assembly {\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator\n            // Compute largest power of two divisor of denominator.\n            // Always >= 1.\n            uint256 twos = (0 - denominator) & denominator;\n            // Divide denominator by power of two\n            assembly {\n                denominator := div(denominator, twos)\n            }\n\n            // Divide [prod1 prod0] by the factors of two\n            assembly {\n                prod0 := div(prod0, twos)\n            }\n            // Shift in bits from prod1 into prod0. For this we need\n            // to flip `twos` such that it is 2**256 / twos.\n            // If twos is zero, then it becomes one\n            assembly {\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2**256\n            // Now that denominator is an odd number, it has an inverse\n            // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n            // Compute the inverse by starting with a seed that is correct\n            // correct for four bits. That is, denominator * inv = 1 mod 2**4\n            uint256 inv = (3 * denominator) ^ 2;\n            // Now use Newton-Raphson iteration to improve the precision.\n            // Thanks to Hensel's lifting lemma, this also works in modular\n            // arithmetic, doubling the correct bits in each step.\n            inv *= 2 - denominator * inv; // inverse mod 2**8\n            inv *= 2 - denominator * inv; // inverse mod 2**16\n            inv *= 2 - denominator * inv; // inverse mod 2**32\n            inv *= 2 - denominator * inv; // inverse mod 2**64\n            inv *= 2 - denominator * inv; // inverse mod 2**128\n            inv *= 2 - denominator * inv; // inverse mod 2**256\n\n            // Because the division is now exact we can divide by multiplying\n            // with the modular inverse of denominator. This will give us the\n            // correct result modulo 2**256. Since the precoditions guarantee\n            // that the outcome is less than 2**256, this is the final result.\n            // We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inv;\n            return result;\n        }\n    }\n\n    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n    /// @param a The multiplicand\n    /// @param b The multiplier\n    /// @param denominator The divisor\n    /// @return result The 256-bit result\n    function mulDivRoundingUp(\n        uint256 a,\n        uint256 b,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        unchecked {\n            result = mulDiv(a, b, denominator);\n            if (mulmod(a, b, denominator) > 0) {\n                require(result < type(uint256).max);\n                result++;\n            }\n        }\n    }\n}\n"
      },
      "lib/v3-core/contracts/libraries/TickMath.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\n/// @title Math library for computing sqrt prices from ticks and vice versa\n/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports\n/// prices between 2**-128 and 2**128\nlibrary TickMath {\n    error T();\n    error R();\n\n    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128\n    int24 internal constant MIN_TICK = -887272;\n    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128\n    int24 internal constant MAX_TICK = -MIN_TICK;\n\n    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\n    uint160 internal constant MIN_SQRT_RATIO = 4295128739;\n    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\n    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n    /// @notice Calculates sqrt(1.0001^tick) * 2^96\n    /// @dev Throws if |tick| > max tick\n    /// @param tick The input tick for the above formula\n    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)\n    /// at the given tick\n    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {\n        unchecked {\n            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n            if (absTick > uint256(int256(MAX_TICK))) revert T();\n\n            uint256 ratio = absTick & 0x1 != 0\n                ? 0xfffcb933bd6fad37aa2d162d1a594001\n                : 0x100000000000000000000000000000000;\n            if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;\n            if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;\n            if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;\n            if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;\n            if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;\n            if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;\n            if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;\n            if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;\n            if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;\n            if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;\n            if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;\n            if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;\n            if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;\n            if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;\n            if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;\n            if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;\n            if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;\n            if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;\n            if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;\n\n            if (tick > 0) ratio = type(uint256).max / ratio;\n\n            // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.\n            // we then downcast because we know the result always fits within 160 bits due to our tick input constraint\n            // we round up in the division so getTickAtSqrtRatio of the output price is always consistent\n            sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n        }\n    }\n\n    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio\n    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may\n    /// ever return.\n    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96\n    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio\n    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {\n        unchecked {\n            // second inequality must be < because the price can never reach the price at the max tick\n            if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R();\n            uint256 ratio = uint256(sqrtPriceX96) << 32;\n\n            uint256 r = ratio;\n            uint256 msb = 0;\n\n            assembly {\n                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(5, gt(r, 0xFFFFFFFF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(4, gt(r, 0xFFFF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(3, gt(r, 0xFF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(2, gt(r, 0xF))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := shl(1, gt(r, 0x3))\n                msb := or(msb, f)\n                r := shr(f, r)\n            }\n            assembly {\n                let f := gt(r, 0x1)\n                msb := or(msb, f)\n            }\n\n            if (msb >= 128) r = ratio >> (msb - 127);\n            else r = ratio << (127 - msb);\n\n            int256 log_2 = (int256(msb) - 128) << 64;\n\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(63, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(62, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(61, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(60, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(59, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(58, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(57, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(56, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(55, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(54, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(53, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(52, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(51, f))\n                r := shr(f, r)\n            }\n            assembly {\n                r := shr(127, mul(r, r))\n                let f := shr(128, r)\n                log_2 := or(log_2, shl(50, f))\n            }\n\n            int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number\n\n            int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);\n            int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);\n\n            tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;\n        }\n    }\n}\n"
      },
      "lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\nimport {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol';\nimport {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol';\nimport {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol';\nimport {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol';\nimport {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol';\nimport {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol';\nimport {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol';\n\n/// @title The interface for a Uniswap V3 Pool\n/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface IUniswapV3Pool is\n    IUniswapV3PoolImmutables,\n    IUniswapV3PoolState,\n    IUniswapV3PoolDerivedState,\n    IUniswapV3PoolActions,\n    IUniswapV3PoolOwnerActions,\n    IUniswapV3PoolErrors,\n    IUniswapV3PoolEvents\n{\n\n}\n"
      },
      "src/utils/LogExpMathv08.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n\n// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npragma solidity ^0.8.0;\n\n/* solhint-disable */\n\n/**\n * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).\n *\n * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural\n * exponentiation and logarithm (where the base is Euler's number).\n *\n * @dev This library was forked from the LogExpMath.sol by Balancer in enable its use with solidity ^0.8.0.\n * https://github.com/balancer/balancer-v2-monorepo/blob/master/pkg/solidity-utils/contracts/math/LogExpMath.sol\n */\nlibrary LogExpMathv08 {\n    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying\n    // two numbers, and multiply by ONE when dividing them.\n\n    error X_OUT_OF_BOUNDS();\n    error Y_OUT_OF_BOUNDS();\n    error PRODUCT_OUT_OF_BOUNDS();\n    error INVALID_EXPONENT();\n    error OUT_OF_BOUNDS();\n\n    // All arguments and return values are 18 decimal fixed point numbers.\n    int256 constant ONE_18 = 1e18;\n\n    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the\n    // case of ln36, 36 decimals.\n    int256 constant ONE_20 = 1e20;\n    int256 constant ONE_36 = 1e36;\n\n    // The domain of natural exponentiation is bound by the word size and number of decimals used.\n    //\n    // Because internally the result will be stored using 20 decimals, the largest possible result is\n    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.\n    // The smallest possible result is 10^(-18), which makes largest negative argument\n    // ln(10^(-18)) = -41.446531673892822312.\n    // We use 130.0 and -41.0 to have some safety margin.\n    int256 constant MAX_NATURAL_EXPONENT = 130e18;\n    int256 constant MIN_NATURAL_EXPONENT = -41e18;\n\n    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point\n    // 256 bit integer.\n    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;\n    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;\n\n    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);\n\n    // 18 decimal constants\n    int256 constant x0 = 128000000000000000000; // 2ˆ7\n    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)\n    int256 constant x1 = 64000000000000000000; // 2ˆ6\n    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)\n\n    // 20 decimal constants\n    int256 constant x2 = 3200000000000000000000; // 2ˆ5\n    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)\n    int256 constant x3 = 1600000000000000000000; // 2ˆ4\n    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)\n    int256 constant x4 = 800000000000000000000; // 2ˆ3\n    int256 constant a4 = 298095798704172827474000; // eˆ(x4)\n    int256 constant x5 = 400000000000000000000; // 2ˆ2\n    int256 constant a5 = 5459815003314423907810; // eˆ(x5)\n    int256 constant x6 = 200000000000000000000; // 2ˆ1\n    int256 constant a6 = 738905609893065022723; // eˆ(x6)\n    int256 constant x7 = 100000000000000000000; // 2ˆ0\n    int256 constant a7 = 271828182845904523536; // eˆ(x7)\n    int256 constant x8 = 50000000000000000000; // 2ˆ-1\n    int256 constant a8 = 164872127070012814685; // eˆ(x8)\n    int256 constant x9 = 25000000000000000000; // 2ˆ-2\n    int256 constant a9 = 128402541668774148407; // eˆ(x9)\n    int256 constant x10 = 12500000000000000000; // 2ˆ-3\n    int256 constant a10 = 113314845306682631683; // eˆ(x10)\n    int256 constant x11 = 6250000000000000000; // 2ˆ-4\n    int256 constant a11 = 106449445891785942956; // eˆ(x11)\n\n    /**\n     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.\n     *\n     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.\n     */\n    function pow(uint256 x, uint256 y) internal pure returns (uint256) {\n        unchecked {\n            if (y == 0) {\n                // We solve the 0^0 indetermination by making it equal one.\n                return uint256(ONE_18);\n            }\n\n            if (x == 0) {\n                return 0;\n            }\n\n            // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to\n            // arrive at that result. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means\n            // x^y = exp(y * ln(x)).\n\n            // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.\n            if (x >> 255 != 0) revert X_OUT_OF_BOUNDS();\n            int256 x_int256 = int256(x);\n\n            // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In\n            // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.\n\n            // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.\n            if (y >= MILD_EXPONENT_BOUND) revert Y_OUT_OF_BOUNDS();\n            int256 y_int256 = int256(y);\n\n            int256 logx_times_y;\n            if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {\n                int256 ln_36_x = _ln_36(x_int256);\n\n                // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just\n                // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal\n                // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the\n                // (downscaled) last 18 decimals.\n                logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);\n            } else {\n                logx_times_y = _ln(x_int256) * y_int256;\n            }\n            logx_times_y /= ONE_18;\n\n            // Finally, we compute exp(y * ln(x)) to arrive at x^y\n            if (MIN_NATURAL_EXPONENT > logx_times_y || logx_times_y > MAX_NATURAL_EXPONENT)\n                revert PRODUCT_OUT_OF_BOUNDS();\n\n            return uint256(exp(logx_times_y));\n        }\n    }\n\n    /**\n     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.\n     *\n     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.\n     */\n    function exp(int256 x) internal pure returns (int256) {\n        unchecked {\n            if (x < MIN_NATURAL_EXPONENT || x > MAX_NATURAL_EXPONENT) revert INVALID_EXPONENT();\n\n            if (x < 0) {\n                // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it\n                // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).\n                // Fixed point division requires multiplying by ONE_18.\n                return ((ONE_18 * ONE_18) / exp(-x));\n            }\n\n            // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,\n            // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7\n            // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the\n            // decomposition.\n            // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this\n            // decomposition, which will be lower than the smallest x_n.\n            // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.\n            // We mutate x by subtracting x_n, making it the remainder of the decomposition.\n\n            // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause\n            // intermediate overflows. Instead we store them as plain integers, with 0 decimals.\n            // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the\n            // decomposition.\n\n            // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct\n            // it and compute the accumulated product.\n\n            int256 firstAN;\n            if (x >= x0) {\n                x -= x0;\n                firstAN = a0;\n            } else if (x >= x1) {\n                x -= x1;\n                firstAN = a1;\n            } else {\n                firstAN = 1; // One with no decimal places\n            }\n\n            // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the\n            // smaller terms.\n            x *= 100;\n\n            // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point\n            // one. Recall that fixed point multiplication requires dividing by ONE_20.\n            int256 product = ONE_20;\n\n            if (x >= x2) {\n                x -= x2;\n                product = (product * a2) / ONE_20;\n            }\n            if (x >= x3) {\n                x -= x3;\n                product = (product * a3) / ONE_20;\n            }\n            if (x >= x4) {\n                x -= x4;\n                product = (product * a4) / ONE_20;\n            }\n            if (x >= x5) {\n                x -= x5;\n                product = (product * a5) / ONE_20;\n            }\n            if (x >= x6) {\n                x -= x6;\n                product = (product * a6) / ONE_20;\n            }\n            if (x >= x7) {\n                x -= x7;\n                product = (product * a7) / ONE_20;\n            }\n            if (x >= x8) {\n                x -= x8;\n                product = (product * a8) / ONE_20;\n            }\n            if (x >= x9) {\n                x -= x9;\n                product = (product * a9) / ONE_20;\n            }\n\n            // x10 and x11 are unnecessary here since we have high enough precision already.\n\n            // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series\n            // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).\n\n            int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.\n            int256 term; // Each term in the sum, where the nth term is (x^n / n!).\n\n            // The first term is simply x.\n            term = x;\n            seriesSum += term;\n\n            // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,\n            // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.\n\n            term = ((term * x) / ONE_20) / 2;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 3;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 4;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 5;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 6;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 7;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 8;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 9;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 10;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 11;\n            seriesSum += term;\n\n            term = ((term * x) / ONE_20) / 12;\n            seriesSum += term;\n\n            // 12 Taylor terms are sufficient for 18 decimal precision.\n\n            // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor\n            // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply\n            // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),\n            // and then drop two digits to return an 18 decimal value.\n\n            return (((product * seriesSum) / ONE_20) * firstAN) / 100;\n        }\n    }\n\n    /**\n     * @dev Logarithm (log(arg, base), with signed 18 decimal fixed point base and argument.\n     */\n    function log(int256 arg, int256 base) internal pure returns (int256) {\n        unchecked {\n            // This performs a simple base change: log(arg, base) = ln(arg) / ln(base).\n\n            // Both logBase and logArg are computed as 36 decimal fixed point numbers, either by using ln_36, or by\n            // upscaling.\n\n            int256 logBase;\n            if (LN_36_LOWER_BOUND < base && base < LN_36_UPPER_BOUND) {\n                logBase = _ln_36(base);\n            } else {\n                logBase = _ln(base) * ONE_18;\n            }\n\n            int256 logArg;\n            if (LN_36_LOWER_BOUND < arg && arg < LN_36_UPPER_BOUND) {\n                logArg = _ln_36(arg);\n            } else {\n                logArg = _ln(arg) * ONE_18;\n            }\n\n            // When dividing, we multiply by ONE_18 to arrive at a result with 18 decimal places\n            return (logArg * ONE_18) / logBase;\n        }\n    }\n\n    /**\n     * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.\n     */\n    function ln(int256 a) internal pure returns (int256) {\n        unchecked {\n            // The real natural logarithm is not defined for negative numbers or zero.\n            if (a <= 0) revert OUT_OF_BOUNDS();\n            if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {\n                return _ln_36(a) / ONE_18;\n            } else {\n                return _ln(a);\n            }\n        }\n    }\n\n    /**\n     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.\n     */\n    function _ln(int256 a) private pure returns (int256) {\n        unchecked {\n            if (a < ONE_18) {\n                // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less\n                // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.\n                // Fixed point division requires multiplying by ONE_18.\n                return (-_ln((ONE_18 * ONE_18) / a));\n            }\n\n            // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which\n            // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,\n            // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot\n            // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.\n            // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this\n            // decomposition, which will be lower than the smallest a_n.\n            // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.\n            // We mutate a by subtracting a_n, making it the remainder of the decomposition.\n\n            // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point\n            // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by\n            // ONE_18 to convert them to fixed point.\n            // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide\n            // by it and compute the accumulated sum.\n\n            int256 sum = 0;\n            if (a >= a0 * ONE_18) {\n                a /= a0; // Integer, not fixed point division\n                sum += x0;\n            }\n\n            if (a >= a1 * ONE_18) {\n                a /= a1; // Integer, not fixed point division\n                sum += x1;\n            }\n\n            // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.\n            sum *= 100;\n            a *= 100;\n\n            // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.\n\n            if (a >= a2) {\n                a = (a * ONE_20) / a2;\n                sum += x2;\n            }\n\n            if (a >= a3) {\n                a = (a * ONE_20) / a3;\n                sum += x3;\n            }\n\n            if (a >= a4) {\n                a = (a * ONE_20) / a4;\n                sum += x4;\n            }\n\n            if (a >= a5) {\n                a = (a * ONE_20) / a5;\n                sum += x5;\n            }\n\n            if (a >= a6) {\n                a = (a * ONE_20) / a6;\n                sum += x6;\n            }\n\n            if (a >= a7) {\n                a = (a * ONE_20) / a7;\n                sum += x7;\n            }\n\n            if (a >= a8) {\n                a = (a * ONE_20) / a8;\n                sum += x8;\n            }\n\n            if (a >= a9) {\n                a = (a * ONE_20) / a9;\n                sum += x9;\n            }\n\n            if (a >= a10) {\n                a = (a * ONE_20) / a10;\n                sum += x10;\n            }\n\n            if (a >= a11) {\n                a = (a * ONE_20) / a11;\n                sum += x11;\n            }\n\n            // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series\n            // that converges rapidly for values of `a` close to one - the same one used in ln_36.\n            // Let z = (a - 1) / (a + 1).\n            // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))\n\n            // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires\n            // division by ONE_20.\n            int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);\n            int256 z_squared = (z * z) / ONE_20;\n\n            // num is the numerator of the series: the z^(2 * n + 1) term\n            int256 num = z;\n\n            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z\n            int256 seriesSum = num;\n\n            // In each step, the numerator is multiplied by z^2\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 3;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 5;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 7;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 9;\n\n            num = (num * z_squared) / ONE_20;\n            seriesSum += num / 11;\n\n            // 6 Taylor terms are sufficient for 36 decimal precision.\n\n            // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)\n            seriesSum *= 2;\n\n            // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both\n            // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal\n            // value.\n\n            return (sum + seriesSum) / 100;\n        }\n    }\n\n    /**\n     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,\n     * for x close to one.\n     *\n     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.\n     */\n    function _ln_36(int256 x) private pure returns (int256) {\n        unchecked {\n            // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits\n            // worthwhile.\n\n            // First, we transform x to a 36 digit fixed point value.\n            x *= ONE_18;\n\n            // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).\n            // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))\n\n            // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires\n            // division by ONE_36.\n            int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);\n            int256 z_squared = (z * z) / ONE_36;\n\n            // num is the numerator of the series: the z^(2 * n + 1) term\n            int256 num = z;\n\n            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z\n            int256 seriesSum = num;\n\n            // In each step, the numerator is multiplied by z^2\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 3;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 5;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 7;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 9;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 11;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 13;\n\n            num = (num * z_squared) / ONE_36;\n            seriesSum += num / 15;\n\n            // 8 Taylor terms are sufficient for 36 decimal precision.\n\n            // All that remains is multiplying by 2 (non fixed point).\n            return seriesSum * 2;\n        }\n    }\n}\n"
      },
      "lib/chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n  function latestAnswer() external view returns (int256);\n\n  function latestTimestamp() external view returns (uint256);\n\n  function latestRound() external view returns (uint256);\n\n  function getAnswer(uint256 roundId) external view returns (int256);\n\n  function getTimestamp(uint256 roundId) external view returns (uint256);\n\n  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);\n\n  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);\n}\n"
      },
      "lib/chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n  function decimals() external view returns (uint8);\n\n  function description() external view returns (string memory);\n\n  function version() external view returns (uint256);\n\n  function getRoundData(uint80 _roundId)\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n\n  function latestRoundData()\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that never changes\n/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values\ninterface IUniswapV3PoolImmutables {\n    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface\n    /// @return The contract address\n    function factory() external view returns (address);\n\n    /// @notice The first of the two tokens of the pool, sorted by address\n    /// @return The token contract address\n    function token0() external view returns (address);\n\n    /// @notice The second of the two tokens of the pool, sorted by address\n    /// @return The token contract address\n    function token1() external view returns (address);\n\n    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6\n    /// @return The fee\n    function fee() external view returns (uint24);\n\n    /// @notice The pool tick spacing\n    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive\n    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...\n    /// This value is an int24 to avoid casting even though it is always positive.\n    /// @return The tick spacing\n    function tickSpacing() external view returns (int24);\n\n    /// @notice The maximum amount of position liquidity that can use any tick in the range\n    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and\n    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool\n    /// @return The max amount of liquidity per tick\n    function maxLiquidityPerTick() external view returns (uint128);\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that can change\n/// @notice These methods compose the pool's state, and can change with any frequency including multiple times\n/// per transaction\ninterface IUniswapV3PoolState {\n    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas\n    /// when accessed externally.\n    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value\n    /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run.\n    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick\n    /// boundary.\n    /// @return observationIndex The index of the last oracle observation that was written,\n    /// @return observationCardinality The current maximum number of observations stored in the pool,\n    /// @return observationCardinalityNext The next maximum number of observations, to be updated when the observation.\n    /// @return feeProtocol The protocol fee for both tokens of the pool.\n    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0\n    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.\n    /// unlocked Whether the pool is currently locked to reentrancy\n    function slot0()\n        external\n        view\n        returns (\n            uint160 sqrtPriceX96,\n            int24 tick,\n            uint16 observationIndex,\n            uint16 observationCardinality,\n            uint16 observationCardinalityNext,\n            uint8 feeProtocol,\n            bool unlocked\n        );\n\n    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool\n    /// @dev This value can overflow the uint256\n    function feeGrowthGlobal0X128() external view returns (uint256);\n\n    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool\n    /// @dev This value can overflow the uint256\n    function feeGrowthGlobal1X128() external view returns (uint256);\n\n    /// @notice The amounts of token0 and token1 that are owed to the protocol\n    /// @dev Protocol fees will never exceed uint128 max in either token\n    function protocolFees() external view returns (uint128 token0, uint128 token1);\n\n    /// @notice The currently in range liquidity available to the pool\n    /// @dev This value has no relationship to the total liquidity across all ticks\n    /// @return The liquidity at the current price of the pool\n    function liquidity() external view returns (uint128);\n\n    /// @notice Look up information about a specific tick in the pool\n    /// @param tick The tick to look up\n    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n    /// tick upper\n    /// @return liquidityNet how much liquidity changes when the pool price crosses the tick,\n    /// @return feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n    /// @return feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n    /// @return tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n    /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,\n    /// @return secondsOutside the seconds spent on the other side of the tick from the current tick,\n    /// @return initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.\n    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n    /// a specific position.\n    function ticks(int24 tick)\n        external\n        view\n        returns (\n            uint128 liquidityGross,\n            int128 liquidityNet,\n            uint256 feeGrowthOutside0X128,\n            uint256 feeGrowthOutside1X128,\n            int56 tickCumulativeOutside,\n            uint160 secondsPerLiquidityOutsideX128,\n            uint32 secondsOutside,\n            bool initialized\n        );\n\n    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information\n    function tickBitmap(int16 wordPosition) external view returns (uint256);\n\n    /// @notice Returns the information about a position by the position's key\n    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper\n    /// @return liquidity The amount of liquidity in the position,\n    /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,\n    /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,\n    /// @return tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,\n    /// @return tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke\n    function positions(bytes32 key)\n        external\n        view\n        returns (\n            uint128 liquidity,\n            uint256 feeGrowthInside0LastX128,\n            uint256 feeGrowthInside1LastX128,\n            uint128 tokensOwed0,\n            uint128 tokensOwed1\n        );\n\n    /// @notice Returns data about a specific observation index\n    /// @param index The element of the observations array to fetch\n    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time\n    /// ago, rather than at a specific index in the array.\n    /// @return blockTimestamp The timestamp of the observation,\n    /// @return tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,\n    /// @return secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,\n    /// @return initialized whether the observation has been initialized and the values are safe to use\n    function observations(uint256 index)\n        external\n        view\n        returns (\n            uint32 blockTimestamp,\n            int56 tickCumulative,\n            uint160 secondsPerLiquidityCumulativeX128,\n            bool initialized\n        );\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that is not stored\n/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the\n/// blockchain. The functions here may have variable gas costs.\ninterface IUniswapV3PoolDerivedState {\n    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp\n    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing\n    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,\n    /// you must call it with secondsAgos = [3600, 0].\n    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in\n    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.\n    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned\n    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp\n    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block\n    /// timestamp\n    function observe(uint32[] calldata secondsAgos)\n        external\n        view\n        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n\n    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range\n    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.\n    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first\n    /// snapshot is taken and the second snapshot is taken.\n    /// @param tickLower The lower tick of the range\n    /// @param tickUpper The upper tick of the range\n    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range\n    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range\n    /// @return secondsInside The snapshot of seconds per liquidity for the range\n    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)\n        external\n        view\n        returns (\n            int56 tickCumulativeInside,\n            uint160 secondsPerLiquidityInsideX128,\n            uint32 secondsInside\n        );\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissionless pool actions\n/// @notice Contains pool methods that can be called by anyone\ninterface IUniswapV3PoolActions {\n    /// @notice Sets the initial price for the pool\n    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value\n    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96\n    function initialize(uint160 sqrtPriceX96) external;\n\n    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback\n    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends\n    /// on tickLower, tickUpper, the amount of liquidity, and the current price.\n    /// @param recipient The address for which the liquidity will be created\n    /// @param tickLower The lower tick of the position in which to add liquidity\n    /// @param tickUpper The upper tick of the position in which to add liquidity\n    /// @param amount The amount of liquidity to mint\n    /// @param data Any data that should be passed through to the callback\n    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback\n    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback\n    function mint(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount,\n        bytes calldata data\n    ) external returns (uint256 amount0, uint256 amount1);\n\n    /// @notice Collects tokens owed to a position\n    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.\n    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or\n    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the\n    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.\n    /// @param recipient The address which should receive the fees collected\n    /// @param tickLower The lower tick of the position for which to collect fees\n    /// @param tickUpper The upper tick of the position for which to collect fees\n    /// @param amount0Requested How much token0 should be withdrawn from the fees owed\n    /// @param amount1Requested How much token1 should be withdrawn from the fees owed\n    /// @return amount0 The amount of fees collected in token0\n    /// @return amount1 The amount of fees collected in token1\n    function collect(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external returns (uint128 amount0, uint128 amount1);\n\n    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position\n    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0\n    /// @dev Fees must be collected separately via a call to #collect\n    /// @param tickLower The lower tick of the position for which to burn liquidity\n    /// @param tickUpper The upper tick of the position for which to burn liquidity\n    /// @param amount How much liquidity to burn\n    /// @return amount0 The amount of token0 sent to the recipient\n    /// @return amount1 The amount of token1 sent to the recipient\n    function burn(\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount\n    ) external returns (uint256 amount0, uint256 amount1);\n\n    /// @notice Swap token0 for token1, or token1 for token0\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback\n    /// @param recipient The address to receive the output of the swap\n    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0\n    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)\n    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this\n    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap\n    /// @param data Any data to be passed through to the callback\n    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive\n    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive\n    function swap(\n        address recipient,\n        bool zeroForOne,\n        int256 amountSpecified,\n        uint160 sqrtPriceLimitX96,\n        bytes calldata data\n    ) external returns (int256 amount0, int256 amount1);\n\n    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback\n    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling\n    /// with 0 amount{0,1} and sending the donation amount(s) from the callback\n    /// @param recipient The address which will receive the token0 and token1 amounts\n    /// @param amount0 The amount of token0 to send\n    /// @param amount1 The amount of token1 to send\n    /// @param data Any data to be passed through to the callback\n    function flash(\n        address recipient,\n        uint256 amount0,\n        uint256 amount1,\n        bytes calldata data\n    ) external;\n\n    /// @notice Increase the maximum number of price and liquidity observations that this pool will store\n    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to\n    /// the input observationCardinalityNext.\n    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store\n    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissioned pool actions\n/// @notice Contains pool methods that may only be called by the factory owner\ninterface IUniswapV3PoolOwnerActions {\n    /// @notice Set the denominator of the protocol's % share of the fees\n    /// @param feeProtocol0 new protocol fee for token0 of the pool\n    /// @param feeProtocol1 new protocol fee for token1 of the pool\n    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;\n\n    /// @notice Collect the protocol fee accrued to the pool\n    /// @param recipient The address to which collected protocol fees should be sent\n    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1\n    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0\n    /// @return amount0 The protocol fee collected in token0\n    /// @return amount1 The protocol fee collected in token1\n    function collectProtocol(\n        address recipient,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external returns (uint128 amount0, uint128 amount1);\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Errors emitted by a pool\n/// @notice Contains all events emitted by the pool\ninterface IUniswapV3PoolErrors {\n    error LOK();\n    error TLU();\n    error TLM();\n    error TUM();\n    error AI();\n    error M0();\n    error M1();\n    error AS();\n    error IIA();\n    error L();\n    error F0();\n    error F1();\n}\n"
      },
      "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Events emitted by a pool\n/// @notice Contains all events emitted by the pool\ninterface IUniswapV3PoolEvents {\n    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool\n    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize\n    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96\n    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool\n    event Initialize(uint160 sqrtPriceX96, int24 tick);\n\n    /// @notice Emitted when liquidity is minted for a given position\n    /// @param sender The address that minted the liquidity\n    /// @param owner The owner of the position and recipient of any minted liquidity\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount The amount of liquidity minted to the position range\n    /// @param amount0 How much token0 was required for the minted liquidity\n    /// @param amount1 How much token1 was required for the minted liquidity\n    event Mint(\n        address sender,\n        address indexed owner,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount,\n        uint256 amount0,\n        uint256 amount1\n    );\n\n    /// @notice Emitted when fees are collected by the owner of a position\n    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees\n    /// @param owner The owner of the position for which fees are collected\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount0 The amount of token0 fees collected\n    /// @param amount1 The amount of token1 fees collected\n    event Collect(\n        address indexed owner,\n        address recipient,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount0,\n        uint128 amount1\n    );\n\n    /// @notice Emitted when a position's liquidity is removed\n    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect\n    /// @param owner The owner of the position for which liquidity is removed\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount The amount of liquidity to remove\n    /// @param amount0 The amount of token0 withdrawn\n    /// @param amount1 The amount of token1 withdrawn\n    event Burn(\n        address indexed owner,\n        int24 indexed tickLower,\n        int24 indexed tickUpper,\n        uint128 amount,\n        uint256 amount0,\n        uint256 amount1\n    );\n\n    /// @notice Emitted by the pool for any swaps between token0 and token1\n    /// @param sender The address that initiated the swap call, and that received the callback\n    /// @param recipient The address that received the output of the swap\n    /// @param amount0 The delta of the token0 balance of the pool\n    /// @param amount1 The delta of the token1 balance of the pool\n    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96\n    /// @param liquidity The liquidity of the pool after the swap\n    /// @param tick The log base 1.0001 of price of the pool after the swap\n    event Swap(\n        address indexed sender,\n        address indexed recipient,\n        int256 amount0,\n        int256 amount1,\n        uint160 sqrtPriceX96,\n        uint128 liquidity,\n        int24 tick\n    );\n\n    /// @notice Emitted by the pool for any flashes of token0/token1\n    /// @param sender The address that initiated the swap call, and that received the callback\n    /// @param recipient The address that received the tokens from flash\n    /// @param amount0 The amount of token0 that was flashed\n    /// @param amount1 The amount of token1 that was flashed\n    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee\n    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee\n    event Flash(\n        address indexed sender,\n        address indexed recipient,\n        uint256 amount0,\n        uint256 amount1,\n        uint256 paid0,\n        uint256 paid1\n    );\n\n    /// @notice Emitted by the pool for increases to the number of observations that can be stored\n    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index\n    /// just before a mint/swap/burn.\n    /// @param observationCardinalityNextOld The previous value of the next observation cardinality\n    /// @param observationCardinalityNextNew The updated value of the next observation cardinality\n    event IncreaseObservationCardinalityNext(\n        uint16 observationCardinalityNextOld,\n        uint16 observationCardinalityNextNew\n    );\n\n    /// @notice Emitted when the protocol fee is changed by the pool\n    /// @param feeProtocol0Old The previous value of the token0 protocol fee\n    /// @param feeProtocol1Old The previous value of the token1 protocol fee\n    /// @param feeProtocol0New The updated value of the token0 protocol fee\n    /// @param feeProtocol1New The updated value of the token1 protocol fee\n    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);\n\n    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner\n    /// @param sender The address that collects the protocol fees\n    /// @param recipient The address that receives the collected protocol fees\n    /// @param amount0 The amount of token0 protocol fees that is withdrawn\n    /// @param amount0 The amount of token1 protocol fees that is withdrawn\n    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@solmate/=lib/solmate/src/",
        "@forge-std/=lib/forge-std/src/",
        "@ds-test/=lib/forge-std/lib/ds-test/src/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@uniswap/v3-periphery/=lib/v3-periphery/",
        "@uniswap/v3-core/=lib/v3-core/",
        "@chainlink/=lib/chainlink/",
        "@uniswapV3P/=lib/v3-periphery/contracts/",
        "@uniswapV3C/=lib/v3-core/contracts/",
        "@balancer/=lib/balancer-v2-monorepo/pkg/",
        "@swaap-v2/=lib/swaap-v2-monorepo/pkg/",
        "script/=script/",
        "@balancer-labs/=lib/swaap-v2-monorepo/../../node_modules/@balancer-labs/",
        "@balancer-labs/v2-vault/=lib/swaap-v2-monorepo/./pkg/vault/",
        "balancer-v2-monorepo/=lib/balancer-v2-monorepo/",
        "chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "pendle-core-v2-public/=lib/pendle-core-v2-public/contracts/",
        "solmate/=lib/solmate/src/",
        "swaap-v2-monorepo/=lib/swaap-v2-monorepo/",
        "v3-core/=lib/v3-core/contracts/",
        "v3-periphery/=lib/v3-periphery/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 1000000
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": false,
      "libraries": {}
    }
  }}