{"file_path":"src/oracles/RootPriceOracle.sol","creation_status":"success","source_code":"// SPDX-License-Identifier:UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { SecurityBase } from \"src/security/SecurityBase.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IPriceOracle } from \"src/interfaces/oracles/IPriceOracle.sol\";\nimport { ISpotPriceOracle } from \"src/interfaces/oracles/ISpotPriceOracle.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { SystemComponent } from \"src/SystemComponent.sol\";\nimport { Roles } from \"src/libs/Roles.sol\";\n\ncontract RootPriceOracle is SystemComponent, SecurityBase, IRootPriceOracle {\n    address private immutable _weth;\n\n    mapping(address => IPriceOracle) public tokenMappings;\n    mapping(address => ISpotPriceOracle) public poolMappings;\n    mapping(address => uint256) public safeSpotPriceThresholds;\n    uint256 internal constant THRESHOLD_PRECISION = 10_000;\n\n    event TokenRemoved(address token);\n    event TokenRegistered(address token, address oracle);\n    event TokenRegistrationReplaced(address token, address oldOracle, address newOracle);\n\n    event SafeSpotPriceThresholdUpdated(address token, uint256 threshold);\n\n    // pool-handler specific events\n    event PoolRegistered(address indexed pool, address indexed oracle);\n    event PoolRegistrationReplaced(address indexed pool, address indexed oldOracle, address indexed newOracle);\n    event PoolRemoved(address indexed pool);\n\n    error AlreadyRegistered(address token);\n    error MissingTokenOracle(address token);\n    error MissingSpotPriceOracle(address token);\n    error MappingDoesNotExist(address token);\n    error ReplaceOldMismatch(address token, address oldExpected, address oldActual);\n    error ReplaceAlreadyMatches(address token, address newOracle);\n    error NoThresholdFound(address token);\n\n    constructor(ISystemRegistry _systemRegistry)\n        SystemComponent(_systemRegistry)\n        SecurityBase(address(_systemRegistry.accessController()))\n    {\n        _weth = address(_systemRegistry.weth());\n    }\n\n    /// @notice Register a new token to oracle mapping\n    /// @dev May require additional registration in the oracle itself\n    /// @param token address of the token to register\n    /// @param oracle address of the oracle to use to lookup price\n    function registerMapping(address token, IPriceOracle oracle) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(token, \"token\");\n        Errors.verifyNotZero(address(oracle), \"oracle\");\n        Errors.verifySystemsMatch(address(this), address(oracle));\n\n        // We want the operation of replacing a mapping to be an explicit\n        // call so we don't accidentally overwrite something\n        if (address(tokenMappings[token]) != address(0)) {\n            revert AlreadyRegistered(token);\n        }\n\n        tokenMappings[token] = oracle;\n\n        emit TokenRegistered(token, address(oracle));\n    }\n\n    /// @notice Replace an existing token -> oracle mapping\n    /// @dev Must exist, matching existing, and new != old value to successfully replace\n    /// @param token address of the token to register\n    /// @param oldOracle existing oracle address\n    /// @param newOracle new oracle address\n    function replaceMapping(\n        address token,\n        IPriceOracle oldOracle,\n        IPriceOracle newOracle\n    ) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(token, \"token\");\n        Errors.verifyNotZero(address(oldOracle), \"oldOracle\");\n        Errors.verifyNotZero(address(newOracle), \"newOracle\");\n        Errors.verifySystemsMatch(address(this), address(newOracle));\n\n        // We want to ensure you know what you're replacing so ensure\n        // you provide a matching old value\n        if (tokenMappings[token] != oldOracle) {\n            revert ReplaceOldMismatch(token, address(oldOracle), address(tokenMappings[token]));\n        }\n\n        // If the old and new values match we can assume you're not doing\n        // what you think you're doing so we just fail\n        if (oldOracle == newOracle) {\n            revert ReplaceAlreadyMatches(token, address(newOracle));\n        }\n\n        tokenMappings[token] = newOracle;\n\n        emit TokenRegistrationReplaced(token, address(oldOracle), address(newOracle));\n    }\n\n    /// @notice Remove a token to oracle mapping\n    /// @dev Must exist. Does not remove any additional configuration from the oracle itself\n    /// @param token address of the token that is registered\n    function removeMapping(address token) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(token, \"token\");\n\n        // If you're trying to remove something that doesn't exist then\n        // some condition you're expecting isn't true. We revert so you can reevaluate\n        if (address(tokenMappings[token]) == address(0)) {\n            revert MappingDoesNotExist(token);\n        }\n\n        delete tokenMappings[token];\n\n        emit TokenRemoved(token);\n    }\n\n    /// @notice Register a new liquidity pool to its LP oracle\n    /// @dev May require additional registration in the oracle itself\n    /// @param pool address of the liquidity pool\n    /// @param oracle address of the LP oracle\n    function registerPoolMapping(address pool, ISpotPriceOracle oracle) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(pool, \"pool\");\n        Errors.verifyNotZero(address(oracle), \"oracle\");\n        Errors.verifySystemsMatch(address(this), address(oracle));\n\n        if (address(poolMappings[pool]) != address(0)) {\n            revert AlreadyRegistered(pool);\n        }\n\n        poolMappings[pool] = oracle;\n\n        emit PoolRegistered(pool, address(oracle));\n    }\n\n    /// @notice Replace an existing oracle for a specified liquidity pool\n    /// @dev Must exist, matching existing, and new != old value to successfully replace\n    /// @param pool address of the liquidity pool\n    /// @param oldOracle address of the current LP oracle\n    /// @param newOracle address of the new LP oracle\n    function replacePoolMapping(\n        address pool,\n        ISpotPriceOracle oldOracle,\n        ISpotPriceOracle newOracle\n    ) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(pool, \"pool\");\n        Errors.verifyNotZero(address(oldOracle), \"oldOracle\");\n        Errors.verifyNotZero(address(newOracle), \"newOracle\");\n        Errors.verifySystemsMatch(address(this), address(newOracle));\n\n        ISpotPriceOracle currentOracle = poolMappings[pool];\n\n        if (currentOracle != oldOracle) revert ReplaceOldMismatch(pool, address(oldOracle), address(currentOracle));\n        if (oldOracle == newOracle) revert ReplaceAlreadyMatches(pool, address(newOracle));\n\n        poolMappings[pool] = newOracle;\n\n        emit PoolRegistrationReplaced(pool, address(oldOracle), address(newOracle));\n    }\n\n    /// @notice Remove an existing oracle for a specified liquidity pool\n    /// @dev Must exist. Does not remove any additional configuration from the oracle itself\n    /// @param pool address of the liquidity pool that needs oracle removal\n    function removePoolMapping(address pool) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(pool, \"pool\");\n\n        if (address(poolMappings[pool]) == address(0)) revert MappingDoesNotExist(pool);\n\n        delete poolMappings[pool];\n\n        emit PoolRemoved(pool);\n    }\n\n    /// @notice Set a threshold for a token spot price discrepancy to be considered safe\n    /// @param token address of the token to set the threshold for\n    /// @param threshold threshold to set (precision to 2 decimal places, i.e. 10000 = 100.00%)\n    function setSafeSpotPriceThreshold(address token, uint256 threshold) external hasRole(Roles.ORACLE_MANAGER) {\n        Errors.verifyNotZero(token, \"token\");\n        Errors.verifyNotZero(threshold, \"threshold\");\n\n        if (threshold > THRESHOLD_PRECISION) revert Errors.InvalidParam(\"threshold\");\n\n        safeSpotPriceThresholds[token] = threshold;\n\n        emit SafeSpotPriceThresholdUpdated(token, threshold);\n    }\n\n    /// @dev This and all price oracles are not view fn's so that we can perform the Curve reentrancy check\n    /// @inheritdoc IRootPriceOracle\n    function getPriceInEth(address token) external returns (uint256 price) {\n        // Skip the token address(0) check and just rely on the oracle lookup\n        // Emit token so we can figure out what was actually 0 later\n        IPriceOracle oracle = _checkTokenOracleRegistration(token);\n\n        price = oracle.getPriceInEth(token);\n    }\n\n    /// @inheritdoc IRootPriceOracle\n    function getFloorCeilingPrice(\n        address pool,\n        address lpToken,\n        address inQuote,\n        bool ceiling\n    ) public returns (uint256 floorOrCeilingPerLpToken) {\n        (uint256 totalReserves, uint256 floorOrCeilingPrice, uint256 totalLpSupply) =\n            _calculateReservesAndPrice(pool, lpToken, inQuote, ceiling);\n        floorOrCeilingPerLpToken = totalReserves * floorOrCeilingPrice / totalLpSupply;\n    }\n\n    /**\n     * @notice Calculates the total reserves, floor/ceiling price, and total LP supply for a given pool and token pair.\n     * @param pool The address of the liquidity pool.\n     * @param lpToken The address of the LP token.\n     * @param inQuote The address of the desired quote token.\n     * @param ceiling A boolean indicating whether to calculate the ceiling price (true) or floor price (false).\n     * @return totalReserves The total reserves in the pool, scaled to the desired quote token decimals.\n     * @return floorOrCeilingPrice The floor or ceiling price, depending on the `ceiling` parameter.\n     * @return totalLpSupply The total LP supply, scaled to the desired quote token decimals.\n     */\n    function _calculateReservesAndPrice(\n        address pool,\n        address lpToken,\n        address inQuote,\n        bool ceiling\n    ) internal returns (uint256 totalReserves, uint256 floorOrCeilingPrice, uint256 totalLpSupply) {\n        ISpotPriceOracle.ReserveItemInfo[] memory reserveInfoArray;\n\n        ISpotPriceOracle oracle = _checkSpotOracleRegistration(pool);\n        (totalLpSupply, reserveInfoArray) = oracle.getSafeSpotPriceInfo(pool, lpToken, inQuote);\n\n        uint256 inQuoteDecimals = IERC20Metadata(inQuote).decimals();\n        uint256 lpTokenDecimals = IERC20Metadata(lpToken).decimals();\n\n        totalLpSupply = _scaleValue(totalLpSupply, lpTokenDecimals, inQuoteDecimals);\n\n        uint256 nTokens = reserveInfoArray.length;\n\n        for (uint256 i = 0; i < nTokens; ++i) {\n            ISpotPriceOracle.ReserveItemInfo memory reserveInfo = reserveInfoArray[i];\n\n            uint256 safePrice = getPriceInQuote(reserveInfo.token, inQuote);\n            uint256 spotPrice = _enforceQuoteToken(inQuote, reserveInfo.actualQuoteToken, reserveInfo.rawSpotPrice);\n\n            uint256 tokenPricedDecimals = IERC20Metadata(reserveInfo.token).decimals();\n            // Scaling reserves, reserves are always returned in the decimals of the token priced.\n            totalReserves += _scaleValue(reserveInfo.reserveAmount, tokenPricedDecimals, inQuoteDecimals);\n\n            if (ceiling) {\n                floorOrCeilingPrice = Math.max(floorOrCeilingPrice, Math.max(spotPrice, safePrice));\n            } else {\n                floorOrCeilingPrice = i == 0\n                    ? Math.min(spotPrice, safePrice)\n                    : Math.min(floorOrCeilingPrice, Math.min(spotPrice, safePrice));\n            }\n        }\n    }\n\n    /**\n     * @dev Scales a value based on the token decimals and desired quote token decimals.\n     * @param value The value to be scaled.\n     * @param tokenDecimals The decimals of the token.\n     * @param inQuoteDecimals The decimals of the desired quote token.\n     * @return The scaled value.\n     */\n    function _scaleValue(\n        uint256 value,\n        uint256 tokenDecimals,\n        uint256 inQuoteDecimals\n    ) internal pure returns (uint256) {\n        if (tokenDecimals > inQuoteDecimals) {\n            return value / 10 ** (tokenDecimals - inQuoteDecimals);\n        } else if (tokenDecimals < inQuoteDecimals) {\n            return value * 10 ** (inQuoteDecimals - tokenDecimals);\n        }\n        return value;\n    }\n\n    /// @inheritdoc IRootPriceOracle\n    function getSpotPriceInEth(address token, address pool) external returns (uint256 price) {\n        Errors.verifyNotZero(token, \"token\");\n        Errors.verifyNotZero(pool, \"pool\");\n\n        ISpotPriceOracle oracle = _checkSpotOracleRegistration(pool);\n\n        address weth = address(systemRegistry.weth());\n\n        return _getSpotPriceInQuote(oracle, token, pool, weth);\n    }\n\n    function _getSpotPriceInQuote(\n        ISpotPriceOracle oracle,\n        address token,\n        address pool,\n        address quoteToken\n    ) internal returns (uint256 price) {\n        // Retrieve the spot price with weth as the requested quote token\n        (uint256 rawPrice, address actualQuoteToken) = oracle.getSpotPrice(token, pool, quoteToken);\n\n        return _enforceQuoteToken(quoteToken, actualQuoteToken, rawPrice);\n    }\n\n    /// @inheritdoc IRootPriceOracle\n    /// @dev Getting major \"Stack too deep\" errors, so if you see locals not abstracted that's why\n    function getRangePricesLP(\n        address lpToken,\n        address pool,\n        address quoteToken\n    ) external returns (uint256 spotPriceInQuote, uint256 safePriceInQuote, bool isSpotSafe) {\n        ISpotPriceOracle spotPriceOracle = _checkSpotOracleRegistration(pool);\n\n        // Retrieve the reserves info for calculations\n        (uint256 totalLPSupply, ISpotPriceOracle.ReserveItemInfo[] memory reserves) =\n            spotPriceOracle.getSafeSpotPriceInfo(pool, lpToken, quoteToken);\n\n        // if lp supply is 0 (while we hold it) means compromised pool, so return 0 for worth (and false for safety)\n        uint256 nTokens = reserves.length;\n        if (nTokens == 0) {\n            revert Errors.InvalidParam(\"reserves\");\n        }\n        if (totalLPSupply == 0) {\n            return (0, 0, false);\n        }\n\n        isSpotSafe = true; // default to true, and set to false if any threshold is breached in the loop below\n\n        // loop through reserves, and sum up aggregates\n\n        for (uint256 i = 0; i < nTokens; ++i) {\n            ISpotPriceOracle.ReserveItemInfo memory reserve = reserves[i];\n\n            uint256 threshold = safeSpotPriceThresholds[reserve.token];\n            if (threshold == 0) revert NoThresholdFound(reserve.token);\n\n            uint256 safePrice = getPriceInQuote(reserve.token, quoteToken);\n            uint256 spotPrice = _enforceQuoteToken(quoteToken, reserve.actualQuoteToken, reserve.rawSpotPrice);\n\n            //\n            // check thresholds to see if spot is safe\n            // NOTE: narrowing scope to avoid \"stack too deep\" errors\n            //\n            {\n                (uint256 largerPrice, uint256 smallerPrice) =\n                    (safePrice > spotPrice) ? (safePrice, spotPrice) : (spotPrice, safePrice);\n                uint256 priceDiff = largerPrice - smallerPrice;\n\n                if (largerPrice == 0 || (priceDiff * THRESHOLD_PRECISION / largerPrice) > threshold) {\n                    isSpotSafe = false; // validate that the spot price is safe\n                }\n            }\n\n            //\n            // add to totals (with padding to quoteToken decimals)\n            uint256 reserveTokenBaseDecimals = 10 ** IERC20Metadata(reserve.token).decimals();\n            safePriceInQuote += reserve.reserveAmount * safePrice / reserveTokenBaseDecimals;\n            spotPriceInQuote += reserve.reserveAmount * spotPrice / reserveTokenBaseDecimals;\n        }\n\n        //\n        // divide by total lp supply to get price per lp token\n        uint256 lpTokenDecimalsPad = 10 ** IERC20Metadata(lpToken).decimals();\n        safePriceInQuote = safePriceInQuote * lpTokenDecimalsPad / totalLPSupply;\n        spotPriceInQuote = spotPriceInQuote * lpTokenDecimalsPad / totalLPSupply;\n    }\n\n    /// @dev if quote token returned is not the requested one, do price conversion\n    function _enforceQuoteToken(\n        address quoteToken,\n        address actualQuoteToken,\n        uint256 rawPrice\n    ) internal returns (uint256) {\n        // If quote token returned is the requested one we return price as is\n        if (actualQuoteToken == quoteToken) {\n            return rawPrice;\n        }\n\n        uint256 decimals = IERC20Metadata(actualQuoteToken).decimals();\n\n        // If not, get the conversion rate from the actualQuoteToken to quoteToken and then derive the spot price\n        return rawPrice * getPriceInQuote(actualQuoteToken, quoteToken) / 10 ** decimals;\n    }\n\n    ///@inheritdoc IRootPriceOracle\n    function getPriceInQuote(address base, address quote) public returns (uint256) {\n        IPriceOracle baseOracle = _checkTokenOracleRegistration(base);\n\n        uint256 quoteDecimals = IERC20Metadata(quote).decimals();\n        if (base == quote) {\n            return 1 * 10 ** quoteDecimals;\n        }\n\n        // No need to go through the extra math if we're asking for it in terms we already have\n        uint256 baseInEth = baseOracle.getPriceInEth(base);\n        if (quote == _weth) {\n            return baseInEth;\n        }\n\n        IPriceOracle quoteOracle = _checkTokenOracleRegistration(quote);\n\n        return (baseInEth * (10 ** quoteDecimals)) / quoteOracle.getPriceInEth(quote);\n    }\n\n    function _checkTokenOracleRegistration(address token) private view returns (IPriceOracle oracle) {\n        oracle = tokenMappings[token];\n        if (address(oracle) == address(0)) {\n            revert MissingTokenOracle(token);\n        }\n    }\n\n    function _checkSpotOracleRegistration(address pool) private view returns (ISpotPriceOracle spotOracle) {\n        spotOracle = poolMappings[pool];\n        if (address(spotOracle) == address(0)) {\n            revert MissingSpotPriceOracle(pool);\n        }\n    }\n\n    /**\n     * @notice Get the floor price of a token in quote token denomination.\n     * @param lpToken Address of the token.\n     * @param pool Address of the pool.\n     * @param quote Address of the quote token.\n     * @return price Price of token in quote token.\n     */\n    function getFloorPrice(address lpToken, address pool, address quote) external returns (uint256 price) {\n        price = getFloorCeilingPrice(pool, lpToken, quote, false);\n    }\n\n    /**\n     * @notice Get the ceiling price of a token in quote token denomination.\n     * @param lpToken Address of the token.\n     * @param pool Address of the pool.\n     * @param quote Address of the quote token.\n     * @return price Price of token in quote token.\n     */\n    function getCeilingPrice(address lpToken, address pool, address quote) external returns (uint256 price) {\n        price = getFloorCeilingPrice(pool, lpToken, quote, true);\n    }\n}\n","deployed_bytecode":"0x608060405234801561001057600080fd5b50600436106101215760003560e01c80637abf085e116100ad578063c9eda0b411610071578063c9eda0b4146102c7578063e3117807146102da578063e3ad3282146102ed578063eeaad0bf14610300578063f12baf5c1461031357600080fd5b80637abf085e1461025457806391da0c79146102675780639972420c1461027a578063bc43cbaf1461028d578063c712bf77146102b457600080fd5b8063508d0a5f116100f4578063508d0a5f146101d25780635445b47c146101fb578063554023301461020e5780635a6804f5146102215780636f119bea1461023457600080fd5b80630a13bf761461012657806322e4ae481461015b5780632b9c42941461017057806334ff2eeb146101b1575b600080fd5b610139610134366004611ecc565b610339565b6040805193845260208401929092521515908201526060015b60405180910390f35b61016e610169366004611f17565b6106ab565b005b61019961017e366004611f50565b6001602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610152565b6101c46101bf366004611f50565b61085b565b604051908152602001610152565b6101996101e0366004611f50565b6000602081905290815260409020546001600160a01b031681565b6101c4610209366004611f17565b6108de565b61016e61021c366004611f6d565b610ae9565b61016e61022f366004611f50565b610c90565b6101c4610242366004611f50565b60026020526000908152604090205481565b61016e610262366004611ecc565b610e12565b6101c4610275366004611fa7565b61106a565b61016e610288366004611ecc565b6110a4565b6101997f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df681565b6101c46102c2366004611ecc565b6112d0565b61016e6102d5366004611f17565b6112e7565b61016e6102e8366004611f50565b6114a2565b6101c46102fb366004611f17565b611617565b6101c461030e366004611ecc565b61170e565b7f0000000000000000000000002218f90a98b0c070676f249ef44834686daa4285610199565b6000806000806103488661174a565b9050600080826001600160a01b031663da83972f898b8a6040518463ffffffff1660e01b815260040161037d93929190612003565b6000604051808303816000875af115801561039c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103c49190810190612096565b80519193509150600081900361040d57604051634389d5ab60e01b8152602060048201526008602482015267726573657276657360c01b60448201526064015b60405180910390fd5b82600003610429576000806000965096509650505050506106a2565b6001945060005b818110156105fc57600083828151811061044c5761044c612196565b60209081029190910181015180516001600160a01b0316600090815260029092526040822054909250908190036104a457815160405163ae395bf360e01b81526001600160a01b039091166004820152602401610404565b60006104b483600001518d6108de565b905060006104cb8d85606001518660400151611793565b90506000808284116104de5782846104e1565b83835b909250905060006104f282846121c2565b90508215806105165750858361050a612710846121d5565b61051491906121ec565b115b156105205760009c505b505050600084600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610567573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061058b919061220e565b61059690600a612315565b9050808386602001516105a991906121d5565b6105b391906121ec565b6105bd908d612324565b9b50808286602001516105d091906121d5565b6105da91906121ec565b6105e4908e612324565b9c505050505050806105f590612337565b9050610430565b5060008a6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610661919061220e565b61066c90600a612315565b90508361067982896121d5565b61068391906121ec565b965083610690828a6121d5565b61069a91906121ec565b975050505050505b93509350939050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa158015610726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074a9190612350565b61076757604051634ca8886760e01b815260040160405180910390fd5b61078d83604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6107b582604051806040016040528060068152602001656f7261636c6560d01b81525061171d565b6107bf3083611847565b6001600160a01b038381166000908152600160205260409020541615610803576040516345ed80e960e01b81526001600160a01b0384166004820152602401610404565b6001600160a01b0383811660008181526001602052604080822080546001600160a01b0319169487169485179055517f26f4b31b7240e7422a9fe2ba5ce7684500302a536166d0ed481d7ad653ff25ab9190a3505050565b6000806108678361193d565b6040516334ff2eeb60e01b81526001600160a01b038581166004830152919250908216906334ff2eeb906024016020604051808303816000875af11580156108b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d7919061236d565b9392505050565b6000806108ea8461193d565b90506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561092c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610950919061220e565b60ff169050836001600160a01b0316856001600160a01b03160361098d5761097981600a612386565b6109849060016121d5565b92505050610ae3565b6040516334ff2eeb60e01b81526001600160a01b038681166004830152600091908416906334ff2eeb906024016020604051808303816000875af11580156109d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fd919061236d565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316856001600160a01b031603610a42579250610ae3915050565b6000610a4d8661193d565b6040516334ff2eeb60e01b81526001600160a01b038881166004830152919250908216906334ff2eeb906024016020604051808303816000875af1158015610a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610abd919061236d565b610ac884600a612386565b610ad290846121d5565b610adc91906121ec565b9450505050505b92915050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190612350565b610ba557604051634ca8886760e01b815260040160405180910390fd5b610bcc83604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b610bf782604051806040016040528060098152602001681d1a1c995cda1bdb1960ba1b815250611981565b612710821115610c3657604051634389d5ab60e01b81526020600482015260096024820152681d1a1c995cda1bdb1960ba1b6044820152606401610404565b6001600160a01b038316600081815260026020908152604091829020859055815192835282018490527fa01bef2ca473420bb5411a3dc54119f5c7b6edacf16ad966c7c038fb605b207691015b60405180910390a1505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa158015610d0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2f9190612350565b610d4c57604051634ca8886760e01b815260040160405180910390fd5b610d7382604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b6001600160a01b0382811660009081526020819052604090205416610db65760405163e285791d60e01b81526001600160a01b0383166004820152602401610404565b6001600160a01b0382166000818152602081815260409182902080546001600160a01b031916905590519182527f4c910b69fe65a61f7531b9c5042b2329ca7179c77290aa7e2eb3afa3c8511fd3910160405180910390a15050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa158015610e8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb19190612350565b610ece57604051634ca8886760e01b815260040160405180910390fd5b610ef584604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b610f2083604051806040016040528060098152602001686f6c644f7261636c6560b81b81525061171d565b610f4b82604051806040016040528060098152602001686e65774f7261636c6560b81b81525061171d565b610f553083611847565b6001600160a01b03848116600090815260208190526040902054811690841614610fb4576001600160a01b038085166000908152602081905260409081902054905163302edb6f60e01b81526104049287928792911690600401612003565b816001600160a01b0316836001600160a01b031603610ff957604051635e913e3b60e11b81526001600160a01b03808616600483015283166024820152604401610404565b6001600160a01b038481166000908152602081905260409081902080546001600160a01b03191692851692909217909155517f01aeadc1af0d18c6bf4844c75f50827ed36067b889afd01a77f64c0fbbc96e2f9061105c90869086908690612003565b60405180910390a150505050565b60008060008061107c888888886119a4565b919450925090508061108e83856121d5565b61109891906121ec565b98975050505050505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa15801561111f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111439190612350565b61116057604051634ca8886760e01b815260040160405180910390fd5b61118684604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6111b183604051806040016040528060098152602001686f6c644f7261636c6560b81b81525061171d565b6111dc82604051806040016040528060098152602001686e65774f7261636c6560b81b81525061171d565b6111e63083611847565b6001600160a01b038085166000908152600160205260409020548116908416811461122a5784848260405163302edb6f60e01b815260040161040493929190612003565b826001600160a01b0316846001600160a01b03160361126f57604051635e913e3b60e11b81526001600160a01b03808716600483015284166024820152604401610404565b6001600160a01b0385811660008181526001602052604080822080546001600160a01b03191688861690811790915590519093881692917fb0a6ae8fb92ba64c3307a1353416ff4a67e1c903954632fb927d4df3a0cb724791a45050505050565b60006112df838584600161106a565b949350505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa158015611362573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113869190612350565b6113a357604051634ca8886760e01b815260040160405180910390fd5b6113ca83604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b6113f282604051806040016040528060068152602001656f7261636c6560d01b81525061171d565b6113fc3083611847565b6001600160a01b038381166000908152602081905260409020541615611440576040516345ed80e960e01b81526001600160a01b0384166004820152602401610404565b6001600160a01b038381166000818152602081815260409182902080546001600160a01b031916948716948517905581519283528201929092527f487c37289624c10056468f1f98ebffbad01edce11374975179672e32e2543bf09101610c83565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000037767cbff88cb623e9404e959560984f7d742df66001600160a01b0316906391d1485490604401602060405180830381865afa15801561151d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115419190612350565b61155e57604051634ca8886760e01b815260040160405180910390fd5b61158482604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6001600160a01b03828116600090815260016020526040902054166115c75760405163e285791d60e01b81526001600160a01b0383166004820152602401610404565b6001600160a01b03821660008181526001602052604080822080546001600160a01b0319169055517f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9190a25050565b600061164083604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b61166682604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b60006116718361174a565b905060007f0000000000000000000000002218f90a98b0c070676f249ef44834686daa42856001600160a01b0316633fc8cef36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f79190612392565b905061170582868684611c64565b95945050505050565b60006112df838584600061106a565b6001600160a01b038216611746578060405163eac0d38960e01b815260040161040491906123d3565b5050565b6001600160a01b03808216600090815260016020526040902054168061178e576040516305d5363760e51b81526001600160a01b0383166004820152602401610404565b919050565b6000836001600160a01b0316836001600160a01b0316036117b55750806108d7565b6000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611819919061220e565b60ff16905061182981600a612386565b61183385876108de565b61183d90856121d5565b61170591906121ec565b6040805160048152602481019091526020810180516001600160e01b0316633c4aebd760e21b179052600090611887906001600160a01b03851690611cf5565b80602001905181019061189a9190612392565b6040805160048152602481019091526020810180516001600160e01b0316633c4aebd760e21b1790529091506000906118dd906001600160a01b03851690611cf5565b8060200190518101906118f09190612392565b9050806001600160a01b0316826001600160a01b03161461193757604051632f6b3b6360e01b81526001600160a01b03808616600483015284166024820152604401610404565b50505050565b6001600160a01b03808216600090815260208190526040902054168061178e576040516317f0ddeb60e31b81526001600160a01b0383166004820152602401610404565b816000036117465780604051634389d5ab60e01b815260040161040491906123d3565b6000806000606060006119b68961174a565b60405163da83972f60e01b81529091506001600160a01b0382169063da83972f906119e9908c908c908c90600401612003565b6000604051808303816000875af1158015611a08573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a309190810190612096565b80935081945050506000876001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9c919061220e565b60ff1690506000896001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ae1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b05919061220e565b60ff169050611b15858284611d1a565b845190955060005b81811015611c54576000868281518110611b3957611b39612196565b602002602001015190506000611b5382600001518e6108de565b90506000611b6a8e84606001518560400151611793565b9050600083600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd4919061220e565b60ff169050611be88460200151828a611d1a565b611bf2908e612324565b9c508d15611c1457611c0d8c611c088486611d79565b611d79565b9b50611c3f565b8415611c3257611c2d8c611c288486611d8f565b611d8f565b611c3c565b611c3c8284611d8f565b9b505b5050505080611c4d90612337565b9050611b1d565b5050505050509450945094915050565b6000806000866001600160a01b03166375075ad58787876040518463ffffffff1660e01b8152600401611c9993929190612003565b60408051808303816000875af1158015611cb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cdb9190612406565b91509150611cea848284611793565b979650505050505050565b60606108d7838360405180606001604052806025815260200161244860259139611d9e565b600081831115611d4a57611d2e82846121c2565b611d3990600a612386565b611d4390856121ec565b90506108d7565b81831015611d7157611d5c83836121c2565b611d6790600a612386565b611d4390856121d5565b509192915050565b6000818311611d8857816108d7565b5090919050565b6000818310611d8857816108d7565b6060600080856001600160a01b031685604051611dbb919061242b565b600060405180830381855afa9150503d8060008114611df6576040519150601f19603f3d011682016040523d82523d6000602084013e611dfb565b606091505b5091509150611e0c86838387611e16565b9695505050505050565b60608315611e85578251600003611e7e576001600160a01b0385163b611e7e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610404565b50816112df565b6112df8383815115611e9a5781518083602001fd5b8060405162461bcd60e51b815260040161040491906123d3565b6001600160a01b0381168114611ec957600080fd5b50565b600080600060608486031215611ee157600080fd5b8335611eec81611eb4565b92506020840135611efc81611eb4565b91506040840135611f0c81611eb4565b809150509250925092565b60008060408385031215611f2a57600080fd5b8235611f3581611eb4565b91506020830135611f4581611eb4565b809150509250929050565b600060208284031215611f6257600080fd5b81356108d781611eb4565b60008060408385031215611f8057600080fd5b8235611f8b81611eb4565b946020939093013593505050565b8015158114611ec957600080fd5b60008060008060808587031215611fbd57600080fd5b8435611fc881611eb4565b93506020850135611fd881611eb4565b92506040850135611fe881611eb4565b91506060850135611ff881611f99565b939692955090935050565b6001600160a01b0393841681529183166020830152909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561205f5761205f612026565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561208e5761208e612026565b604052919050565b60008060408084860312156120aa57600080fd5b8351925060208085015167ffffffffffffffff808211156120ca57600080fd5b818701915087601f8301126120de57600080fd5b8151818111156120f0576120f0612026565b6120fe848260051b01612065565b818152848101925060079190911b83018401908982111561211e57600080fd5b928401925b81841015612186576080848b03121561213c5760008081fd5b61214461203c565b845161214f81611eb4565b81528486015186820152868501518782015260608086015161217081611eb4565b9082015283526080939093019291840191612123565b8096505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610ae357610ae36121ac565b8082028115828204841417610ae357610ae36121ac565b60008261220957634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561222057600080fd5b815160ff811681146108d757600080fd5b600181815b8085111561226c578160001904821115612252576122526121ac565b8085161561225f57918102915b93841c9390800290612236565b509250929050565b60008261228357506001610ae3565b8161229057506000610ae3565b81600181146122a657600281146122b0576122cc565b6001915050610ae3565b60ff8411156122c1576122c16121ac565b50506001821b610ae3565b5060208310610133831016604e8410600b84101617156122ef575081810a610ae3565b6122f98383612231565b806000190482111561230d5761230d6121ac565b029392505050565b60006108d760ff841683612274565b80820180821115610ae357610ae36121ac565b600060018201612349576123496121ac565b5060010190565b60006020828403121561236257600080fd5b81516108d781611f99565b60006020828403121561237f57600080fd5b5051919050565b60006108d78383612274565b6000602082840312156123a457600080fd5b81516108d781611eb4565b60005b838110156123ca5781810151838201526020016123b2565b50506000910152565b60208152600082518060208401526123f28160408501602087016123af565b601f01601f19169190910160400192915050565b6000806040838503121561241957600080fd5b825191506020830151611f4581611eb4565b6000825161243d8184602087016123af565b919091019291505056fe416464726573733a206c6f772d6c6576656c207374617469632063616c6c206661696c6564ced6982f480260bdd8ad5cb18ff2854f0306d78d904ad6cc107e8f3a0f526c18a2646970667358221220767bfd4f10da92d9c191e3df26780c4ea9ac801f759826bf6e4bb7699362f3a464736f6c63430008110033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"london","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":true,"runs":200},"remappings":[":ERC4626/=lib/properties/lib/ERC4626/contracts/",":crytic/properties/=lib/properties/",":ds-test/=lib/forge-std/lib/ds-test/src/",":erc4626-tests/=lib/erc4626-tests/",":forge-std/=lib/forge-std/src/",":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",":prb-math/=lib/prb-math/",":properties/=lib/properties/contracts/",":solmate/=lib/properties/lib/solmate/src/",":src/=src/",":test/=test/",":usingtellor/=lib/usingtellor/contracts/"]},"optimization_runs":200,"sourcify_repo_url":"https://repo.sourcify.dev/contracts/full_match/1/0x61F8BE7FD721e80C0249829eaE6f0DAf21bc2CaC/","decoded_constructor_args":[["0x2218F90A98b0C070676f249EF44834686dAa4285",{"internalType":"contract ISystemRegistry","name":"_systemRegistry","type":"address"}]],"compiler_version":"0.8.17+commit.8df45f5f","is_verified_via_verifier_alliance":false,"verified_at":"2024-08-30T14:21:02.975343Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x60e06040523480156200001157600080fd5b506040516200278c3803806200278c8339810160408190526200003491620001eb565b806001600160a01b031663bc43cbaf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000073573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000999190620001eb565b81620000d8816040518060400160405280600f81526020016e5f73797374656d526567697374727960881b8152506200019960201b6200171d1760201c565b6001600160a01b0390811660805281166200010657604051630cbe126f60e11b815260040160405180910390fd5b806001600160a01b031660a0816001600160a01b03168152505050806001600160a01b0316633fc8cef36040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000160573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001869190620001eb565b6001600160a01b031660c0525062000262565b6001600160a01b038216620001ce578060405163eac0d38960e01b8152600401620001c5919062000212565b60405180910390fd5b5050565b6001600160a01b0381168114620001e857600080fd5b50565b600060208284031215620001fe57600080fd5b81516200020b81620001d2565b9392505050565b600060208083528351808285015260005b81811015620002415785810183015185820160400152820162000223565b506000604082860101526040601f19601f8301168501019250505092915050565b60805160a05160c0516124c2620002ca6000396000610a01015260008181610292015281816106d701528181610b1501528181610cbc01528181610e3e015281816110d00152818161131301526114ce015260008181610315015261167701526124c26000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c80637abf085e116100ad578063c9eda0b411610071578063c9eda0b4146102c7578063e3117807146102da578063e3ad3282146102ed578063eeaad0bf14610300578063f12baf5c1461031357600080fd5b80637abf085e1461025457806391da0c79146102675780639972420c1461027a578063bc43cbaf1461028d578063c712bf77146102b457600080fd5b8063508d0a5f116100f4578063508d0a5f146101d25780635445b47c146101fb578063554023301461020e5780635a6804f5146102215780636f119bea1461023457600080fd5b80630a13bf761461012657806322e4ae481461015b5780632b9c42941461017057806334ff2eeb146101b1575b600080fd5b610139610134366004611ecc565b610339565b6040805193845260208401929092521515908201526060015b60405180910390f35b61016e610169366004611f17565b6106ab565b005b61019961017e366004611f50565b6001602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610152565b6101c46101bf366004611f50565b61085b565b604051908152602001610152565b6101996101e0366004611f50565b6000602081905290815260409020546001600160a01b031681565b6101c4610209366004611f17565b6108de565b61016e61021c366004611f6d565b610ae9565b61016e61022f366004611f50565b610c90565b6101c4610242366004611f50565b60026020526000908152604090205481565b61016e610262366004611ecc565b610e12565b6101c4610275366004611fa7565b61106a565b61016e610288366004611ecc565b6110a4565b6101997f000000000000000000000000000000000000000000000000000000000000000081565b6101c46102c2366004611ecc565b6112d0565b61016e6102d5366004611f17565b6112e7565b61016e6102e8366004611f50565b6114a2565b6101c46102fb366004611f17565b611617565b6101c461030e366004611ecc565b61170e565b7f0000000000000000000000000000000000000000000000000000000000000000610199565b6000806000806103488661174a565b9050600080826001600160a01b031663da83972f898b8a6040518463ffffffff1660e01b815260040161037d93929190612003565b6000604051808303816000875af115801561039c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103c49190810190612096565b80519193509150600081900361040d57604051634389d5ab60e01b8152602060048201526008602482015267726573657276657360c01b60448201526064015b60405180910390fd5b82600003610429576000806000965096509650505050506106a2565b6001945060005b818110156105fc57600083828151811061044c5761044c612196565b60209081029190910181015180516001600160a01b0316600090815260029092526040822054909250908190036104a457815160405163ae395bf360e01b81526001600160a01b039091166004820152602401610404565b60006104b483600001518d6108de565b905060006104cb8d85606001518660400151611793565b90506000808284116104de5782846104e1565b83835b909250905060006104f282846121c2565b90508215806105165750858361050a612710846121d5565b61051491906121ec565b115b156105205760009c505b505050600084600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610567573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061058b919061220e565b61059690600a612315565b9050808386602001516105a991906121d5565b6105b391906121ec565b6105bd908d612324565b9b50808286602001516105d091906121d5565b6105da91906121ec565b6105e4908e612324565b9c505050505050806105f590612337565b9050610430565b5060008a6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561063d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610661919061220e565b61066c90600a612315565b90508361067982896121d5565b61068391906121ec565b965083610690828a6121d5565b61069a91906121ec565b975050505050505b93509350939050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015610726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074a9190612350565b61076757604051634ca8886760e01b815260040160405180910390fd5b61078d83604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6107b582604051806040016040528060068152602001656f7261636c6560d01b81525061171d565b6107bf3083611847565b6001600160a01b038381166000908152600160205260409020541615610803576040516345ed80e960e01b81526001600160a01b0384166004820152602401610404565b6001600160a01b0383811660008181526001602052604080822080546001600160a01b0319169487169485179055517f26f4b31b7240e7422a9fe2ba5ce7684500302a536166d0ed481d7ad653ff25ab9190a3505050565b6000806108678361193d565b6040516334ff2eeb60e01b81526001600160a01b038581166004830152919250908216906334ff2eeb906024016020604051808303816000875af11580156108b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d7919061236d565b9392505050565b6000806108ea8461193d565b90506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561092c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610950919061220e565b60ff169050836001600160a01b0316856001600160a01b03160361098d5761097981600a612386565b6109849060016121d5565b92505050610ae3565b6040516334ff2eeb60e01b81526001600160a01b038681166004830152600091908416906334ff2eeb906024016020604051808303816000875af11580156109d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fd919061236d565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031603610a42579250610ae3915050565b6000610a4d8661193d565b6040516334ff2eeb60e01b81526001600160a01b038881166004830152919250908216906334ff2eeb906024016020604051808303816000875af1158015610a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610abd919061236d565b610ac884600a612386565b610ad290846121d5565b610adc91906121ec565b9450505050505b92915050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190612350565b610ba557604051634ca8886760e01b815260040160405180910390fd5b610bcc83604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b610bf782604051806040016040528060098152602001681d1a1c995cda1bdb1960ba1b815250611981565b612710821115610c3657604051634389d5ab60e01b81526020600482015260096024820152681d1a1c995cda1bdb1960ba1b6044820152606401610404565b6001600160a01b038316600081815260026020908152604091829020859055815192835282018490527fa01bef2ca473420bb5411a3dc54119f5c7b6edacf16ad966c7c038fb605b207691015b60405180910390a1505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015610d0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2f9190612350565b610d4c57604051634ca8886760e01b815260040160405180910390fd5b610d7382604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b6001600160a01b0382811660009081526020819052604090205416610db65760405163e285791d60e01b81526001600160a01b0383166004820152602401610404565b6001600160a01b0382166000818152602081815260409182902080546001600160a01b031916905590519182527f4c910b69fe65a61f7531b9c5042b2329ca7179c77290aa7e2eb3afa3c8511fd3910160405180910390a15050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015610e8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb19190612350565b610ece57604051634ca8886760e01b815260040160405180910390fd5b610ef584604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b610f2083604051806040016040528060098152602001686f6c644f7261636c6560b81b81525061171d565b610f4b82604051806040016040528060098152602001686e65774f7261636c6560b81b81525061171d565b610f553083611847565b6001600160a01b03848116600090815260208190526040902054811690841614610fb4576001600160a01b038085166000908152602081905260409081902054905163302edb6f60e01b81526104049287928792911690600401612003565b816001600160a01b0316836001600160a01b031603610ff957604051635e913e3b60e11b81526001600160a01b03808616600483015283166024820152604401610404565b6001600160a01b038481166000908152602081905260409081902080546001600160a01b03191692851692909217909155517f01aeadc1af0d18c6bf4844c75f50827ed36067b889afd01a77f64c0fbbc96e2f9061105c90869086908690612003565b60405180910390a150505050565b60008060008061107c888888886119a4565b919450925090508061108e83856121d5565b61109891906121ec565b98975050505050505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa15801561111f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111439190612350565b61116057604051634ca8886760e01b815260040160405180910390fd5b61118684604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6111b183604051806040016040528060098152602001686f6c644f7261636c6560b81b81525061171d565b6111dc82604051806040016040528060098152602001686e65774f7261636c6560b81b81525061171d565b6111e63083611847565b6001600160a01b038085166000908152600160205260409020548116908416811461122a5784848260405163302edb6f60e01b815260040161040493929190612003565b826001600160a01b0316846001600160a01b03160361126f57604051635e913e3b60e11b81526001600160a01b03808716600483015284166024820152604401610404565b6001600160a01b0385811660008181526001602052604080822080546001600160a01b03191688861690811790915590519093881692917fb0a6ae8fb92ba64c3307a1353416ff4a67e1c903954632fb927d4df3a0cb724791a45050505050565b60006112df838584600161106a565b949350505050565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa158015611362573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113869190612350565b6113a357604051634ca8886760e01b815260040160405180910390fd5b6113ca83604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b6113f282604051806040016040528060068152602001656f7261636c6560d01b81525061171d565b6113fc3083611847565b6001600160a01b038381166000908152602081905260409020541615611440576040516345ed80e960e01b81526001600160a01b0384166004820152602401610404565b6001600160a01b038381166000818152602081815260409182902080546001600160a01b031916948716948517905581519283528201929092527f487c37289624c10056468f1f98ebffbad01edce11374975179672e32e2543bf09101610c83565b604051632474521560e21b815260008051602061246d83398151915260048201819052336024830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa15801561151d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115419190612350565b61155e57604051634ca8886760e01b815260040160405180910390fd5b61158482604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b6001600160a01b03828116600090815260016020526040902054166115c75760405163e285791d60e01b81526001600160a01b0383166004820152602401610404565b6001600160a01b03821660008181526001602052604080822080546001600160a01b0319169055517f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9190a25050565b600061164083604051806040016040528060058152602001643a37b5b2b760d91b81525061171d565b61166682604051806040016040528060048152602001631c1bdbdb60e21b81525061171d565b60006116718361174a565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633fc8cef36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f79190612392565b905061170582868684611c64565b95945050505050565b60006112df838584600061106a565b6001600160a01b038216611746578060405163eac0d38960e01b815260040161040491906123d3565b5050565b6001600160a01b03808216600090815260016020526040902054168061178e576040516305d5363760e51b81526001600160a01b0383166004820152602401610404565b919050565b6000836001600160a01b0316836001600160a01b0316036117b55750806108d7565b6000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611819919061220e565b60ff16905061182981600a612386565b61183385876108de565b61183d90856121d5565b61170591906121ec565b6040805160048152602481019091526020810180516001600160e01b0316633c4aebd760e21b179052600090611887906001600160a01b03851690611cf5565b80602001905181019061189a9190612392565b6040805160048152602481019091526020810180516001600160e01b0316633c4aebd760e21b1790529091506000906118dd906001600160a01b03851690611cf5565b8060200190518101906118f09190612392565b9050806001600160a01b0316826001600160a01b03161461193757604051632f6b3b6360e01b81526001600160a01b03808616600483015284166024820152604401610404565b50505050565b6001600160a01b03808216600090815260208190526040902054168061178e576040516317f0ddeb60e31b81526001600160a01b0383166004820152602401610404565b816000036117465780604051634389d5ab60e01b815260040161040491906123d3565b6000806000606060006119b68961174a565b60405163da83972f60e01b81529091506001600160a01b0382169063da83972f906119e9908c908c908c90600401612003565b6000604051808303816000875af1158015611a08573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a309190810190612096565b80935081945050506000876001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9c919061220e565b60ff1690506000896001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ae1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b05919061220e565b60ff169050611b15858284611d1a565b845190955060005b81811015611c54576000868281518110611b3957611b39612196565b602002602001015190506000611b5382600001518e6108de565b90506000611b6a8e84606001518560400151611793565b9050600083600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd4919061220e565b60ff169050611be88460200151828a611d1a565b611bf2908e612324565b9c508d15611c1457611c0d8c611c088486611d79565b611d79565b9b50611c3f565b8415611c3257611c2d8c611c288486611d8f565b611d8f565b611c3c565b611c3c8284611d8f565b9b505b5050505080611c4d90612337565b9050611b1d565b5050505050509450945094915050565b6000806000866001600160a01b03166375075ad58787876040518463ffffffff1660e01b8152600401611c9993929190612003565b60408051808303816000875af1158015611cb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cdb9190612406565b91509150611cea848284611793565b979650505050505050565b60606108d7838360405180606001604052806025815260200161244860259139611d9e565b600081831115611d4a57611d2e82846121c2565b611d3990600a612386565b611d4390856121ec565b90506108d7565b81831015611d7157611d5c83836121c2565b611d6790600a612386565b611d4390856121d5565b509192915050565b6000818311611d8857816108d7565b5090919050565b6000818310611d8857816108d7565b6060600080856001600160a01b031685604051611dbb919061242b565b600060405180830381855afa9150503d8060008114611df6576040519150601f19603f3d011682016040523d82523d6000602084013e611dfb565b606091505b5091509150611e0c86838387611e16565b9695505050505050565b60608315611e85578251600003611e7e576001600160a01b0385163b611e7e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610404565b50816112df565b6112df8383815115611e9a5781518083602001fd5b8060405162461bcd60e51b815260040161040491906123d3565b6001600160a01b0381168114611ec957600080fd5b50565b600080600060608486031215611ee157600080fd5b8335611eec81611eb4565b92506020840135611efc81611eb4565b91506040840135611f0c81611eb4565b809150509250925092565b60008060408385031215611f2a57600080fd5b8235611f3581611eb4565b91506020830135611f4581611eb4565b809150509250929050565b600060208284031215611f6257600080fd5b81356108d781611eb4565b60008060408385031215611f8057600080fd5b8235611f8b81611eb4565b946020939093013593505050565b8015158114611ec957600080fd5b60008060008060808587031215611fbd57600080fd5b8435611fc881611eb4565b93506020850135611fd881611eb4565b92506040850135611fe881611eb4565b91506060850135611ff881611f99565b939692955090935050565b6001600160a01b0393841681529183166020830152909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561205f5761205f612026565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561208e5761208e612026565b604052919050565b60008060408084860312156120aa57600080fd5b8351925060208085015167ffffffffffffffff808211156120ca57600080fd5b818701915087601f8301126120de57600080fd5b8151818111156120f0576120f0612026565b6120fe848260051b01612065565b818152848101925060079190911b83018401908982111561211e57600080fd5b928401925b81841015612186576080848b03121561213c5760008081fd5b61214461203c565b845161214f81611eb4565b81528486015186820152868501518782015260608086015161217081611eb4565b9082015283526080939093019291840191612123565b8096505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610ae357610ae36121ac565b8082028115828204841417610ae357610ae36121ac565b60008261220957634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561222057600080fd5b815160ff811681146108d757600080fd5b600181815b8085111561226c578160001904821115612252576122526121ac565b8085161561225f57918102915b93841c9390800290612236565b509250929050565b60008261228357506001610ae3565b8161229057506000610ae3565b81600181146122a657600281146122b0576122cc565b6001915050610ae3565b60ff8411156122c1576122c16121ac565b50506001821b610ae3565b5060208310610133831016604e8410600b84101617156122ef575081810a610ae3565b6122f98383612231565b806000190482111561230d5761230d6121ac565b029392505050565b60006108d760ff841683612274565b80820180821115610ae357610ae36121ac565b600060018201612349576123496121ac565b5060010190565b60006020828403121561236257600080fd5b81516108d781611f99565b60006020828403121561237f57600080fd5b5051919050565b60006108d78383612274565b6000602082840312156123a457600080fd5b81516108d781611eb4565b60005b838110156123ca5781810151838201526020016123b2565b50506000910152565b60208152600082518060208401526123f28160408501602087016123af565b601f01601f19169190910160400192915050565b6000806040838503121561241957600080fd5b825191506020830151611f4581611eb4565b6000825161243d8184602087016123af565b919091019291505056fe416464726573733a206c6f772d6c6576656c207374617469632063616c6c206661696c6564ced6982f480260bdd8ad5cb18ff2854f0306d78d904ad6cc107e8f3a0f526c18a2646970667358221220767bfd4f10da92d9c191e3df26780c4ea9ac801f759826bf6e4bb7699362f3a464736f6c634300081100330000000000000000000000002218f90a98b0c070676f249ef44834686daa4285","name":"RootPriceOracle","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"london","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":true,"additional_sources":[{"file_path":"src/interfaces/security/IAccessController.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IAccessControlEnumerable } from \"openzeppelin-contracts/access/IAccessControlEnumerable.sol\";\n\ninterface IAccessController is IAccessControlEnumerable {\n    error AccessDenied();\n\n    /**\n     * @notice Setup a role for an account\n     * @param role The role to setup\n     * @param account The account to setup the role for\n     */\n    function setupRole(bytes32 role, address account) external;\n\n    /**\n     * @notice Verify if an account is an owner. Reverts if not\n     * @param account The account to verify\n     */\n    function verifyOwner(address account) external view;\n}\n"},{"file_path":"src/SystemComponent.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\n\ncontract SystemComponent is ISystemComponent {\n    ISystemRegistry internal immutable systemRegistry;\n\n    constructor(ISystemRegistry _systemRegistry) {\n        Errors.verifyNotZero(address(_systemRegistry), \"_systemRegistry\");\n        systemRegistry = _systemRegistry;\n    }\n\n    /// @inheritdoc ISystemComponent\n    function getSystemRegistry() external view returns (address) {\n        return address(systemRegistry);\n    }\n}\n"},{"file_path":"src/interfaces/oracles/IPriceOracle.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\n/// @notice An oracle that can provide prices for single or multiple classes of tokens\ninterface IPriceOracle {\n    /// @notice Returns a fair price for the provided token in ETH\n    /// @dev May require additional registration with the provider before being used for a token\n    /// @param token Token to get the price of\n    /// @return price The price of the token in ETH\n    function getPriceInEth(address token) external returns (uint256 price);\n\n    /// @notice Retrieve the description of the oracle\n    /// @return description\n    function getDescription() external view returns (string memory);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/math/Math.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\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                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. 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            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator,\n        Rounding rounding\n    ) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10**64) {\n                value /= 10**64;\n                result += 64;\n            }\n            if (value >= 10**32) {\n                value /= 10**32;\n                result += 32;\n            }\n            if (value >= 10**16) {\n                value /= 10**16;\n                result += 16;\n            }\n            if (value >= 10**8) {\n                value /= 10**8;\n                result += 8;\n            }\n            if (value >= 10**4) {\n                value /= 10**4;\n                result += 4;\n            }\n            if (value >= 10**2) {\n                value /= 10**2;\n                result += 2;\n            }\n            if (value >= 10**1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n        }\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\n\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"},{"file_path":"src/libs/Roles.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\nlibrary Roles {\n    // Naming Conventions:\n    // - Use MANAGER, CREATOR, UPDATER, ..., for roles primarily managing on-chain activities.\n    // - Use EXECUTOR for roles that trigger off-chain initiated actions.\n    // - Group roles by functional area for clarity.\n\n    // Destination Vault Management\n    bytes32 public constant DESTINATION_VAULT_FACTORY_MANAGER = keccak256(\"CREATE_DESTINATION_VAULT_ROLE\");\n    bytes32 public constant DESTINATION_VAULT_REGISTRY_MANAGER = keccak256(\"DESTINATION_VAULT_REGISTRY_MANAGER\");\n    bytes32 public constant DESTINATION_VAULT_MANAGER = keccak256(\"DESTINATION_VAULT_MANAGER\");\n\n    // Auto Pool Factory and Registry Management\n    bytes32 public constant AUTO_POOL_REGISTRY_UPDATER = keccak256(\"REGISTRY_UPDATER\");\n    bytes32 public constant AUTO_POOL_FACTORY_MANAGER = keccak256(\"AUTO_POOL_FACTORY_MANAGER\");\n    bytes32 public constant AUTO_POOL_FACTORY_VAULT_CREATOR = keccak256(\"CREATE_POOL_ROLE\");\n\n    // Auto Pool Management\n    bytes32 public constant AUTO_POOL_DESTINATION_UPDATER = keccak256(\"DESTINATION_VAULTS_UPDATER\");\n    bytes32 public constant AUTO_POOL_FEE_UPDATER = keccak256(\"AUTO_POOL_FEE_SETTER_ROLE\");\n    bytes32 public constant AUTO_POOL_PERIODIC_FEE_UPDATER = keccak256(\"AUTO_POOL_PERIODIC_FEE_SETTER_ROLE\");\n    bytes32 public constant AUTO_POOL_REWARD_MANAGER = keccak256(\"AUTO_POOL_REWARD_MANAGER_ROLE\");\n    bytes32 public constant AUTO_POOL_MANAGER = keccak256(\"AUTO_POOL_ADMIN\");\n    bytes32 public constant REBALANCER = keccak256(\"REBALANCER_ROLE\");\n    bytes32 public constant STATS_HOOK_POINTS_ADMIN = keccak256(\"STATS_HOOK_POINTS_ADMIN\");\n\n    // Reward Management\n    bytes32 public constant LIQUIDATOR_MANAGER = keccak256(\"LIQUIDATOR_ROLE\");\n    bytes32 public constant DV_REWARD_MANAGER = keccak256(\"DV_REWARD_MANAGER_ROLE\");\n    bytes32 public constant REWARD_LIQUIDATION_MANAGER = keccak256(\"REWARD_LIQUIDATION_MANAGER\");\n    bytes32 public constant EXTRA_REWARD_MANAGER = keccak256(\"EXTRA_REWARD_MANAGER_ROLE\");\n    bytes32 public constant REWARD_LIQUIDATION_EXECUTOR = keccak256(\"REWARD_LIQUIDATION_EXECUTOR\");\n\n    // Statistics and Reporting\n    bytes32 public constant STATS_CALC_REGISTRY_MANAGER = keccak256(\"STATS_CALC_REGISTRY_MANAGER\");\n    bytes32 public constant STATS_CALC_FACTORY_MANAGER = keccak256(\"CREATE_STATS_CALC_ROLE\");\n    bytes32 public constant STATS_CALC_FACTORY_TEMPLATE_MANAGER = keccak256(\"STATS_CALC_TEMPLATE_MGMT_ROLE\");\n\n    bytes32 public constant STATS_SNAPSHOT_EXECUTOR = keccak256(\"STATS_SNAPSHOT_ROLE\");\n    bytes32 public constant STATS_INCENTIVE_TOKEN_UPDATER = keccak256(\"STATS_INCENTIVE_TOKEN_UPDATER\");\n    bytes32 public constant STATS_GENERAL_MANAGER = keccak256(\"STATS_GENERAL_MANAGER\");\n    bytes32 public constant STATS_LST_ETH_TOKEN_EXECUTOR = keccak256(\"STATS_LST_ETH_TOKEN_EXECUTOR\");\n\n    // Emergency Management\n    bytes32 public constant EMERGENCY_PAUSER = keccak256(\"EMERGENCY_PAUSER\");\n    bytes32 public constant SEQUENCER_OVERRIDE_MANAGER = keccak256(\"SEQUENCER_OVERRIDE_MANAGER\");\n\n    // Miscellaneous Roles\n    bytes32 public constant SOLVER = keccak256(\"SOLVER_ROLE\");\n    bytes32 public constant AUTO_POOL_REPORTING_EXECUTOR = keccak256(\"AUTO_POOL_UPDATE_DEBT_REPORTING_ROLE\");\n\n    // Swapper Roles\n    bytes32 public constant SWAP_ROUTER_MANAGER = keccak256(\"SWAP_ROUTER_MANAGER\");\n\n    // Price Oracles Roles\n    bytes32 public constant ORACLE_MANAGER = keccak256(\"ORACLE_MANAGER_ROLE\");\n    bytes32 public constant CUSTOM_ORACLE_EXECUTOR = keccak256(\"CUSTOM_ORACLE_EXECUTOR\");\n    bytes32 public constant MAVERICK_FEE_ORACLE_EXECUTOR = keccak256(\"MAVERICK_FEE_ORACLE_MANAGER\");\n\n    // AccToke Roles\n    bytes32 public constant ACC_TOKE_MANAGER = keccak256(\"ACC_TOKE_MANAGER\");\n\n    // Admin Roles\n    bytes32 public constant TOKEN_RECOVERY_MANAGER = keccak256(\"TOKEN_RECOVERY_ROLE\");\n    bytes32 public constant INFRASTRUCTURE_MANAGER = keccak256(\"INFRASTRUCTURE_MANAGER\");\n\n    // Cross chain communications roles\n    bytes32 public constant MESSAGE_PROXY_MANAGER = keccak256(\"MESSAGE_PROXY_MANAGER\");\n    bytes32 public constant MESSAGE_PROXY_EXECUTOR = keccak256(\"MESSAGE_PROXY_EXECUTOR\");\n    bytes32 public constant RECEIVING_ROUTER_MANAGER = keccak256(\"RECEIVING_ROUTER_MANAGER\");\n    bytes32 public constant RECEIVING_ROUTER_EXECUTOR = keccak256(\"RECEIVING_ROUTER_EXECUTOR\");\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n    enum RecoverError {\n        NoError,\n        InvalidSignature,\n        InvalidSignatureLength,\n        InvalidSignatureS,\n        InvalidSignatureV // Deprecated in v4.8\n    }\n\n    function _throwError(RecoverError error) private pure {\n        if (error == RecoverError.NoError) {\n            return; // no error: do nothing\n        } else if (error == RecoverError.InvalidSignature) {\n            revert(\"ECDSA: invalid signature\");\n        } else if (error == RecoverError.InvalidSignatureLength) {\n            revert(\"ECDSA: invalid signature length\");\n        } else if (error == RecoverError.InvalidSignatureS) {\n            revert(\"ECDSA: invalid signature 's' value\");\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature` or error string. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {toEthSignedMessageHash} on it.\n     *\n     * Documentation for signature generation:\n     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n        if (signature.length == 65) {\n            bytes32 r;\n            bytes32 s;\n            uint8 v;\n            // ecrecover takes the signature parameters, and the only way to get them\n            // currently is to use assembly.\n            /// @solidity memory-safe-assembly\n            assembly {\n                r := mload(add(signature, 0x20))\n                s := mload(add(signature, 0x40))\n                v := byte(0, mload(add(signature, 0x60)))\n            }\n            return tryRecover(hash, v, r, s);\n        } else {\n            return (address(0), RecoverError.InvalidSignatureLength);\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature`. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {toEthSignedMessageHash} on it.\n     */\n    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, signature);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n     *\n     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(\n        bytes32 hash,\n        bytes32 r,\n        bytes32 vs\n    ) internal pure returns (address, RecoverError) {\n        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n        uint8 v = uint8((uint256(vs) >> 255) + 27);\n        return tryRecover(hash, v, r, s);\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n     *\n     * _Available since v4.2._\n     */\n    function recover(\n        bytes32 hash,\n        bytes32 r,\n        bytes32 vs\n    ) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address, RecoverError) {\n        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n        // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n        //\n        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n        // these malleable signatures as well.\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            return (address(0), RecoverError.InvalidSignatureS);\n        }\n\n        // If the signature is valid (and not malleable), return the signer address\n        address signer = ecrecover(hash, v, r, s);\n        if (signer == address(0)) {\n            return (address(0), RecoverError.InvalidSignature);\n        }\n\n        return (signer, RecoverError.NoError);\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function recover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n     * produces hash corresponding to the one signed with the\n     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n     * JSON-RPC method as part of EIP-191.\n     *\n     * See {recover}.\n     */\n    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n        // 32 is the length in bytes of hash,\n        // enforced by the type signature above\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Message, created from `s`. This\n     * produces hash corresponding to the one signed with the\n     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n     * JSON-RPC method as part of EIP-191.\n     *\n     * See {recover}.\n     */\n    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Typed Data, created from a\n     * `domainSeparator` and a `structHash`. This produces hash corresponding\n     * to the one signed with the\n     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n     * JSON-RPC method as part of EIP-712.\n     *\n     * See {recover}.\n     */\n    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n    }\n}\n"},{"file_path":"src/utils/Errors.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Address } from \"openzeppelin-contracts/utils/Address.sol\";\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\n// solhint-disable max-line-length\nlibrary Errors {\n    using Address for address;\n    ///////////////////////////////////////////////////////////////////\n    //                       Set errors\n    ///////////////////////////////////////////////////////////////////\n\n    error AccessDenied();\n    error ZeroAddress(string paramName);\n    error ZeroAmount();\n    error InsufficientBalance(address token);\n    error AssetNotAllowed(address token);\n    error NotImplemented();\n    error InvalidAddress(address addr);\n    error InvalidParam(string paramName);\n    error InvalidParams();\n    error UnsafePrice(address token, uint256 spotPrice, uint256 safePrice);\n    error AlreadySet(string param);\n    error AlreadyRegistered(address param);\n    error SlippageExceeded(uint256 expected, uint256 actual);\n    error ArrayLengthMismatch(uint256 length1, uint256 length2, string details);\n\n    error ItemNotFound();\n    error ItemExists();\n    error MissingRole(bytes32 role, address user);\n    error RegistryItemMissing(string item);\n    error NotRegistered();\n    // Used to check storage slot is empty before setting.\n    error MustBeZero();\n    // Used to check storage slot set before deleting.\n    error MustBeSet();\n\n    error ApprovalFailed(address token);\n    error FlashLoanFailed(address token, uint256 amount);\n\n    error SystemMismatch(address source1, address source2);\n\n    error InvalidToken(address token);\n    error UnreachableError();\n\n    error InvalidSigner(address signer);\n\n    error InvalidChainId(uint256 chainId);\n\n    error SenderMismatch(address recipient, address sender);\n\n    error UnsupportedMessage(bytes32 messageType, bytes message);\n\n    error NotSupported();\n\n    error InvalidConfiguration();\n\n    error InvalidDataReturned();\n\n    function verifyNotZero(address addr, string memory paramName) internal pure {\n        if (addr == address(0)) {\n            revert ZeroAddress(paramName);\n        }\n    }\n\n    function verifyNotZero(bytes32 key, string memory paramName) internal pure {\n        if (key == bytes32(0)) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotEmpty(string memory val, string memory paramName) internal pure {\n        if (bytes(val).length == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotZero(uint256 num, string memory paramName) internal pure {\n        if (num == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifySystemsMatch(address component1, address component2) internal view {\n        address registry1 =\n            abi.decode(component1.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));\n        address registry2 =\n            abi.decode(component2.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));\n\n        if (registry1 != registry2) {\n            revert SystemMismatch(component1, component2);\n        }\n    }\n\n    function verifyArrayLengths(uint256 length1, uint256 length2, string memory details) internal pure {\n        if (length1 != length2) {\n            revert ArrayLengthMismatch(length1, length2, details);\n        }\n    }\n}\n"},{"file_path":"src/interfaces/stats/IStatsCalculator.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Capture information about a pool or destination\ninterface IStatsCalculator {\n    /// @notice thrown when no snapshot is taken\n    error NoSnapshotTaken();\n\n    /// @notice The id for this instance of a calculator\n    function getAprId() external view returns (bytes32);\n\n    /// @notice The id of the underlying asset/pool/destination this calculator represents\n    /// @dev This may be a generated address\n    function getAddressId() external view returns (address);\n\n    /// @notice Setup the calculator after it has been copied\n    /// @dev Should only be executed one time\n    /// @param dependentAprIds apr ids that cover the dependencies of this calculator\n    /// @param initData setup data specific to this type of calculator\n    function initialize(bytes32[] calldata dependentAprIds, bytes calldata initData) external;\n\n    /// @notice Capture stat data about this setup\n    function snapshot() external;\n\n    /// @notice Indicates if a snapshot should be taken\n    /// @return takeSnapshot if true then a snapshot should be taken. If false, calling snapshot will do nothing\n    function shouldSnapshot() external view returns (bool takeSnapshot);\n\n    /// @dev Enum representing the snapshot status for a given rewarder (Convex and Aura) or reward token (Maverick)\n    enum SnapshotStatus {\n        noSnapshot, // Indicates that no snapshot has been taken yet for the rewarder.\n        tooSoon, // Indicates that it's too soon to take another snapshot since the last one.\n        shouldFinalize, // Indicates that the conditions are met for finalizing a snapshot.\n        shouldRestart // Indicates that the conditions are met for restarting a snapshot.\n\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"},{"file_path":"src/interfaces/ISystemComponent.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @notice Stores a reference to the registry for this system\ninterface ISystemComponent {\n    /// @notice The system instance this contract is tied to\n    function getSystemRegistry() external view returns (address registry);\n}\n"},{"file_path":"src/vault/libs/AutopoolDebt.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\nimport { IDestinationVault } from \"src/interfaces/vault/IDestinationVault.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\nimport { IAutopoolStrategy } from \"src/interfaces/strategy/IAutopoolStrategy.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\n\nlibrary AutopoolDebt {\n    using Math for uint256;\n    using SafeERC20 for IERC20;\n    using WithdrawalQueue for StructuredLinkedList.List;\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using AutopoolToken for AutopoolToken.TokenData;\n\n    /// @notice Max time a cached debt report can be used\n    uint256 public constant MAX_DEBT_REPORT_AGE_SECONDS = 1 days;\n\n    error VaultShutdown();\n    error WithdrawShareCalcInvalid(uint256 currentShares, uint256 cachedShares);\n    error RebalanceDestinationsMatch(address destinationVault);\n    error RebalanceFailed(string message);\n    error InvalidPrices();\n    error InvalidTotalAssetPurpose();\n    error InvalidDestination(address destination);\n    error TooFewAssets(uint256 requested, uint256 actual);\n    error SharesAndAssetsReceived(uint256 assets, uint256 shares);\n    error AmountExceedsAllowance(uint256 shares, uint256 allowed);\n    error PositivePriceRecoupNotCovered(uint256 remaining);\n\n    event DestinationDebtReporting(\n        address destination, AutopoolDebt.IdleDebtUpdates debtInfo, uint256 claimed, uint256 claimGasUsed\n    );\n    event NewNavShareFeeMark(uint256 navPerShare, uint256 timestamp);\n    event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    event Withdraw(\n        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares\n    );\n\n    struct DestinationInfo {\n        /// @notice Current underlying value at the destination vault\n        /// @dev Used for calculating totalDebt, mid point of min and max\n        uint256 cachedDebtValue;\n        /// @notice Current minimum underlying value at the destination vault\n        /// @dev Used for calculating totalDebt during withdrawal\n        uint256 cachedMinDebtValue;\n        /// @notice Current maximum underlying value at the destination vault\n        /// @dev Used for calculating totalDebt of the deposit\n        uint256 cachedMaxDebtValue;\n        /// @notice Last block timestamp this info was updated\n        uint256 lastReport;\n        /// @notice How many shares of the destination vault we owned at last report\n        uint256 ownedShares;\n    }\n\n    struct IdleDebtUpdates {\n        bool pricesWereSafe;\n        uint256 totalIdleDecrease;\n        uint256 totalIdleIncrease;\n        uint256 totalDebtIncrease;\n        uint256 totalDebtDecrease;\n        uint256 totalMinDebtIncrease;\n        uint256 totalMinDebtDecrease;\n        uint256 totalMaxDebtIncrease;\n        uint256 totalMaxDebtDecrease;\n    }\n\n    struct RebalanceOutParams {\n        /// Address that will received the withdrawn underlyer\n        address receiver;\n        /// The \"out\" destination vault\n        address destinationOut;\n        /// The amount of tokenOut that will be withdrawn\n        uint256 amountOut;\n        /// The underlyer for destinationOut\n        address tokenOut;\n        IERC20 _baseAsset;\n        bool _shutdown;\n    }\n\n    /// @dev In memory struct only for managing vars in _withdraw\n    struct WithdrawInfo {\n        uint256 currentIdle;\n        uint256 assetsFromIdle;\n        uint256 totalAssetsToPull;\n        uint256 assetsToPull;\n        uint256 assetsPulled;\n        uint256 idleIncrease;\n        uint256 debtDecrease;\n        uint256 debtMinDecrease;\n        uint256 debtMaxDecrease;\n        uint256 totalMinDebt;\n        uint256 destinationRound;\n        uint256 lastRoundSlippage;\n        uint256 expectedAssets;\n        uint256 remainingRecoup;\n    }\n\n    struct FlashRebalanceParams {\n        uint256 totalIdle;\n        uint256 totalDebt;\n        IERC20 baseAsset;\n        bool shutdown;\n    }\n\n    struct FlashResultInfo {\n        uint256 tokenInBalanceBefore;\n        uint256 tokenInBalanceAfter;\n        bytes32 flashResult;\n    }\n\n    function flashRebalance(\n        DestinationInfo storage destInfoOut,\n        DestinationInfo storage destInfoIn,\n        IERC3156FlashBorrower receiver,\n        IStrategy.RebalanceParams memory params,\n        IStrategy.SummaryStats memory destSummaryOut,\n        IAutopoolStrategy autoPoolStrategy,\n        FlashRebalanceParams memory flashParams,\n        bytes calldata data\n    ) external returns (IdleDebtUpdates memory result) {\n        // Handle decrease (shares going \"Out\", cashing in shares and sending underlying back to swapper)\n        // If the tokenOut is _asset we assume they are taking idle\n        // which is already in the contract\n        result = _handleRebalanceOut(\n            AutopoolDebt.RebalanceOutParams({\n                receiver: address(receiver),\n                destinationOut: params.destinationOut,\n                amountOut: params.amountOut,\n                tokenOut: params.tokenOut,\n                _baseAsset: flashParams.baseAsset,\n                _shutdown: flashParams.shutdown\n            }),\n            destInfoOut\n        );\n\n        if (!result.pricesWereSafe) {\n            revert InvalidPrices();\n        }\n\n        // Handle increase (shares coming \"In\", getting underlying from the swapper and trading for new shares)\n        if (params.amountIn > 0) {\n            FlashResultInfo memory flashResultInfo;\n            // get \"before\" counts\n            flashResultInfo.tokenInBalanceBefore = IERC20(params.tokenIn).balanceOf(address(this));\n\n            // Give control back to the solver so they can make use of the \"out\" assets\n            // and get our \"in\" asset\n            flashResultInfo.flashResult = receiver.onFlashLoan(msg.sender, params.tokenIn, params.amountIn, 0, data);\n\n            // We assume the solver will send us the assets\n            flashResultInfo.tokenInBalanceAfter = IERC20(params.tokenIn).balanceOf(address(this));\n\n            // Make sure the call was successful and verify we have at least the assets we think\n            // we were getting\n            if (\n                flashResultInfo.flashResult != keccak256(\"ERC3156FlashBorrower.onFlashLoan\")\n                    || flashResultInfo.tokenInBalanceAfter < flashResultInfo.tokenInBalanceBefore + params.amountIn\n            ) {\n                revert Errors.FlashLoanFailed(params.tokenIn, params.amountIn);\n            }\n\n            {\n                // make sure we have a valid path\n                (bool success, string memory message) = autoPoolStrategy.verifyRebalance(params, destSummaryOut);\n                if (!success) {\n                    revert RebalanceFailed(message);\n                }\n            }\n\n            if (params.tokenIn != address(flashParams.baseAsset)) {\n                IdleDebtUpdates memory inDebtResult = _handleRebalanceIn(\n                    destInfoIn,\n                    IDestinationVault(params.destinationIn),\n                    params.tokenIn,\n                    flashResultInfo.tokenInBalanceAfter\n                );\n                if (!inDebtResult.pricesWereSafe) {\n                    revert InvalidPrices();\n                }\n                result.totalDebtDecrease += inDebtResult.totalDebtDecrease;\n                result.totalDebtIncrease += inDebtResult.totalDebtIncrease;\n                result.totalMinDebtDecrease += inDebtResult.totalMinDebtDecrease;\n                result.totalMinDebtIncrease += inDebtResult.totalMinDebtIncrease;\n                result.totalMaxDebtDecrease += inDebtResult.totalMaxDebtDecrease;\n                result.totalMaxDebtIncrease += inDebtResult.totalMaxDebtIncrease;\n            } else {\n                result.totalIdleIncrease += flashResultInfo.tokenInBalanceAfter - flashResultInfo.tokenInBalanceBefore;\n            }\n        }\n    }\n\n    /// @notice Perform deposit and debt info update for the \"in\" destination during a rebalance\n    /// @dev This \"in\" function performs less validations than its \"out\" version\n    /// @param dvIn The \"in\" destination vault\n    /// @param tokenIn The underlyer for dvIn\n    /// @param depositAmount The amount of tokenIn that will be deposited\n    /// @return result Changes in debt values\n    function _handleRebalanceIn(\n        DestinationInfo storage destInfo,\n        IDestinationVault dvIn,\n        address tokenIn,\n        uint256 depositAmount\n    ) private returns (IdleDebtUpdates memory result) {\n        LibAdapter._approve(IERC20(tokenIn), address(dvIn), depositAmount);\n\n        // Snapshot our current shares so we know how much to back out\n        uint256 originalShareBal = dvIn.balanceOf(address(this));\n\n        // deposit to dv\n        uint256 newShares = dvIn.depositUnderlying(depositAmount);\n\n        // Update the debt info snapshot\n        result = _recalculateDestInfo(destInfo, dvIn, originalShareBal, originalShareBal + newShares);\n    }\n\n    /**\n     * @notice Perform withdraw and debt info update for the \"out\" destination during a rebalance\n     * @dev This \"out\" function performs more validations and handles idle as opposed to \"in\" which does not\n     *  debtDecrease The previous amount of debt destinationOut accounted for in totalDebt\n     *  debtIncrease The current amount of debt destinationOut should account for in totalDebt\n     *  idleDecrease Amount of baseAsset that was sent from the vault. > 0 only when tokenOut == baseAsset\n     *  idleIncrease Amount of baseAsset that was claimed from Destination Vault\n     * @param params Rebalance out params\n     * @param destOutInfo The \"out\" destination vault info\n     * @return assetChange debt and idle change data\n     */\n    function _handleRebalanceOut(\n        RebalanceOutParams memory params,\n        DestinationInfo storage destOutInfo\n    ) private returns (IdleDebtUpdates memory assetChange) {\n        // Handle decrease (shares going \"Out\", cashing in shares and sending underlying back to swapper)\n        // If the tokenOut is _asset we assume they are taking idle\n        // which is already in the contract\n        if (params.amountOut > 0) {\n            if (params.tokenOut != address(params._baseAsset)) {\n                IDestinationVault dvOut = IDestinationVault(params.destinationOut);\n\n                // Snapshot our current shares so we know how much to back out\n                uint256 originalShareBal = dvOut.balanceOf(address(this));\n\n                // Burning our shares will claim any pending baseAsset\n                // rewards and send them to us.\n                // Get our starting balance\n                uint256 beforeBaseAssetBal = params._baseAsset.balanceOf(address(this));\n\n                // Withdraw underlying from the destination vault\n                // Shares are sent directly to the flashRebalance receiver\n                // slither-disable-next-line unused-return\n                dvOut.withdrawUnderlying(params.amountOut, params.receiver);\n\n                // Update the debt info snapshot\n                assetChange =\n                    _recalculateDestInfo(destOutInfo, dvOut, originalShareBal, originalShareBal - params.amountOut);\n\n                // Capture any rewards we may have claimed as part of withdrawing\n                assetChange.totalIdleIncrease = params._baseAsset.balanceOf(address(this)) - beforeBaseAssetBal;\n            } else {\n                // If we are shutdown then the only operations we should be performing are those that get\n                // the base asset back to the vault. We shouldn't be sending out more\n\n                if (params._shutdown) {\n                    revert VaultShutdown();\n                }\n                // Working with idle baseAsset which should be in the vault already\n                // Just send it out\n                IERC20(params.tokenOut).safeTransfer(params.receiver, params.amountOut);\n                assetChange.totalIdleDecrease = params.amountOut;\n\n                // We weren't dealing with any debt or pricing, just idle, so we can just mark\n                // it as safe\n                assetChange.pricesWereSafe = true;\n            }\n        }\n    }\n\n    function recalculateDestInfo(\n        DestinationInfo storage destInfo,\n        IDestinationVault destVault,\n        uint256 originalShares,\n        uint256 currentShares\n    ) external returns (IdleDebtUpdates memory result) {\n        result = _recalculateDestInfo(destInfo, destVault, originalShares, currentShares);\n    }\n\n    /// @dev Will not revert on unsafe prices. Up to the caller.\n    function _recalculateDestInfo(\n        DestinationInfo storage destInfo,\n        IDestinationVault destVault,\n        uint256 originalShares,\n        uint256 currentShares\n    ) private returns (IdleDebtUpdates memory result) {\n        // Figure out what to back out of our totalDebt number.\n        // We could have had withdraws since the last snapshot which means our\n        // cached currentDebt number should be decreased based on the remaining shares\n        // totalDebt is decreased using the same proportion of shares method during withdrawals\n        // so this should represent whatever is remaining.\n\n        // Prices are per LP token and whether or not the prices are safe to use\n        // If they aren't safe then just continue and we'll get it on the next go around\n        (uint256 spotPrice, uint256 safePrice, bool isSpotSafe) = destVault.getRangePricesLP();\n\n        // Calculate what we're backing out based on the original shares\n        uint256 minPrice = spotPrice > safePrice ? safePrice : spotPrice;\n        uint256 maxPrice = spotPrice > safePrice ? spotPrice : safePrice;\n\n        // If we previously had shares, calculate how much of our cached numbers\n        // still remain as this will be deducted from the overall debt numbers\n        // over time\n        uint256 prevOwnedShares = destInfo.ownedShares;\n        if (prevOwnedShares > 0) {\n            result.totalDebtDecrease = (destInfo.cachedDebtValue * originalShares) / prevOwnedShares;\n            result.totalMinDebtDecrease = (destInfo.cachedMinDebtValue * originalShares) / prevOwnedShares;\n            result.totalMaxDebtDecrease = (destInfo.cachedMaxDebtValue * originalShares) / prevOwnedShares;\n        }\n\n        // The overall debt value is the mid point of min and max\n        uint256 div = 10 ** destVault.decimals();\n        uint256 newDebtValue = (minPrice * currentShares + maxPrice * currentShares) / (div * 2);\n\n        result.pricesWereSafe = isSpotSafe;\n        result.totalDebtIncrease = newDebtValue;\n        result.totalMinDebtIncrease = minPrice * currentShares / div;\n        result.totalMaxDebtIncrease = maxPrice * currentShares / div;\n\n        // Save our current new values\n        destInfo.cachedDebtValue = newDebtValue;\n        destInfo.cachedMinDebtValue = result.totalMinDebtIncrease;\n        destInfo.cachedMaxDebtValue = result.totalMaxDebtIncrease;\n        destInfo.lastReport = block.timestamp;\n        destInfo.ownedShares = currentShares;\n    }\n\n    function totalAssetsTimeChecked(\n        StructuredLinkedList.List storage debtReportQueue,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo,\n        IAutopool.TotalAssetPurpose purpose\n    ) external returns (uint256) {\n        IDestinationVault destVault = IDestinationVault(debtReportQueue.peekHead());\n        uint256 recalculatedTotalAssets = IAutopool(address(this)).totalAssets(purpose);\n\n        while (address(destVault) != address(0)) {\n            uint256 lastReport = destinationInfo[address(destVault)].lastReport;\n\n            if (lastReport + MAX_DEBT_REPORT_AGE_SECONDS > block.timestamp) {\n                // Its not stale\n\n                // This report is OK, we don't need to recalculate anything\n                break;\n            } else {\n                // It is stale, recalculate\n\n                //slither-disable-next-line unused-return\n                uint256 currentShares = destVault.balanceOf(address(this));\n                uint256 staleDebt;\n                uint256 extremePrice;\n\n                // Figure out exactly which price to use based on its purpose\n                if (purpose == IAutopool.TotalAssetPurpose.Deposit) {\n                    // We use max value so that anything deposited is worth less\n                    extremePrice = destVault.getUnderlyerCeilingPrice();\n\n                    // Round down. We are subtracting this value out of the total so some left\n                    // behind just increases the value which is what we want\n                    staleDebt = destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                        currentShares, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Down\n                    );\n                } else if (purpose == IAutopool.TotalAssetPurpose.Withdraw) {\n                    // We use min value so that we value the shares as worth less\n                    extremePrice = destVault.getUnderlyerFloorPrice();\n                    // Round up. We are subtracting this value out of the total so if we take a little\n                    // extra it just decreases the value which is what we want\n                    staleDebt = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                        currentShares, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n                    );\n                } else {\n                    revert InvalidTotalAssetPurpose();\n                }\n\n                // Back out our stale debt, add in its new value\n                // Our goal is to find the most conservative value in each situation. If the current\n                // value we have represents that, then use it. Otherwise, use the new one.\n                uint256 newValue = (currentShares * extremePrice) / destVault.ONE();\n\n                if (purpose == IAutopool.TotalAssetPurpose.Deposit && staleDebt > newValue) {\n                    newValue = staleDebt;\n                } else if (purpose == IAutopool.TotalAssetPurpose.Withdraw && staleDebt < newValue) {\n                    newValue = staleDebt;\n                }\n\n                recalculatedTotalAssets = recalculatedTotalAssets + newValue - staleDebt;\n            }\n\n            destVault = IDestinationVault(debtReportQueue.getAdjacent(address(destVault), true));\n        }\n\n        return recalculatedTotalAssets;\n    }\n\n    function _updateDebtReporting(\n        StructuredLinkedList.List storage debtReportQueue,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo,\n        uint256 numToProcess\n    ) external returns (IdleDebtUpdates memory result) {\n        numToProcess = Math.min(numToProcess, debtReportQueue.sizeOf());\n\n        for (uint256 i = 0; i < numToProcess; ++i) {\n            IDestinationVault destVault = IDestinationVault(debtReportQueue.popHead());\n\n            // Get the reward value we've earned. DV rewards are always in terms of base asset\n            // We track the gas used purely for off-chain stats purposes\n            // Main rewarder on DV's store the earned and liquidated rewards\n            // Extra rewarders are disabled at the DV level\n            uint256 claimGasUsed = gasleft();\n            uint256 beforeBaseAsset = IERC20(IAutopool(address(this)).asset()).balanceOf(address(this));\n            IMainRewarder(destVault.rewarder()).getReward(address(this), address(this), false);\n            uint256 claimedRewardValue =\n                IERC20(IAutopool(address(this)).asset()).balanceOf(address(this)) - beforeBaseAsset;\n            result.totalIdleIncrease += claimedRewardValue;\n\n            // Recalculate the debt info figuring out the change in\n            // total debt value we can roll up later\n            uint256 currentShareBalance = destVault.balanceOf(address(this));\n\n            AutopoolDebt.IdleDebtUpdates memory debtResult = _recalculateDestInfo(\n                destinationInfo[address(destVault)], destVault, currentShareBalance, currentShareBalance\n            );\n\n            result.totalDebtDecrease += debtResult.totalDebtDecrease;\n            result.totalDebtIncrease += debtResult.totalDebtIncrease;\n            result.totalMinDebtDecrease += debtResult.totalMinDebtDecrease;\n            result.totalMinDebtIncrease += debtResult.totalMinDebtIncrease;\n            result.totalMaxDebtDecrease += debtResult.totalMaxDebtDecrease;\n            result.totalMaxDebtIncrease += debtResult.totalMaxDebtIncrease;\n\n            // If we no longer have shares, then there's no reason to continue reporting on the destination.\n            // The strategy will only call for the info if its moving \"out\" of the destination\n            // and that will only happen if we have shares.\n            // A rebalance where we move \"in\" to the position will refresh the data at that time\n            if (currentShareBalance > 0) {\n                debtReportQueue.addToTail(address(destVault));\n            }\n\n            claimGasUsed -= gasleft();\n\n            emit DestinationDebtReporting(address(destVault), debtResult, claimedRewardValue, claimGasUsed);\n        }\n    }\n\n    function _initiateWithdrawInfo(\n        uint256 assets,\n        IAutopool.AssetBreakdown storage assetBreakdown\n    ) private view returns (WithdrawInfo memory) {\n        uint256 idle = assetBreakdown.totalIdle;\n        WithdrawInfo memory info = WithdrawInfo({\n            currentIdle: idle,\n            // If idle can cover the full amount, then we want to pull all assets from there\n            // Otherwise, we want to pull from the market and only get idle if we exhaust the market\n            assetsFromIdle: assets > idle ? 0 : assets,\n            totalAssetsToPull: 0,\n            assetsToPull: 0,\n            assetsPulled: 0,\n            idleIncrease: 0,\n            debtDecrease: 0,\n            debtMinDecrease: 0,\n            debtMaxDecrease: 0,\n            totalMinDebt: assetBreakdown.totalDebtMin,\n            destinationRound: 0,\n            lastRoundSlippage: 0,\n            expectedAssets: 0,\n            remainingRecoup: 0\n        });\n\n        info.totalAssetsToPull = assets - info.assetsFromIdle;\n\n        // This var we use to track our progress later\n        info.assetsToPull = assets - info.assetsFromIdle;\n\n        // Idle + minDebt is the maximum amount of assets/debt we could burn during a withdraw.\n        // If the user is request more than that (like during a withdraw) we can just revert\n        // early without trying\n        if (info.totalAssetsToPull > info.currentIdle + info.totalMinDebt) {\n            revert TooFewAssets(assets, info.currentIdle + info.totalMinDebt);\n        }\n\n        return info;\n    }\n\n    function withdraw(\n        uint256 assets,\n        uint256 applicableTotalAssets,\n        IAutopool.AssetBreakdown storage assetBreakdown,\n        StructuredLinkedList.List storage withdrawalQueue,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo\n    ) public returns (uint256 actualAssets, uint256 actualShares, uint256 debtBurned) {\n        WithdrawInfo memory info = _initiateWithdrawInfo(assets, assetBreakdown);\n\n        // Pull the market if there aren't enough funds in idle to cover the entire amount\n\n        // This flow is not bounded by a set number of shares. The user has requested X assets\n        // and a variable number of shares to burn so we don't have easy break out points like we do\n        // during redeem (like using debt burned). When we get slippage here and don't meet the requested assets\n        // we need to keep going if we can. This is tricky if we consider that (most of) our destinations are\n        // LP positions and we'll be swapping assets, so we can expect some slippage. Even\n        // if our minDebtValue numbers are up to date and perfectly accurate slippage could ensure we\n        // are always receiving less than we expect/calculate and we never hit the requested assets\n        // even though the owner would have shares to cover it. Under normal/expected conditions, our\n        // minDebtValue is lower than actual and we expect overall value to be going up, so we burn a tad\n        // more than we should and receive a tad more than we expect. This should cover us. However,\n        // in other conditions we have to be sure we aren't endlessly trying to approach 0 so we are tracking\n        // the slippage we received on the last pull, repricing, and applying an increasing multiplier until we either\n        // pull enough to cover or pull them all and/or move to the next destination.\n\n        uint256 dvSharesToBurn;\n        while (info.assetsToPull > 0) {\n            IDestinationVault destVault = IDestinationVault(withdrawalQueue.peekHead());\n\n            // We've run out of destinations\n            if (address(destVault) == address(0)) {\n                break;\n            }\n\n            uint256 dvShares = destVault.balanceOf(address(this));\n            {\n                uint256 dvSharesValue;\n                if (info.destinationRound == 0) {\n                    // First time pulling\n\n                    // We use the min debt value here because its a withdrawal and we're trying to cover an amount\n                    // of assets. Undervaluing the shares may mean we pull more but given that we expect slippage\n                    // that is desirable.\n                    dvSharesValue = destinationInfo[address(destVault)].cachedMinDebtValue * dvShares\n                        / destinationInfo[address(destVault)].ownedShares;\n                } else {\n                    // When we've pulled from this destination before, i.e. destinationRound > 0, then we\n                    // know a more accurate exchange rate and its worse than we were expecting.\n                    // We even will pad it a bit as we want to account for any additional slippage we\n                    // may receive by say being farther down an AMM curve.\n\n                    // dvSharesToBurn is the last value we used when pulling from this destination\n                    // info.expectedAssets is how much we expected to get on that last pull\n                    // info.expectedAssets - info.lastRoundSlippage is how much we actually received\n\n                    uint256 paddedSlippage = info.lastRoundSlippage * (info.destinationRound + 10_000) / 10_000;\n\n                    if (paddedSlippage < info.expectedAssets) {\n                        dvSharesValue = (info.expectedAssets - paddedSlippage) * dvShares / dvSharesToBurn;\n                    } else {\n                        // This will just mean we pull all shares\n                        dvSharesValue = 0;\n                    }\n                }\n\n                if (dvSharesValue > info.assetsToPull) {\n                    dvSharesToBurn = (dvShares * info.assetsToPull) / dvSharesValue;\n\n                    // On withdraw, we are trying to meet a specific number of assets without a limit\n                    // on the debt we can burn. Burning 0 due to the valuations here would be an automatic failure\n                    // as we still have assets to satisfy and debt to burn. We at least have to burn 1 even if it\n                    // results in a larger over pull\n                    if (dvSharesToBurn == 0) {\n                        dvSharesToBurn = 1;\n                    }\n\n                    // Only need to set it here because the only time we'll use it is if\n                    // we don't exhaust all shares and have to try the destination again\n                    info.expectedAssets = info.assetsToPull;\n                } else {\n                    dvSharesToBurn = dvShares;\n                }\n            }\n\n            uint256 pulledAssets;\n            uint256 debtValueBurned;\n            // Get the base asset back from the Destination. Also performs a check that we aren't receiving\n            // poor execution on our swaps based on safe prices\n            (info, pulledAssets, debtValueBurned) = _withdrawAssets(info, destinationInfo, destVault, dvSharesToBurn);\n\n            info.assetsPulled += pulledAssets;\n\n            if (info.remainingRecoup > 0) {\n                // If the destination is so severely undervalued that it can't cover its own recoup then we have no\n                // recourse but to burn the entire destination and the user would to have to cover the full overage\n                // from the next destinations can get nothing from this one. Should not be allowed.\n                revert PositivePriceRecoupNotCovered(info.remainingRecoup);\n            }\n\n            // If we've exhausted all shares we can remove the withdrawal from the queue\n            // We need to leave it in the debt report queue though so that our destination specific\n            // debt tracking values can be updated\n            if (dvShares == dvSharesToBurn) {\n                withdrawalQueue.popAddress(address(destVault));\n                info.destinationRound = 0;\n                info.lastRoundSlippage = 0;\n            } else {\n                // If we didn't burn all the shares and we received enough to cover our\n                // expected that means we'll break out below as we've hit our target\n                unchecked {\n                    if (pulledAssets < info.expectedAssets) {\n                        info.lastRoundSlippage = info.expectedAssets - pulledAssets;\n                        if (info.destinationRound == 0) {\n                            info.destinationRound = 100;\n                        } else {\n                            info.destinationRound *= 2;\n                        }\n                    }\n                }\n            }\n\n            // It's possible we'll get back more assets than we anticipate from a swap\n            // so if we do, throw it in idle and stop processing. You don't get more than we've calculated\n            if (info.assetsPulled >= info.totalAssetsToPull) {\n                info.idleIncrease += info.assetsPulled - info.totalAssetsToPull;\n                info.assetsPulled = info.totalAssetsToPull;\n                info.assetsToPull = 0;\n                break;\n            }\n\n            info.assetsToPull -= pulledAssets;\n        }\n\n        // We didn't get enough assets from the debt pull\n        // See if we can get the rest from idle\n        if (info.assetsPulled < assets && info.currentIdle > 0) {\n            uint256 remaining = assets - info.assetsPulled;\n            if (remaining <= info.currentIdle) {\n                info.assetsFromIdle = remaining;\n            }\n            // We don't worry about the else case because if currentIdle can't\n            // cover remaining then we'll fail the `actualAssets < assets`\n            // check below and revert\n        }\n\n        debtBurned = info.assetsFromIdle + info.debtMinDecrease;\n        actualAssets = info.assetsFromIdle + info.assetsPulled;\n\n        if (actualAssets < assets) {\n            revert TooFewAssets(assets, actualAssets);\n        }\n\n        actualShares = IAutopool(address(this)).convertToShares(\n            Math.max(actualAssets, debtBurned),\n            applicableTotalAssets,\n            IAutopool(address(this)).totalSupply(),\n            Math.Rounding.Up\n        );\n\n        // Subtract what's taken out of idle from totalIdle\n        // We may also have some increase to account for it we over pulled\n        // or received better execution than we were anticipating\n        // slither-disable-next-line events-maths\n        assetBreakdown.totalIdle = info.currentIdle + info.idleIncrease - info.assetsFromIdle;\n\n        // Save off our various debt numbers\n        if (info.debtDecrease > assetBreakdown.totalDebt) {\n            assetBreakdown.totalDebt = 0;\n        } else {\n            assetBreakdown.totalDebt -= info.debtDecrease;\n        }\n\n        if (info.debtMinDecrease > info.totalMinDebt) {\n            assetBreakdown.totalDebtMin = 0;\n        } else {\n            assetBreakdown.totalDebtMin -= info.debtMinDecrease;\n        }\n\n        if (info.debtMaxDecrease > assetBreakdown.totalDebtMax) {\n            assetBreakdown.totalDebtMax = 0;\n        } else {\n            assetBreakdown.totalDebtMax -= info.debtMaxDecrease;\n        }\n    }\n\n    function _withdrawAssets(\n        WithdrawInfo memory info,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo,\n        IDestinationVault destVault,\n        uint256 dvSharesToBurn\n    ) internal returns (WithdrawInfo memory, uint256 pulledAssets, uint256 debtValueBurned) {\n        if (dvSharesToBurn > 0) {\n            address[] memory tokensBurned;\n            uint256[] memory amountsBurned;\n\n            // Destination Vaults always burn the exact amount we instruct them to\n            (pulledAssets, tokensBurned, amountsBurned) = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));\n\n            // Calculate the totalDebt we'll need to remove based on the shares we're burning\n            // We're rounding up here so take care when actually applying to totalDebt\n            debtValueBurned = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMinDecrease += debtValueBurned;\n\n            info.debtDecrease += destinationInfo[address(destVault)].cachedDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n\n            uint256 maxDebtBurned = destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMaxDecrease += maxDebtBurned;\n\n            // See if we received a reasonable amount of the base asset back based on the value\n            // of the tokens that were burned.\n            uint256 totalValueBurned;\n            {\n                uint256 tokenLen = tokensBurned.length;\n                IRootPriceOracle rootPriceOracle = ISystemRegistry(destVault.getSystemRegistry()).rootPriceOracle();\n                for (uint256 i = 0; i < tokenLen;) {\n                    totalValueBurned += amountsBurned[i] * rootPriceOracle.getPriceInEth(tokensBurned[i])\n                        / (10 ** IERC20(tokensBurned[i]).decimals());\n                    unchecked {\n                        ++i;\n                    }\n                }\n            }\n\n            // How much, if any, should be dropping into idle?\n            // Anything pulled over debtValueBurned goes to idle, user can't get more than we think its worth.\n            // However, if we pulled less than the current value of the tokens we burned, so long as\n            // that value is greater than debt min, we need to recoup that as well and put it into idle\n\n            uint256 amountToRecoup;\n            if (totalValueBurned > debtValueBurned) {\n                // The shares we burned are worth more than we'll be recouping from the debt burn\n                // the difference we still need to get\n                amountToRecoup = totalValueBurned - debtValueBurned;\n\n                uint256 maxCreditBps = destVault.recoupMaxCredit();\n                uint256 gapCredit = maxDebtBurned - debtValueBurned;\n                uint256 credit = Math.min(gapCredit, debtValueBurned * maxCreditBps / 10_000);\n\n                if (credit > amountToRecoup) {\n                    amountToRecoup = 0;\n                } else {\n                    amountToRecoup -= credit;\n                }\n            }\n\n            // This is done regardless of whether we were under valued. User can still only\n            // get what we've valued it at.\n            if (pulledAssets > debtValueBurned) {\n                uint256 overDebtValue = pulledAssets - debtValueBurned;\n                info.idleIncrease += overDebtValue;\n                pulledAssets -= overDebtValue;\n\n                // Since this is going to idle it goes to satisfy the recoup as well\n                if (amountToRecoup > 0) {\n                    if (amountToRecoup > overDebtValue) {\n                        amountToRecoup -= overDebtValue;\n                    } else {\n                        amountToRecoup = 0;\n                    }\n                }\n            }\n\n            // If we still have a value we need to recoup it means that the debt range credit\n            // as well as what was pulled over the min debt value wasn't enough to cover\n            // the under valued burn. Now we have to try and take it from what is going back\n            // to the user\n            if (amountToRecoup > 0) {\n                if (amountToRecoup > pulledAssets) {\n                    // Recoup is more than we pulled so we'll have some recoup left over\n                    amountToRecoup -= pulledAssets;\n\n                    // Everything that was pulled goes to idle\n                    info.idleIncrease += pulledAssets;\n                    pulledAssets = 0;\n\n                    // We'll have to try and get the remaining amount from another destination\n                    info.remainingRecoup += amountToRecoup;\n                } else {\n                    // We pulled enough assets to cover the recoup\n                    pulledAssets -= amountToRecoup;\n\n                    // Ensure the recoup goes to idle\n                    info.idleIncrease += amountToRecoup;\n                }\n            }\n        }\n\n        return (info, pulledAssets, debtValueBurned);\n    }\n\n    /// @notice Perform a removal of assets via the redeem path where the shares are the limiting factor.\n    /// This means we break out whenever we reach either `assets` retrieved or debt value equivalent to `assets` burned\n    function redeem(\n        uint256 assets,\n        uint256 applicableTotalAssets,\n        IAutopool.AssetBreakdown storage assetBreakdown,\n        StructuredLinkedList.List storage withdrawalQueue,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo\n    ) public returns (uint256 actualAssets, uint256 actualShares, uint256 debtBurned) {\n        WithdrawInfo memory info = _initiateWithdrawInfo(assets, assetBreakdown);\n\n        // If not enough funds in idle, then pull what we need from destinations\n        bool exhaustedDestinations = false;\n        while (info.assetsToPull > 0) {\n            IDestinationVault destVault = IDestinationVault(withdrawalQueue.peekHead());\n            if (address(destVault) == address(0)) {\n                exhaustedDestinations = true;\n                break;\n            }\n\n            uint256 dvShares = destVault.balanceOf(address(this));\n            uint256 dvSharesToBurn = dvShares;\n            {\n                // Valuing these shares higher, rounding up, will result in us burning less of them\n                // in the event we don't burn all of them. Good thing.\n                uint256 dvSharesValue = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                    dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n                );\n\n                // If the dv shares we own are worth more than we need, limit the shares to burn\n                // Any extra we get will be dropped into idle\n                if (dvSharesValue > info.assetsToPull) {\n                    uint256 limitedShares = (dvSharesToBurn * info.assetsToPull) / dvSharesValue;\n\n                    // Final set for the actual shares we'll burn later\n                    dvSharesToBurn = limitedShares;\n                }\n            }\n\n            uint256 pulledAssets;\n            uint256 debtValueBurned;\n            // Get the base asset back from the Destination. Also performs a check that we aren't receiving\n            // poor execution on our swaps based on safe prices\n            // slither-disable-next-line unused-return\n            (info, pulledAssets, debtValueBurned) = _withdrawAssets(info, destinationInfo, destVault, dvSharesToBurn);\n\n            // If we've exhausted all shares we can remove the destination from the withdrawal queue\n            // We need to leave it in the debt report queue though so that our destination specific\n            // debt tracking values can be updated\n            if (dvShares == dvSharesToBurn) {\n                withdrawalQueue.popAddress(address(destVault));\n            }\n\n            info.assetsPulled += pulledAssets;\n\n            // Any deficiency in the amount we received is slippage.\n            // There is a round up on debtValueBurned so just making sure it never under flows here\n            // _withdrawAssets ensures that pulledAssets is always lte debtValueBurned and we always\n            // want to debit the max so we just use debtValueBurned\n            if (debtValueBurned > info.assetsToPull) {\n                info.assetsToPull = 0;\n            } else {\n                info.assetsToPull -= debtValueBurned;\n            }\n\n            // We either have enough assets, or we've burned the max debt we're allowed\n            if (info.assetsToPull == 0) {\n                break;\n            }\n\n            // If we didn't exhaust all of the shares from the destination it means we\n            // assume we will get everything we need from there and everything else is slippage\n            if (dvShares != dvSharesToBurn) {\n                info.assetsToPull = 0;\n                break;\n            }\n        }\n\n        // See if we can pull the remaining recoup from other destinations we may have pulled from\n        if (info.remainingRecoup > 0) {\n            if (info.remainingRecoup > info.assetsPulled) {\n                info.remainingRecoup -= info.assetsPulled;\n                info.idleIncrease += info.assetsPulled;\n                info.assetsPulled = 0;\n            } else {\n                info.assetsPulled -= info.remainingRecoup;\n                info.idleIncrease += info.remainingRecoup;\n                info.remainingRecoup = 0;\n            }\n        }\n\n        // We didn't get enough assets from the debt pull\n        // See if we can get the rest from idle\n        if (info.assetsToPull > 0 && info.currentIdle > 0 && exhaustedDestinations) {\n            if (info.assetsToPull < info.currentIdle) {\n                info.assetsFromIdle = info.assetsToPull;\n            } else {\n                info.assetsFromIdle = info.currentIdle;\n            }\n        }\n\n        debtBurned = info.assetsFromIdle + info.debtMinDecrease;\n        actualAssets = info.assetsFromIdle + info.assetsPulled;\n\n        // If we took from idle, and we have remaining assets to recoup\n        // we need to put some back in idle\n        if (info.remainingRecoup > 0 && info.assetsFromIdle > 0) {\n            // We only need to do this if the idle assets can cover the remaining recoup fully because\n            // we'll be reverting otherwise\n            if (info.assetsFromIdle >= info.remainingRecoup) {\n                // We still need to charge for the recoup so we're going to leave it in debtBurned\n                // but we'll take it back out of actualAssets so it stays in idle. We need to lower\n                // assetsFromIdle as well so that the final numbers get updated too\n                actualAssets -= info.remainingRecoup;\n                info.assetsFromIdle -= info.remainingRecoup;\n                info.remainingRecoup = 0;\n            } else {\n                // Just updating this number so we get an accurate value in the revert below\n                info.remainingRecoup -= info.assetsFromIdle;\n            }\n        }\n\n        // We took everything we could and still can't cover, time to revert\n        if (info.remainingRecoup > 0) {\n            revert PositivePriceRecoupNotCovered(info.remainingRecoup);\n        }\n\n        actualShares = IAutopool(address(this)).convertToShares(\n            debtBurned, applicableTotalAssets, IAutopool(address(this)).totalSupply(), Math.Rounding.Up\n        );\n\n        // Subtract what's taken out of idle from totalIdle\n        // We may also have some increase to account for it we over pulled\n        // or received better execution than we were anticipating\n        // slither-disable-next-line events-maths\n        assetBreakdown.totalIdle = info.currentIdle + info.idleIncrease - info.assetsFromIdle;\n\n        // Save off our various debt numbers\n        if (info.debtDecrease > assetBreakdown.totalDebt) {\n            assetBreakdown.totalDebt = 0;\n        } else {\n            assetBreakdown.totalDebt -= info.debtDecrease;\n        }\n\n        if (info.debtMinDecrease > info.totalMinDebt) {\n            assetBreakdown.totalDebtMin = 0;\n        } else {\n            assetBreakdown.totalDebtMin -= info.debtMinDecrease;\n        }\n\n        if (info.debtMaxDecrease > assetBreakdown.totalDebtMax) {\n            assetBreakdown.totalDebtMax = 0;\n        } else {\n            assetBreakdown.totalDebtMax -= info.debtMaxDecrease;\n        }\n    }\n\n    /**\n     * @notice Function to complete a withdrawal or redeem.  This runs after shares to be burned and assets to be\n     *    transferred are calculated.\n     * @param assets Amount of assets to be transferred to receiver.\n     * @param shares Amount of shares to be burned from owner.\n     * @param owner Owner of shares, user to burn shares from.\n     * @param receiver The receiver of the baseAsset.\n     * @param baseAsset Base asset of the Autopool.\n     * @param assetBreakdown Asset breakdown for the Autopool.\n     * @param tokenData Token data for the Autopool.\n     */\n    function completeWithdrawal(\n        uint256 assets,\n        uint256 shares,\n        address owner,\n        address receiver,\n        IERC20 baseAsset,\n        IAutopool.AssetBreakdown storage assetBreakdown,\n        AutopoolToken.TokenData storage tokenData\n    ) external {\n        if (msg.sender != owner) {\n            uint256 allowed = IAutopool(address(this)).allowance(owner, msg.sender);\n            if (allowed != type(uint256).max) {\n                if (shares > allowed) revert AmountExceedsAllowance(shares, allowed);\n\n                unchecked {\n                    tokenData.approve(owner, msg.sender, allowed - shares);\n                }\n            }\n        }\n\n        tokenData.burn(owner, shares);\n\n        uint256 ts = IAutopool(address(this)).totalSupply();\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n\n        emit Nav(assetBreakdown.totalIdle, assetBreakdown.totalDebt, ts);\n\n        baseAsset.safeTransfer(receiver, assets);\n    }\n\n    /**\n     * @notice A helper function to get estimates of what would happen on a withdraw or redeem.\n     * @dev Reverts all changing state.\n     * @param previewWithdraw Bool denoting whether to preview a redeem or withdrawal.\n     * @param assets Assets to be withdrawn or redeemed.\n     * @param applicableTotalAssets Operation dependent assets in the Autopool.\n     * @param functionCallEncoded Abi encoded function signature for recursive call.\n     * @param assetBreakdown Breakdown of vault assets from Autopool storage.\n     * @param withdrawalQueue Destination vault withdrawal queue from Autopool storage.\n     * @param destinationInfo Mapping of information for destinations.\n     * @return assetsAmount Preview of amount of assets to send to receiver.\n     * @return sharesAmount Preview of amount of assets to burn from owner.\n     */\n    function preview(\n        bool previewWithdraw,\n        uint256 assets,\n        uint256 applicableTotalAssets,\n        bytes memory functionCallEncoded,\n        IAutopool.AssetBreakdown storage assetBreakdown,\n        StructuredLinkedList.List storage withdrawalQueue,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo\n    ) external returns (uint256 assetsAmount, uint256 sharesAmount) {\n        if (msg.sender != address(this)) {\n            // Perform a recursive call the function in `funcCallEncoded`.  This will result in a call back to\n            // the Autopool, and then this function. The intention is to reach the \"else\" block in this function.\n            // solhint-disable avoid-low-level-calls\n            // slither-disable-next-line missing-zero-check,low-level-calls\n            (bool success, bytes memory returnData) = address(this).call(functionCallEncoded);\n            // solhint-enable avoid-low-level-calls\n\n            // If the recursive call is successful, it means an unintended code path was taken.\n            if (success) {\n                revert Errors.UnreachableError();\n            }\n\n            bytes4 sharesAmountSig = bytes4(keccak256(\"SharesAndAssetsReceived(uint256,uint256)\"));\n\n            // Extract the error signature (first 4 bytes) from the revert reason.\n            bytes4 errorSignature;\n            // solhint-disable no-inline-assembly\n            assembly {\n                errorSignature := mload(add(returnData, 0x20))\n            }\n\n            // If the error matches the expected signature, extract the amount from the revert reason and return.\n            if (errorSignature == sharesAmountSig) {\n                // Extract subsequent bytes for uint256.\n                assembly {\n                    assetsAmount := mload(add(returnData, 0x24))\n                    sharesAmount := mload(add(returnData, 0x44))\n                }\n            } else {\n                // If the error is not the expected one, forward the original revert reason.\n                assembly {\n                    revert(add(32, returnData), mload(returnData))\n                }\n            }\n            // solhint-enable no-inline-assembly\n        }\n        // This branch is taken during the recursive call.\n        else {\n            // Perform the actual withdrawal or redeem logic to compute the amount. This will be reverted to\n            // simulate the action.\n            uint256 previewAssets;\n            uint256 previewShares;\n            if (previewWithdraw) {\n                (previewAssets, previewShares,) =\n                    withdraw(assets, applicableTotalAssets, assetBreakdown, withdrawalQueue, destinationInfo);\n            } else {\n                (previewAssets, previewShares,) =\n                    redeem(assets, applicableTotalAssets, assetBreakdown, withdrawalQueue, destinationInfo);\n            }\n\n            // Revert with the computed amount as an error.\n            revert SharesAndAssetsReceived(previewAssets, previewShares);\n        }\n    }\n}\n"},{"file_path":"src/interfaces/liquidation/IAsyncSwapperRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\ninterface IAsyncSwapperRegistry {\n    event SwapperAdded(address indexed item);\n    event SwapperRemoved(address indexed item);\n\n    /// @notice Registers an item\n    /// @param item Item address to be added\n    function register(address item) external;\n\n    /// @notice Removes item registration\n    /// @param item Item address to be removed\n    function unregister(address item) external;\n\n    /// @notice Returns a list of all registered items\n    function list() external view returns (address[] memory);\n\n    /// @notice Checks if an address is a valid item\n    /// @param item Item address to be checked\n    function isRegistered(address item) external view returns (bool);\n\n    /// @notice Checks if an address is a valid swapper and reverts if not\n    /// @param item Swapper address to be checked\n    function verifyIsRegistered(address item) external view;\n}\n"},{"file_path":"src/interfaces/security/ISystemSecurity.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\ninterface ISystemSecurity {\n    /// @notice Get the number of NAV/share operations currently in progress\n    /// @return Number of operations\n    function navOpsInProgress() external view returns (uint256);\n\n    /// @notice Called at the start of any NAV/share changing operation\n    function enterNavOperation() external;\n\n    /// @notice Called at the end of any NAV/share changing operation\n    function exitNavOperation() external;\n\n    /// @notice Whether or not the system as a whole is paused\n    function isSystemPaused() external returns (bool);\n}\n"},{"file_path":"src/interfaces/rewarders/IMainRewarder.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IBaseRewarder } from \"src/interfaces/rewarders/IBaseRewarder.sol\";\nimport { IExtraRewarder } from \"src/interfaces/rewarders/IExtraRewarder.sol\";\n\ninterface IMainRewarder is IBaseRewarder {\n    error ExtraRewardsNotAllowed();\n    error MaxExtraRewardsReached();\n\n    /// @notice Extra rewards can be added, but not removed, ref: https://github.com/Tokemak/v2-core/issues/659\n    event ExtraRewardAdded(address reward);\n\n    /**\n     * @notice Adds an ExtraRewarder contract address to the extraRewards array.\n     * @param reward The address of the ExtraRewarder contract.\n     */\n    function addExtraReward(address reward) external;\n\n    /**\n     * @notice Withdraws the specified amount of tokens from the vault for the specified account, and transfers all\n     * rewards for the account from this contract and any linked extra reward contracts.\n     * @param account The address of the account to withdraw tokens and claim rewards for.\n     * @param amount The amount of tokens to withdraw.\n     * @param claim If true, claims all rewards for the account from this contract and any linked extra reward\n     * contracts.\n     */\n    function withdraw(address account, uint256 amount, bool claim) external;\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account from this contract and any linked extra reward\n     * contracts.\n     * @dev If claimExtras is true, also claims all rewards from linked extra reward contracts.\n     * @param account The address of the account to claim rewards for.\n     * @param recipient The address to send the rewards to.\n     * @param claimExtras If true, claims rewards from linked extra reward contracts.\n     */\n    function getReward(address account, address recipient, bool claimExtras) external;\n\n    /**\n     * @notice Number of extra rewards currently registered\n     */\n    function extraRewardsLength() external view returns (uint256);\n\n    /**\n     * @notice Get the extra rewards array values\n     */\n    function extraRewards() external view returns (address[] memory);\n\n    /**\n     * @notice Get the rewarder at the specified index\n     */\n    function getExtraRewarder(uint256 index) external view returns (IExtraRewarder);\n}\n"},{"file_path":"src/vault/libs/AutopoolToken.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\nimport { ECDSA } from \"openzeppelin-contracts/utils/cryptography/ECDSA.sol\";\nimport { IERC20Permit } from \"openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\n\n/// @notice ERC20 token functionality converted into a library. Based on OZ's v5\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.1/contracts/token/ERC20/ERC20.sol\nlibrary AutopoolToken {\n    struct TokenData {\n        /// @notice Token balances\n        /// @dev account => balance\n        mapping(address => uint256) balances;\n        /// @notice Account spender allowances\n        /// @dev account => spender => allowance\n        mapping(address => mapping(address => uint256)) allowances;\n        /// @notice Total supply of the pool. Be careful when using this directly from the struct. The pool itself\n        /// modifies this number based on unlocked profited shares\n        uint256 totalSupply;\n        /// @notice ERC20 Permit nonces\n        /// @dev account -> nonce. Exposed via `nonces(owner)`\n        mapping(address => uint256) nonces;\n    }\n\n    /// @notice EIP2612 permit type hash\n    bytes32 public constant PERMIT_TYPEHASH =\n        keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n\n    /// @notice EIP712 domain type hash\n    bytes32 public constant TYPE_HASH =\n        keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n\n    /// @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    /// @param balance Current balance for the interacting account.\n    /// @param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n\n    /// @dev Indicates a failure with the token `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    error ERC20InvalidSender(address sender);\n\n    /// @dev Indicates a failure with the token `receiver`. Used in transfers.\n    /// @param receiver Address to which tokens are being transferred.\n    error ERC20InvalidReceiver(address receiver);\n\n    /// @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.\n    ///@param spender Address that may be allowed to operate on tokens without being their owner.\n    /// @param allowance Amount of tokens a `spender` is allowed to operate with.\n    ///@param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n    /// @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n    /// @param approver Address initiating an approval operation.\n    error ERC20InvalidApprover(address approver);\n\n    /// @dev Indicates a failure with the `spender` to be approved. Used in approvals.\n    /// @param spender Address that may be allowed to operate on tokens without being their owner.\n    error ERC20InvalidSpender(address spender);\n\n    /// @dev Permit deadline has expired.\n    error ERC2612ExpiredSignature(uint256 deadline);\n    /// @dev Mismatched signature.\n    error ERC2612InvalidSigner(address signer, address owner);\n    /// @dev The nonce used for an `account` is not the expected current nonce.\n    error InvalidAccountNonce(address account, uint256 currentNonce);\n\n    /// @dev Emitted when `value` tokens are moved from one account `from` to another `to`.\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /// @dev Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}.\n    /// `value` is the new allowance.\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /// @dev Sets a `value` amount of tokens as the allowance of `spender` over the caller's tokens.\n    function approve(TokenData storage data, address spender, uint256 value) external returns (bool) {\n        address owner = msg.sender;\n        approve(data, owner, spender, value);\n        return true;\n    }\n\n    /// @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.\n    function approve(TokenData storage data, address owner, address spender, uint256 value) public {\n        _approve(data, owner, spender, value, true);\n    }\n\n    function transfer(TokenData storage data, address to, uint256 value) external returns (bool) {\n        address owner = msg.sender;\n        _transfer(data, owner, to, value);\n        return true;\n    }\n\n    /// @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism.\n    /// value` is then deducted from the caller's allowance.\n    function transferFrom(TokenData storage data, address from, address to, uint256 value) external returns (bool) {\n        address spender = msg.sender;\n        _spendAllowance(data, from, spender, value);\n        _transfer(data, from, to, value);\n        return true;\n    }\n\n    /// @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\n    function mint(TokenData storage data, address account, uint256 value) external {\n        if (account == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(data, address(0), account, value);\n    }\n\n    /// @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\n    function burn(TokenData storage data, address account, uint256 value) external {\n        if (account == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        _update(data, account, address(0), value);\n    }\n\n    function permit(\n        TokenData storage data,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        if (block.timestamp > deadline) {\n            revert ERC2612ExpiredSignature(deadline);\n        }\n\n        uint256 nonce;\n        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be\n        // decremented or reset. This guarantees that the nonce never overflows.\n        unchecked {\n            // It is important to do x++ and not ++x here. Nonces starts at 0\n            nonce = data.nonces[owner]++;\n        }\n\n        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonce, deadline));\n\n        bytes32 hash = ECDSA.toTypedDataHash(IERC20Permit(address(this)).DOMAIN_SEPARATOR(), structHash);\n\n        address signer = ECDSA.recover(hash, v, r, s);\n        if (signer != owner) {\n            revert ERC2612InvalidSigner(signer, owner);\n        }\n\n        approve(data, owner, spender, value);\n    }\n\n    /// @dev Moves a `value` amount of tokens from `from` to `to`.\n    function _transfer(TokenData storage data, address from, address to, uint256 value) private {\n        if (from == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        if (to == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(data, from, to, value);\n    }\n\n    /// @dev Updates `owner` s allowance for `spender` based on spent `value`.\n    function _spendAllowance(TokenData storage data, address owner, address spender, uint256 value) private {\n        uint256 currentAllowance = data.allowances[owner][spender];\n        if (currentAllowance != type(uint256).max) {\n            if (currentAllowance < value) {\n                revert ERC20InsufficientAllowance(spender, currentAllowance, value);\n            }\n            unchecked {\n                _approve(data, owner, spender, currentAllowance - value, false);\n            }\n        }\n    }\n\n    /// @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\n    /// (or `to`) is the zero address.\n    function _update(TokenData storage data, address from, address to, uint256 value) private {\n        if (from == address(0)) {\n            // Overflow check required: The rest of the code assumes that totalSupply never overflows\n            data.totalSupply += value;\n        } else {\n            uint256 fromBalance = data.balances[from];\n            if (fromBalance < value) {\n                revert ERC20InsufficientBalance(from, fromBalance, value);\n            }\n            unchecked {\n                // Overflow not possible: value <= fromBalance <= totalSupply.\n                data.balances[from] = fromBalance - value;\n            }\n        }\n\n        if (to == address(0)) {\n            unchecked {\n                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\n                data.totalSupply -= value;\n            }\n        } else {\n            unchecked {\n                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\n                data.balances[to] += value;\n            }\n        }\n\n        emit Transfer(from, to, value);\n    }\n\n    /// @dev Variant of `_approve` with an optional flag to enable or disable the Approval event.\n    function _approve(TokenData storage data, address owner, address spender, uint256 value, bool emitEvent) private {\n        if (owner == address(0)) {\n            revert ERC20InvalidApprover(address(0));\n        }\n        if (spender == address(0)) {\n            revert ERC20InvalidSpender(address(0));\n        }\n        data.allowances[owner][spender] = value;\n        if (emitEvent) {\n            emit Approval(owner, spender, value);\n        }\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping(bytes32 => uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value's index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (lastIndex != toDeleteIndex) {\n                bytes32 lastValue = set._values[lastIndex];\n\n                // Move the last value to the index where the value to delete is\n                set._values[toDeleteIndex] = lastValue;\n                // Update the index for the moved value\n                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        bytes32[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol","source_code":"// 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"},{"file_path":"src/security/SecurityBase.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\n\ncontract SecurityBase {\n    IAccessController public immutable accessController;\n\n    error UndefinedAddress();\n\n    constructor(address _accessController) {\n        if (_accessController == address(0)) revert UndefinedAddress();\n\n        accessController = IAccessController(_accessController);\n    }\n\n    modifier onlyOwner() {\n        accessController.verifyOwner(msg.sender);\n        _;\n    }\n\n    modifier hasRole(bytes32 role) {\n        if (!accessController.hasRole(role, msg.sender)) revert Errors.AccessDenied();\n        _;\n    }\n\n    ///////////////////////////////////////////////////////////////////\n    //\n    //  Forward all the regular methods to central security module\n    //\n    ///////////////////////////////////////////////////////////////////\n\n    function _hasRole(bytes32 role, address account) internal view returns (bool) {\n        return accessController.hasRole(role, account);\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"},{"file_path":"src/interfaces/vault/IDestinationVaultRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IDestinationVaultFactory } from \"src/interfaces/vault/IDestinationVaultFactory.sol\";\n\n/// @notice Tracks valid Destination Vaults for the system\ninterface IDestinationVaultRegistry {\n    /// @notice Determines if a given address is a valid Destination Vault in the system\n    /// @param destinationVault address to check\n    /// @return True if vault is registered\n    function isRegistered(address destinationVault) external view returns (bool);\n\n    /// @notice Registers a new Destination Vault\n    /// @dev Should be locked down to only a factory\n    /// @param newDestinationVault Address of the new vault\n    function register(address newDestinationVault) external;\n\n    /// @notice Checks if an address is a valid Destination Vault and reverts if not\n    /// @param destinationVault Destination Vault address to checked\n    function verifyIsRegistered(address destinationVault) external view;\n\n    /// @notice Returns a list of all registered vaults\n    function listVaults() external view returns (address[] memory);\n\n    /// @notice Factory that is allowed to create and registry Destination Vaults\n    function factory() external view returns (IDestinationVaultFactory);\n}\n"},{"file_path":"src/strategy/WithdrawalQueue.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17; // their version was using 8.12?\n\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\n\n// https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/src/contracts/libraries/StructuredLinkedList.sol\nlibrary WithdrawalQueue {\n    using StructuredLinkedList for StructuredLinkedList.List;\n\n    error CannotInsertZeroAddress();\n    error UnexpectedNodeRemoved();\n    error AddToHeadFailed();\n    error AddToTailFailed();\n    error NodeDoesNotExist();\n\n    /// @notice Returns true if the address is in the queue.\n    function addressExists(StructuredLinkedList.List storage queue, address addr) public view returns (bool) {\n        return StructuredLinkedList.nodeExists(queue, _addressToUint(addr));\n    }\n\n    /// @notice Returns the current head.\n    function peekHead(StructuredLinkedList.List storage queue) public view returns (address) {\n        return _uintToAddress(StructuredLinkedList.getHead(queue));\n    }\n\n    /// @notice Returns the current tail.\n    function peekTail(StructuredLinkedList.List storage queue) public view returns (address) {\n        return _uintToAddress(StructuredLinkedList.getTail(queue));\n    }\n\n    /// @notice Returns the number of items in the queue\n    function sizeOf(StructuredLinkedList.List storage queue) public view returns (uint256) {\n        return StructuredLinkedList.sizeOf(queue);\n    }\n\n    /// @notice Return all items in the queue\n    /// @dev Enumerates from head to tail\n    function getList(StructuredLinkedList.List storage self) public view returns (address[] memory list) {\n        uint256 size = self.sizeOf();\n        list = new address[](size);\n\n        if (size > 0) {\n            uint256 lastNode = self.getHead();\n            list[0] = _uintToAddress(lastNode);\n            for (uint256 i = 1; i < size; ++i) {\n                (bool exists, uint256 node) = self.getAdjacent(lastNode, true);\n\n                if (!exists) {\n                    revert NodeDoesNotExist();\n                }\n\n                list[i] = _uintToAddress(node);\n                lastNode = node;\n            }\n        }\n    }\n\n    /// @notice Returns the current tail.\n    function popHead(StructuredLinkedList.List storage queue) public returns (address) {\n        return _uintToAddress(StructuredLinkedList.popFront(queue));\n    }\n\n    /// @notice remove address toRemove from queue if it exists.\n    function popAddress(StructuredLinkedList.List storage queue, address toRemove) public {\n        uint256 addrAsUint = _addressToUint(toRemove);\n        uint256 _removedNode = StructuredLinkedList.remove(queue, addrAsUint);\n        if (!((_removedNode == addrAsUint) || (_removedNode == 0))) {\n            revert UnexpectedNodeRemoved();\n        }\n    }\n\n    /// @notice returns true if there are no addresses in queue.\n    function isEmpty(StructuredLinkedList.List storage queue) public view returns (bool) {\n        return !StructuredLinkedList.listExists(queue);\n    }\n\n    /// @notice if addr in queue, move it to the top\n    // if addr not in queue, add it to the top of the queue.\n    // if queue is empty, make a new queue with addr as the only node\n    function addToHead(StructuredLinkedList.List storage queue, address addr) public {\n        if (addr == address(0)) {\n            revert CannotInsertZeroAddress();\n        }\n        popAddress(queue, addr);\n        bool success = StructuredLinkedList.pushFront(queue, _addressToUint(addr));\n        if (!success) {\n            revert AddToHeadFailed();\n        }\n    }\n\n    function getAdjacent(\n        StructuredLinkedList.List storage queue,\n        address addr,\n        bool direction\n    ) public view returns (address) {\n        (bool exists, uint256 addrNum) = queue.getAdjacent(_addressToUint(addr), direction);\n        if (!exists) {\n            return address(0);\n        }\n        return _uintToAddress(addrNum);\n    }\n\n    /// @notice if addr in queue, move it to the end\n    // if addr not in queue, add it to the end of the queue.\n    // if queue is empty, make a new queue with addr as the only node\n    function addToTail(StructuredLinkedList.List storage queue, address addr) public {\n        if (addr == address(0)) {\n            revert CannotInsertZeroAddress();\n        }\n\n        popAddress(queue, addr);\n        bool success = StructuredLinkedList.pushBack(queue, _addressToUint(addr));\n        if (!success) {\n            revert AddToTailFailed();\n        }\n    }\n\n    function _addressToUint(address addr) private pure returns (uint256) {\n        return uint256(uint160(addr));\n    }\n\n    function _uintToAddress(uint256 x) private pure returns (address) {\n        return address(uint160(x));\n    }\n}\n"},{"file_path":"src/interfaces/utils/IWETH9.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.7;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\ninterface IWETH9 is IERC20 {\n    function symbol() external view returns (string memory);\n\n    function deposit() external payable;\n    function withdraw(uint256 amount) external;\n}\n"},{"file_path":"src/interfaces/rewarders/IBaseRewarder.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\ninterface IBaseRewarder {\n    error RecoverDurationPending();\n\n    event RewardAdded(\n        uint256 reward,\n        uint256 rewardRate,\n        uint256 lastUpdateBlock,\n        uint256 periodInBlockFinish,\n        uint256 historicalRewards\n    );\n    event UserRewardUpdated(\n        address indexed user, uint256 amount, uint256 rewardPerTokenStored, uint256 lastUpdateBlock\n    );\n    event Staked(address indexed user, uint256 amount);\n    event Withdrawn(address indexed user, uint256 amount);\n    event RewardPaid(address indexed user, address indexed recipient, uint256 reward);\n    event QueuedRewardsUpdated(uint256 startingQueuedRewards, uint256 startingNewRewards, uint256 queuedRewards);\n    event AddedToWhitelist(address indexed wallet);\n    event RemovedFromWhitelist(address indexed wallet);\n\n    event TokeLockDurationUpdated(uint256 newDuration);\n\n    event Recovered(address token, address recipient, uint256 amount);\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account\n     */\n    function getReward() external;\n\n    /**\n     * @notice Stakes the specified amount of tokens for the specified account.\n     * @param account The address of the account to stake tokens for.\n     * @param amount The amount of tokens to stake.\n     */\n    function stake(address account, uint256 amount) external;\n\n    /**\n     * @notice Calculate the earned rewards for an account.\n     * @param account Address of the account.\n     * @return The earned rewards for the given account.\n     */\n    function earned(address account) external view returns (uint256);\n\n    /**\n     * @notice Calculates the rewards per token for the current block.\n     * @dev The total amount of rewards available in the system is fixed, and it needs to be distributed among the users\n     * based on their token balances and staking duration.\n     * Rewards per token represent the amount of rewards that each token is entitled to receive at the current block.\n     * The calculation takes into account the reward rate, the time duration since the last update,\n     * and the total supply of tokens in the staking pool.\n     * @return The updated rewards per token value for the current block.\n     */\n    function rewardPerToken() external view returns (uint256);\n\n    /**\n     * @notice Get the current reward rate per block.\n     * @return The current reward rate per block.\n     */\n    function rewardRate() external view returns (uint256);\n\n    /**\n     * @notice Get the current TOKE lock duration.\n     * @return The current TOKE lock duration.\n     */\n    function tokeLockDuration() external view returns (uint256);\n\n    /**\n     * @notice Get the last block where rewards are applicable.\n     * @return The last block number where rewards are applicable.\n     */\n    function lastBlockRewardApplicable() external view returns (uint256);\n\n    /**\n     * @notice The total amount of tokens staked\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @notice The amount of tokens staked for the specified account\n     * @param account The address of the account to get the balance of\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @notice Queue new rewards to be distributed.\n     * @param newRewards The amount of new rewards to be queued.\n     */\n    function queueNewRewards(uint256 newRewards) external;\n\n    /**\n     * @notice Token distributed as rewards\n     * @return reward token address\n     */\n    function rewardToken() external view returns (address);\n\n    /**\n     * @notice Add an address to the whitelist.\n     * @param wallet The address to be added to the whitelist.\n     */\n    function addToWhitelist(address wallet) external;\n\n    /**\n     * @notice Remove an address from the whitelist.\n     * @param wallet The address to be removed from the whitelist.\n     */\n    function removeFromWhitelist(address wallet) external;\n\n    /**\n     * @notice Recovers tokens from the rewarder. However, a recovery duration of 1 year is applicable for reward token\n     * @param token Address of token\n     * @param recipient recipient Address of recipient\n     */\n    function recover(address token, address recipient) external;\n\n    /**\n     * @notice Check if an address is whitelisted.\n     * @param wallet The address to be checked.\n     * @return bool indicating if the address is whitelisted.\n     */\n    function isWhitelisted(address wallet) external view returns (bool);\n}\n"},{"file_path":"src/interfaces/stats/IStatsCalculatorRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IStatsCalculator } from \"src/interfaces/stats/IStatsCalculator.sol\";\n\n/// @notice Track stat calculators for this instance of the system\ninterface IStatsCalculatorRegistry {\n    /// @notice Get a registered calculator\n    /// @dev Should revert if missing\n    /// @param aprId key of the calculator to get\n    /// @return calculator instance of the calculator\n    function getCalculator(bytes32 aprId) external view returns (IStatsCalculator calculator);\n\n    /// @notice List all calculator addresses registered\n    function listCalculators() external view returns (bytes32[] memory, address[] memory);\n\n    /// @notice Register a new stats calculator\n    /// @param calculator address of the calculator\n    function register(address calculator) external;\n\n    /// @notice Remove a stats calculator\n    /// @param aprId key of the calculator to remove\n    function removeCalculator(bytes32 aprId) external;\n\n    /// @notice Set the factory that can register calculators\n    /// @param factory address of the factory\n    function setCalculatorFactory(address factory) external;\n}\n"},{"file_path":"src/interfaces/rewarders/IExtraRewarder.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IBaseRewarder } from \"src/interfaces/rewarders/IBaseRewarder.sol\";\n\ninterface IExtraRewarder is IBaseRewarder {\n    /**\n     * @notice Withdraws the specified amount of tokens from the vault for the specified account.\n     * @param account The address of the account to withdraw tokens for.\n     * @param amount The amount of tokens to withdraw.\n     */\n    function withdraw(address account, uint256 amount) external;\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account from this contract.\n     * @param account The address of the account to claim rewards for.\n     * @param recipient The address to send the rewards to.\n     */\n    function getReward(address account, address recipient) external;\n}\n"},{"file_path":"src/interfaces/swapper/ISwapRouter.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISyncSwapper } from \"src/interfaces/swapper/ISyncSwapper.sol\";\n\ninterface ISwapRouter {\n    struct SwapData {\n        address token;\n        address pool;\n        ISyncSwapper swapper;\n        bytes data;\n    }\n\n    error MaxSlippageExceeded();\n    error SwapRouteLookupFailed(address from, address to);\n    error SwapFailed();\n\n    event SwapRouteSet(address indexed token, SwapData[] routes);\n    event SwapForQuoteSuccessful(\n        address indexed assetToken,\n        uint256 sellAmount,\n        address indexed quoteToken,\n        uint256 minBuyAmount,\n        uint256 buyAmount\n    );\n\n    /**\n     * @notice Sets a new swap route for a given asset token.\n     * @param assetToken The asset token for which the swap route is being set.\n     * @param _swapRoute The new swap route as an array of SwapData. The last element represents the quoteToken.\n     * @dev Each 'hop' in the swap route is validated using the respective swapper's validate function. The validate\n     * function ensures that the encoded data contains the correct 'fromAddress' and 'toAddress' (swapData.token), and\n     * verifies that these tokens are in the pool.\n     */\n    function setSwapRoute(address assetToken, SwapData[] calldata _swapRoute) external;\n\n    /**\n     * @notice Swaps the asset token for the quote token.\n     * @dev We're adopting an \"exact in, variable out\" model for all our swaps. This ensures that the entire sellAmount\n     * is used, eliminating the need for additional balance checks and refunds. This model is expected to be followed by\n     * all swapper implementations to maintain consistency and to optimize for gas efficiency.\n     * @param assetToken The address of the asset token to swap.\n     * @param sellAmount The exact amount of the asset token to swap.\n     * @param quoteToken The address of the quote token.\n     * @param minBuyAmount The minimum amount of the quote token expected to be received from the swap.\n     * @return The amount received from the swap.\n     */\n    function swapForQuote(\n        address assetToken,\n        uint256 sellAmount,\n        address quoteToken,\n        uint256 minBuyAmount\n    ) external returns (uint256);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC3156 FlashBorrower, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashBorrower {\n    /**\n     * @dev Receive a flash loan.\n     * @param initiator The initiator of the loan.\n     * @param token The loan currency.\n     * @param amount The amount of tokens lent.\n     * @param fee The additional amount of tokens to repay.\n     * @param data Arbitrary data structure, intended to contain user-defined parameters.\n     * @return The keccak256 hash of \"IERC3156FlashBorrower.onFlashLoan\"\n     */\n    function onFlashLoan(\n        address initiator,\n        address token,\n        uint256 amount,\n        uint256 fee,\n        bytes calldata data\n    ) external returns (bytes32);\n}\n"},{"file_path":"src/interfaces/staking/IAccToke.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\ninterface IAccToke {\n    ///////////////////////////////////////////////////////////////////\n    //                        Variables\n    ///////////////////////////////////////////////////////////////////\n\n    function startEpoch() external view returns (uint256);\n    function minStakeDuration() external view returns (uint256);\n\n    struct Lockup {\n        uint128 amount;\n        uint128 end;\n        uint256 points;\n    }\n\n    function getLockups(address user) external view returns (Lockup[] memory);\n    function toke() external view returns (IERC20Metadata);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Errors\n    ///////////////////////////////////////////////////////////////////\n\n    error ZeroAddress();\n    error StakingDurationTooShort();\n    error StakingDurationTooLong();\n    error StakingPointsExceeded();\n    error IncorrectStakingAmount();\n    error InsufficientFunds();\n    error LockupDoesNotExist();\n    error NotUnlockableYet();\n    error AlreadyUnlocked();\n    error ExtendDurationTooShort();\n    error TransfersDisabled();\n    error TransferFailed();\n    error NoRewardsToClaim();\n    error InsufficientAmount();\n    error InvalidLockupIds();\n    error InvalidDurationLength();\n    error InvalidMinStakeDuration();\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Events\n    ///////////////////////////////////////////////////////////////////\n    event SetMaxStakeDuration(uint256 oldDuration, uint256 newDuration);\n    event Stake(address indexed user, uint256 lockupId, uint256 amount, uint256 end, uint256 points);\n    event Unstake(address indexed user, uint256 lockupId, uint256 amount, uint256 end, uint256 points);\n    event Extend(\n        address indexed user,\n        uint256 lockupId,\n        uint256 amount,\n        uint256 oldEnd,\n        uint256 newEnd,\n        uint256 oldPoints,\n        uint256 newPoints\n    );\n    event RewardsAdded(uint256 amount, uint256 accRewardPerShare);\n    event RewardsCollected(address indexed user, uint256 amount);\n    event RewardsClaimed(address indexed user, uint256 amount);\n\n    ///////////////////////////////////////////////////////////////////\n    //\n    //                        Staking Methods\n    //\n    ///////////////////////////////////////////////////////////////////\n\n    /**\n     * @notice Stake TOKE to an address that may not be the same as the sender of the funds. This can be used to give\n     * staked funds to someone else.\n     *\n     * If staking before the start of staking (epoch), then the lockup start and end dates are shifted forward so that\n     * the lockup starts at the epoch.\n     *\n     * @param amount TOKE to lockup in the stake\n     * @param duration in seconds for the stake\n     * @param to address to receive ownership of the stake\n     */\n    function stake(uint256 amount, uint256 duration, address to) external;\n\n    /**\n     * @notice Stake TOKE\n     *\n     * If staking before the start of staking (epoch), then the lockup start and end dates are shifted forward so that\n     * the lockup starts at the epoch.\n     *\n     * @notice Stake TOKE for myself.\n     * @param amount TOKE to lockup in the stake\n     * @param duration in seconds for the stake\n     */\n    function stake(uint256 amount, uint256 duration) external;\n\n    /**\n     * @notice Collect staked TOKE for a lockup and any earned rewards.\n     * @param lockupIds the id of the lockup to unstake\n     */\n    function unstake(uint256[] memory lockupIds) external;\n\n    /**\n     * @notice Extend a stake lockup for additional points.\n     *\n     * The stake end time is computed from the current time + duration, just like it is for new stakes. So a new stake\n     * for seven days duration and an old stake extended with a seven days duration would have the same end.\n     *\n     * If an extend is made before the start of staking, the start time for the new stake is shifted forwards to the\n     * start of staking, which also shifts forward the end date.\n     *\n     * @param lockupIds the id of the old lockup to extend\n     * @param durations number of seconds from now to stake for\n     */\n    function extend(uint256[] memory lockupIds, uint256[] memory durations) external;\n\n    ///////////////////////////////////////////////////////////////////\n    //\n    //                        Rewards\n    //\n    ///////////////////////////////////////////////////////////////////\n\n    /// @notice The total amount of rewards earned for all stakes\n    function totalRewardsEarned() external returns (uint256);\n\n    /// @notice Total rewards claimed by all stakers\n    function totalRewardsClaimed() external returns (uint256);\n\n    /// @notice Rewards claimed by a specific wallet\n    /// @param user Address of the wallet to check\n    function rewardsClaimed(address user) external returns (uint256);\n\n    /**\n     * @notice Preview the number of points that would be returned for the\n     * given amount and duration.\n     *\n     * @param amount TOKE to be staked\n     * @param duration number of seconds to stake for\n     * @return points staking points that would be returned\n     * @return end staking period end date\n     */\n    function previewPoints(uint256 amount, uint256 duration) external view returns (uint256, uint256);\n\n    /// @notice Preview the reward amount a caller can claim\n    function previewRewards() external view returns (uint256);\n\n    /// @notice Preview the reward amount a specified wallet can claim\n    function previewRewards(address user) external view returns (uint256);\n\n    /// @notice Claim rewards for the caller\n    function collectRewards() external returns (uint256);\n\n    /// @notice Check if amount can be staked\n    function isStakeableAmount(uint256 amount) external pure returns (bool);\n}\n"},{"file_path":"src/interfaces/stats/ILSTStats.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Return stats on base LSTs\ninterface ILSTStats {\n    struct LSTStatsData {\n        uint256 lastSnapshotTimestamp;\n        uint256 baseApr;\n        int256 discount; // positive number is a discount, negative is a premium\n        uint24[10] discountHistory; // 7 decimal precision\n        uint40 discountTimestampByPercent; // timestamp that the token reached 1pct discount\n    }\n\n    /// @notice Get the current stats for the LST\n    /// @dev Returned data is a combination of current data and filtered snapshots\n    /// @return lstStatsData current data on the LST\n    function current() external returns (LSTStatsData memory lstStatsData);\n\n    /// @notice Get the EthPerToken (or Share) for the LST\n    /// @return ethPerShare the backing eth for the LST\n    function calculateEthPerToken() external view returns (uint256 ethPerShare);\n\n    /// @notice Get if the underlying LST token is rebasing\n    /// @return rebasing is true if the lst is a rebasing token\n    function isRebasing() external view returns (bool rebasing);\n}\n"},{"file_path":"src/interfaces/oracles/ISpotPriceOracle.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\n/// @notice An oracle that can provide spot prices for tokens using specific liquidity pools\ninterface ISpotPriceOracle {\n    /**\n     * @notice Retrieve the spot price for a token in a specified quote currency utilizing a specific liquidity pool\n     * @dev The function will attempt to retrieve the price in the requested quote currency,\n     *      but might return a price in an alternative quote currency if the requested one isn't available.\n     *      It's the caller's responsibility to handle the potentially different returned quote currency.\n     * @param token The token to get the spot price of\n     * @param pool The liquidity pool to use for the price retrieval\n     * @param requestedQuoteToken The desired quote token (e.g. WETH) for the returned price\n     * @return price The spot price of the token\n     * @return actualQuoteToken The actual quote token used for the returned price\n     */\n    function getSpotPrice(\n        address token,\n        address pool,\n        address requestedQuoteToken\n    ) external returns (uint256 price, address actualQuoteToken);\n\n    /**\n     * @notice In memory struct for gathering all the info required for reserves for a safe spot price calculation\n     *\n     * @param token Pool token\n     * @param reserveAmount amount of the token in the pool\n     * @param rawSpotPrice Price of the token in the pool\n     * @param actualQuoteToken Actual quote token used for the rawSpotPrice\n     */\n    struct ReserveItemInfo {\n        address token;\n        uint256 reserveAmount;\n        uint256 rawSpotPrice;\n        address actualQuoteToken;\n    }\n\n    /**\n     * @notice Retrieve the total supply of the LP token for the specified pool\n     *\n     * @param pool The liquidity pool to use for the check\n     * @param lpToken LP token of the specified pool\n     * @param quoteToken The desired quote token (e.g. WETH) for the returned price\n     * @return totalLPSupply Total supply of the LP token\n     * @return reserves `ReserveItemInfo` struct containing the reserves info\n     */\n    function getSafeSpotPriceInfo(\n        address pool,\n        address lpToken,\n        address quoteToken\n    ) external returns (uint256 totalLPSupply, ReserveItemInfo[] memory reserves);\n\n    /**\n     * @notice Retrieve the description of the oracle\n     * @return description\n     */\n    function getDescription() external view returns (string memory);\n}\n"},{"file_path":"src/interfaces/vault/IAutopilotRouter.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IAutopilotRouterBase } from \"src/interfaces/vault/IAutopilotRouterBase.sol\";\nimport { IRewards } from \"src/interfaces/rewarders/IRewards.sol\";\nimport { SwapParams } from \"src/interfaces/liquidation/IAsyncSwapper.sol\";\n\n/**\n * @title IAutopilotRouter Interface\n * @notice Extends the IAutopilotRouterBase with specific flows to save gas\n */\ninterface IAutopilotRouter is IAutopilotRouterBase {\n    /**\n     * ***************************   Deposit ********************************\n     */\n\n    /**\n     * @notice deposit available asset balance to a AutopoolETH.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function depositBalance(\n        IAutopool vault,\n        address to,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice deposit max assets to a AutopoolETH.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function depositMax(\n        IAutopool vault,\n        address to,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * *************************   Withdraw   **********************************\n     */\n\n    /**\n     * @notice withdraw `amount` to a AutopoolETH.\n     * @param fromVault The AutopoolETH to withdraw assets from.\n     * @param toVault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param amount The amount of assets to withdraw from fromVault.\n     * @param maxSharesIn The max amount of fromVault shares withdrawn by caller.\n     * @param minSharesOut The min amount of toVault shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MaxSharesError, MinSharesError\n     */\n    function withdrawToDeposit(\n        IAutopool fromVault,\n        IAutopool toVault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesIn,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * *************************   Redeem    ********************************\n     */\n\n    /**\n     * @notice redeem `shares` to a AutopoolETH.\n     * @param fromVault The AutopoolETH to redeem shares from.\n     * @param toVault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param shares The amount of shares to redeem from fromVault.\n     * @param minSharesOut The min amount of toVault shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinAmountError, MinSharesError\n     */\n    function redeemToDeposit(\n        IAutopool fromVault,\n        IAutopool toVault,\n        address to,\n        uint256 shares,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem max shares to a AutopoolETH.\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param to The destination of assets.\n     * @param minAmountOut The min amount of assets received by `to`.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeemMax(\n        IAutopool vault,\n        address to,\n        uint256 minAmountOut\n    ) external payable returns (uint256 amountOut);\n\n    /**\n     * @notice swaps token\n     * @param swapper Address of the swapper to use\n     * @param swapParams  Parameters for the swap\n     * @return amountReceived Swap output amount\n     */\n    function swapToken(\n        address swapper,\n        SwapParams memory swapParams\n    ) external payable returns (uint256 amountReceived);\n\n    /**\n     * @notice claims vault token rewards\n     * @param rewarder Address of the rewarder to claim from\n     * @param recipient Struct containing recipient details\n     * @return amountReceived Swap output amount\n     */\n    function claimRewards(\n        IRewards rewarder,\n        IRewards.Recipient calldata recipient,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external payable returns (uint256);\n\n    /**\n     * @notice swaps Exact token balance in the contract\n     * @param swapper Address of the swapper to use\n     * @param swapParams  Parameters for the swap\n     * @return amountReceived Swap output amount\n     * @dev sets the sellAmount to the balance of the contract\n     */\n    function swapTokenBalance(\n        address swapper,\n        SwapParams memory swapParams\n    ) external payable returns (uint256 amountReceived);\n}\n"},{"file_path":"src/interfaces/stats/IDexLSTStats.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ILSTStats } from \"src/interfaces/stats/ILSTStats.sol\";\n\n/// @title Return stats DEXs with LSTs\ninterface IDexLSTStats {\n    event DexSnapshotTaken(uint256 snapshotTimestamp, uint256 priorFeeApr, uint256 newFeeApr, uint256 unfilteredFeeApr);\n\n    struct StakingIncentiveStats {\n        // time-weighted average total supply to prevent spikes/attacks from impacting rebalancing\n        uint256 safeTotalSupply;\n        // rewardTokens, annualizedRewardAmounts, and periodFinishForRewards will match indexes\n        // they are split to workaround an issue with forge having nested structs\n        // address of the reward tokens\n        address[] rewardTokens;\n        // the annualized reward rate for the reward token\n        uint256[] annualizedRewardAmounts;\n        // the timestamp for when the rewards are set to terminate\n        uint40[] periodFinishForRewards;\n        // incentive rewards score. max 48, min 0\n        uint8 incentiveCredits;\n    }\n\n    struct DexLSTStatsData {\n        uint256 lastSnapshotTimestamp;\n        uint256 feeApr;\n        uint256[] reservesInEth;\n        StakingIncentiveStats stakingIncentiveStats;\n        ILSTStats.LSTStatsData[] lstStatsData;\n    }\n\n    /// @notice Get the current stats for the DEX with underlying LST tokens\n    /// @dev Returned data is a combination of current data and filtered snapshots\n    /// @return dexLSTStatsData current data on the DEX\n    function current() external returns (DexLSTStatsData memory dexLSTStatsData);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/Strings.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n}\n"},{"file_path":"src/interfaces/utils/ICurveResolver.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\ninterface ICurveResolver {\n    /// @notice Resolve details of a Curve pool regardless of type or version\n    /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools.\n    /// @param poolAddress pool address to lookup\n    /// @return tokens tokens that make up the pool\n    /// @return numTokens the number of tokens. tokens are not unwrapped.\n    /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap\n    function resolve(address poolAddress)\n        external\n        view\n        returns (address[8] memory tokens, uint256 numTokens, bool isStableSwap);\n\n    /// @notice Resolve details of a Curve pool regardless of type or version\n    /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools.\n    /// @dev Use the isStableSwap value to differentiate between StableSwap (V1) and CryptoSwap (V2) pools.\n    /// @param poolAddress pool address to lookup\n    /// @return tokens tokens that make up the pool\n    /// @return numTokens the number of tokens. tokens are not unwrapped\n    /// @return lpToken lp token of the pool\n    /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap\n    function resolveWithLpToken(address poolAddress)\n        external\n        view\n        returns (address[8] memory tokens, uint256 numTokens, address lpToken, bool isStableSwap);\n\n    /// @notice Get the lp token of a Curve pool\n    /// @param poolAddress pool address to lookup\n    function getLpToken(address poolAddress) external view returns (address);\n\n    /// @notice Get the reserves of a Curve pools' tokens\n    /// @dev Actual balances length might differ from 8 and should be verified by the caller\n    /// @param poolAddress pool address to lookup\n    /// @return balances reserves of the pool tokens\n    function getReservesInfo(address poolAddress) external view returns (uint256[8] memory balances);\n}\n"},{"file_path":"src/interfaces/vault/IAutopool.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { IERC4626 } from \"src/interfaces/vault/IERC4626.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { IAutopoolStrategy } from \"src/interfaces/strategy/IAutopoolStrategy.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20Permit } from \"openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\n\ninterface IAutopool is IERC4626, IERC20Permit {\n    enum VaultShutdownStatus {\n        Active,\n        Deprecated,\n        Exploit\n    }\n\n    /// @param unlockPeriodInSeconds Time it takes for profit to unlock in seconds\n    /// @param fullProfitUnlockTime Time at which all profit will have been unlocked\n    /// @param lastProfitUnlockTime Last time profits were unlocked\n    /// @param profitUnlockRate Per second rate at which profit shares unlocks. Rate when calculated is denominated in\n    /// MAX_BPS_PROFIT. TODO: Get into uint112\n    struct ProfitUnlockSettings {\n        uint48 unlockPeriodInSeconds;\n        uint48 fullProfitUnlockTime;\n        uint48 lastProfitUnlockTime;\n        uint256 profitUnlockRate;\n    }\n\n    /// @param feeSink Where claimed fees are sent\n    /// @param totalAssetsHighMark The last totalAssets amount we took fees at\n    /// @param totalAssetsHighMarkTimestamp The last timestamp we updated the high water mark\n    /// @param lastPeriodicFeeTake Timestamp of when the last periodic fee was taken.\n    /// @param periodicFeeSink Address that receives periodic fee.\n    /// @param periodicFeeBps Current periodic fee.  100% == 10000.\n    /// @param streamingFeeBps Current streaming fee taken on profit. 100% == 10000\n    /// @param navPerShareLastFeeMark The last nav/share height we took fees at\n    /// @param navPerShareLastFeeMarkTimestamp The last timestamp we took fees at\n    /// @param rebalanceFeeHighWaterMarkEnabled Returns whether the nav/share high water mark is enabled for the\n    /// rebalance fee\n    struct AutopoolFeeSettings {\n        address feeSink;\n        uint256 totalAssetsHighMark;\n        uint256 totalAssetsHighMarkTimestamp;\n        uint256 lastPeriodicFeeTake;\n        address periodicFeeSink;\n        uint256 periodicFeeBps;\n        uint256 streamingFeeBps;\n        uint256 navPerShareLastFeeMark;\n        uint256 navPerShareLastFeeMarkTimestamp;\n        bool rebalanceFeeHighWaterMarkEnabled;\n    }\n\n    /// @param totalIdle The amount of baseAsset deposited into the contract pending deployment\n    /// @param totalDebt The current (though cached) value of assets we've deployed\n    /// @param totalDebtMin The current (though cached) value of assets we use for valuing during deposits\n    /// @param totalDebtMax The current (though cached) value of assets we use for valuing during withdrawals\n    struct AssetBreakdown {\n        uint256 totalIdle;\n        uint256 totalDebt;\n        uint256 totalDebtMin;\n        uint256 totalDebtMax;\n    }\n\n    enum TotalAssetPurpose {\n        Global,\n        Deposit,\n        Withdraw\n    }\n\n    /* ******************************** */\n    /*      Events                      */\n    /* ******************************** */\n    event TokensPulled(address[] tokens, uint256[] amounts, address[] destinations);\n    event TokensRecovered(address[] tokens, uint256[] amounts, address[] destinations);\n    event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    event RewarderSet(address newRewarder, address oldRewarder);\n    event DestinationDebtReporting(address destination, uint256 debtValue, uint256 claimed, uint256 claimGasUsed);\n    event FeeCollected(uint256 fees, address feeSink, uint256 mintedShares, uint256 profit, uint256 idle, uint256 debt);\n    event PeriodicFeeCollected(uint256 fees, address feeSink, uint256 mintedShares);\n    event Shutdown(VaultShutdownStatus reason);\n\n    /* ******************************** */\n    /*      Errors                      */\n    /* ******************************** */\n\n    error ERC4626MintExceedsMax(uint256 shares, uint256 maxMint);\n    error ERC4626DepositExceedsMax(uint256 assets, uint256 maxDeposit);\n    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);\n    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);\n    error InvalidShutdownStatus(VaultShutdownStatus status);\n\n    error WithdrawalFailed();\n    error DepositFailed();\n    error InsufficientFundsInDestinations(uint256 deficit);\n    error WithdrawalIncomplete();\n    error ValueSharesMismatch(uint256 value, uint256 shares);\n\n    /// @notice A full unit of this pool\n    // solhint-disable-next-line func-name-mixedcase\n    function ONE() external view returns (uint256);\n\n    /// @notice Query the type of vault\n    function vaultType() external view returns (bytes32);\n\n    /// @notice Strategy governing the pools rebalances\n    function autoPoolStrategy() external view returns (IAutopoolStrategy);\n\n    /// @notice Allow token recoverer to collect dust / unintended transfers (non-tracked assets only)\n    function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external;\n\n    /// @notice Set the order of destination vaults used for withdrawals\n    // NOTE: will be done going directly to strategy (IStrategy) vault points to.\n    //       How it'll delegate is still being decided\n    // function setWithdrawalQueue(address[] calldata destinations) external;\n\n    /// @notice Get a list of destination vaults with pending assets to clear out\n    function getRemovalQueue() external view returns (address[] memory);\n\n    function getFeeSettings() external view returns (AutopoolFeeSettings memory);\n\n    /// @notice Initiate the shutdown procedures for this vault\n    function shutdown(VaultShutdownStatus reason) external;\n\n    /// @notice True if the vault has been shutdown\n    function isShutdown() external view returns (bool);\n\n    /// @notice Returns the reason for shutdown (or `Active` if not shutdown)\n    function shutdownStatus() external view returns (VaultShutdownStatus);\n\n    /// @notice gets the list of supported destination vaults for the Autopool/Strategy\n    /// @return _destinations List of supported destination vaults\n    function getDestinations() external view returns (address[] memory _destinations);\n\n    function convertToShares(\n        uint256 assets,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) external view returns (uint256 shares);\n\n    function convertToAssets(\n        uint256 shares,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) external view returns (uint256 assets);\n\n    function totalAssets(TotalAssetPurpose purpose) external view returns (uint256);\n\n    function getAssetBreakdown() external view returns (AssetBreakdown memory);\n\n    /// @notice get a destinations last reported debt value\n    /// @param destVault the address of the target destination\n    /// @return destinations last reported debt value\n    function getDestinationInfo(address destVault) external view returns (AutopoolDebt.DestinationInfo memory);\n\n    /// @notice check if a destination is registered with the vault\n    function isDestinationRegistered(address destination) external view returns (bool);\n\n    /// @notice get if a destinationVault is queued for removal by the AutopoolETH\n    function isDestinationQueuedForRemoval(address destination) external view returns (bool);\n\n    /// @notice Returns instance of vault rewarder.\n    function rewarder() external view returns (IMainRewarder);\n\n    /// @notice Returns all past rewarders.\n    function getPastRewarders() external view returns (address[] memory _pastRewarders);\n\n    /// @notice Returns boolean telling whether address passed in is past rewarder.\n    function isPastRewarder(address _pastRewarder) external view returns (bool);\n}\n"},{"file_path":"src/interfaces/vault/IAutopoolRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Keep track of Vaults created through the Vault Factory\ninterface IAutopoolRegistry {\n    ///////////////////////////////////////////////////////////////////\n    //                        Errors\n    ///////////////////////////////////////////////////////////////////\n\n    error VaultNotFound(address vaultAddress);\n    error VaultAlreadyExists(address vaultAddress);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Events\n    ///////////////////////////////////////////////////////////////////\n    event VaultAdded(address indexed asset, address indexed vault);\n    event VaultRemoved(address indexed asset, address indexed vault);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Functions\n    ///////////////////////////////////////////////////////////////////\n\n    /// @notice Checks if an address is a valid vault\n    /// @param vaultAddress Vault address to be added\n    function isVault(address vaultAddress) external view returns (bool);\n\n    /// @notice Registers a vault\n    /// @param vaultAddress Vault address to be added\n    function addVault(address vaultAddress) external;\n\n    /// @notice Removes vault registration\n    /// @param vaultAddress Vault address to be removed\n    function removeVault(address vaultAddress) external;\n\n    /// @notice Returns a list of all registered vaults\n    function listVaults() external view returns (address[] memory);\n\n    /// @notice Returns a list of all registered vaults for a given asset\n    /// @param asset Asset address\n    function listVaultsForAsset(address asset) external view returns (address[] memory);\n\n    /// @notice Returns a list of all registered vaults for a given type\n    /// @param _vaultType Vault type\n    function listVaultsForType(bytes32 _vaultType) external view returns (address[] memory);\n}\n"},{"file_path":"src/interfaces/swapper/ISyncSwapper.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\n\ninterface ISyncSwapper {\n    error DataMismatch(string element);\n    error InvalidIndex();\n\n    /**\n     * @notice Returns address of swap router that can access SyncSwapper contract\n     */\n    function router() external view returns (ISwapRouter);\n\n    /**\n     * @notice Swaps sellToken for buyToken\n     * @param pool The address of the pool for the swapper\n     * @param sellTokenAddress The address of the token to sell\n     * @param sellAmount The amount of sellToken to sell\n     * @param buyTokenAddress The address of the token to buy\n     * @param minBuyAmount The minimum amount of buyToken expected\n     * @param data Additional data used differently by the different swappers\n     * @return actualBuyAmount The actual amount received from the swap\n     */\n    function swap(\n        address pool,\n        address sellTokenAddress,\n        uint256 sellAmount,\n        address buyTokenAddress,\n        uint256 minBuyAmount,\n        bytes memory data\n    ) external returns (uint256 actualBuyAmount);\n\n    /**\n     * @notice Validates that the swapData contains the correct information, ensuring that the encoded data contains the\n     * correct 'fromAddress' and 'toAddress' (swapData.token), and verifies that these tokens are in the pool\n     * @dev This function should revert with a DataMismatch error if the swapData is invalid\n     * @param fromAddress The address from which the swap originates\n     * @param swapData The data associated with the swap that needs to be validated\n     */\n    function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view;\n}\n"},{"file_path":"src/interfaces/vault/IERC4626.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n/// @dev Interface of the ERC4626 \"Tokenized Vault Standard\", as defined in https://eips.ethereum.org/EIPS/eip-4626\n/// @dev Due to the nature of obtaining estimates for previewing withdraws and redeems, a few functions are not\n///     view and therefore do not conform to eip 4626.  These functions use state changing operations\n///     to get accurate estimates, reverting after the preview amounts have been obtained.\ninterface IERC4626 is IERC20Metadata {\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares\n    );\n\n    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and\n    /// withdrawing.\n    /// @dev\n    /// - MUST be an ERC-20 token contract.\n    /// - MUST NOT revert.\n    function asset() external view returns (address assetTokenAddress);\n\n    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.\n    /// @dev\n    /// - SHOULD include any compounding that occurs from yield.\n    /// - MUST be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT revert.\n    function totalAssets() external view returns (uint256 totalManagedAssets);\n\n    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    /// @dev\n    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT show any variations depending on the caller.\n    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n    /// from.\n    function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    /// @dev\n    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT show any variations depending on the caller.\n    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n    /// from.\n    function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n    /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the\n    /// receiver,\n    /// through a deposit call.\n    /// @dev\n    /// - MUST return a limited value if receiver is subject to some deposit limit.\n    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n    /// - MUST NOT revert.\n    function maxDeposit(address receiver) external returns (uint256 maxAssets);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block,\n    /// given\n    /// current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n    ///   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n    ///   in the same transaction.\n    /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n    ///   deposit would be accepted, regardless if the user has enough tokens approved, etc.\n    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.\n    function previewDeposit(uint256 assets) external returns (uint256 shares);\n\n    /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n    /// @dev\n    /// - MUST emit the Deposit event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   deposit execution, and are accounted for during deposit.\n    /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n    ///   approving enough underlying tokens to the Vault contract, etc).\n    ///\n    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n    function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n    /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n    /// @dev\n    /// - MUST return a limited value if receiver is subject to some mint limit.\n    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n    /// - MUST NOT revert.\n    function maxMint(address receiver) external returns (uint256 maxShares);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n    /// current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n    ///   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n    ///   same transaction.\n    /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n    ///   would be accepted, regardless if the user has enough tokens approved, etc.\n    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by minting.\n    function previewMint(uint256 shares) external returns (uint256 assets);\n\n    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n    /// @dev\n    /// - MUST emit the Deposit event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n    ///   execution, and are accounted for during mint.\n    /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n    ///   approving enough underlying tokens to the Vault contract, etc).\n    ///\n    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n    function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n    /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n    /// Vault, through a withdraw call.\n    /// @dev\n    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n    /// - MUST NOT revert.\n    function maxWithdraw(address owner) external returns (uint256 maxAssets);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n    /// given current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n    ///   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n    ///   called\n    ///   in the same transaction.\n    /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n    ///   the withdrawal would be accepted, regardless if the user has enough shares, etc.\n    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.\n    function previewWithdraw(uint256 assets) external returns (uint256 shares);\n\n    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n    /// @dev\n    /// - MUST emit the Withdraw event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   withdraw execution, and are accounted for during withdraw.\n    /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n    ///   not having enough shares, etc).\n    ///\n    /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n    /// Those methods should be performed separately.\n    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);\n\n    /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n    /// through a redeem call.\n    /// @dev\n    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n    /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n    /// - MUST NOT revert.\n    function maxRedeem(address owner) external returns (uint256 maxShares);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n    /// given current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n    ///   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n    ///   same transaction.\n    /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n    ///   redemption would be accepted, regardless if the user has enough shares, etc.\n    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n    function previewRedeem(uint256 shares) external returns (uint256 assets);\n\n    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n    /// @dev\n    /// - MUST emit the Withdraw event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   redeem execution, and are accounted for during redeem.\n    /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n    ///   not having enough shares, etc).\n    ///\n    /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n    /// Those methods should be performed separately.\n    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);\n}\n"},{"file_path":"src/interfaces/destinations/IDestinationAdapter.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\n/**\n * @title IDestinationAdapter\n * @dev This is a super-interface to unify different types of adapters to be registered in Destination Registry.\n *      Specific interface type is defined by extending from this interface.\n */\ninterface IDestinationAdapter {\n    error MustBeMoreThanZero();\n    error ArraysLengthMismatch();\n    error BalanceMustIncrease();\n    error MinLpAmountNotReached();\n    error LpTokenAmountMismatch();\n    error NoNonZeroAmountProvided();\n    error InvalidBalanceChange();\n    error InvalidAddress(address);\n}\n"},{"file_path":"src/interfaces/stats/IIncentivesPricingStats.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title EWMA pricing for incentive tokens\ninterface IIncentivesPricingStats {\n    event TokenAdded(address indexed token);\n    event TokenRemoved(address indexed token);\n    event TokenSnapshot(\n        address indexed token,\n        uint40 lastSnapshot,\n        uint256 fastFilterPrice,\n        uint256 slowFilterPrice,\n        uint256 initCount,\n        bool initComplete\n    );\n\n    error TokenAlreadyRegistered(address token);\n    error TokenNotFound(address token);\n    error IncentiveTokenPriceStale(address token);\n    error TokenSnapshotNotReady(address token);\n\n    struct TokenSnapshotInfo {\n        uint40 lastSnapshot;\n        bool _initComplete;\n        uint8 _initCount;\n        uint256 _initAcc;\n        uint256 fastFilterPrice;\n        uint256 slowFilterPrice;\n    }\n\n    /// @notice add a token to snapshot\n    /// @dev the token must be configured in the RootPriceOracle before adding here\n    /// @param token the address of the token to add\n    function setRegisteredToken(address token) external;\n\n    /// @notice remove a token from being snapshot\n    /// @param token the address of the token to remove\n    function removeRegisteredToken(address token) external;\n\n    /// @notice get the addresses for all currently registered tokens\n    /// @return tokens all of the registered token addresses\n    function getRegisteredTokens() external view returns (address[] memory tokens);\n\n    /// @notice get all of the registered tokens with the latest snapshot info\n    /// @return tokenAddresses token addresses in the same order as info\n    /// @return info a list of snapshot info for the tokens\n    function getTokenPricingInfo()\n        external\n        view\n        returns (address[] memory tokenAddresses, TokenSnapshotInfo[] memory info);\n\n    /// @notice update the snapshot for the specified tokens\n    /// @dev if a token is not ready to be snapshot the entire call will fail\n    function snapshot(address[] calldata tokensToSnapshot) external;\n\n    /// @notice get the latest prices for an incentive token. Reverts if token is not registered\n    /// @return fastPrice the price based on the faster filter (weighted toward current prices)\n    /// @return slowPrice the price based on the slower filter (weighted toward older prices, relative to fast)\n    function getPrice(address token, uint40 staleCheck) external view returns (uint256 fastPrice, uint256 slowPrice);\n\n    /// @notice get the latest prices for an incentive token or zero if the token is not registered\n    /// @return fastPrice the price based on the faster filter (weighted toward current prices)\n    /// @return slowPrice the price based on the slower filter (weighted toward older prices, relative to fast)\n    function getPriceOrZero(\n        address token,\n        uint40 staleCheck\n    ) external view returns (uint256 fastPrice, uint256 slowPrice);\n}\n"},{"file_path":"src/interfaces/vault/IBaseAssetVault.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\ninterface IBaseAssetVault {\n    /// @notice Asset that this Vault primarily manages\n    /// @dev Vault decimals should be the same as the baseAsset\n    function baseAsset() external view returns (address);\n}\n"},{"file_path":"src/interfaces/vault/IAutopilotRouterBase.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity >=0.8.7;\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title AutopoolETH Router Base Interface\n * @notice A canonical router between AutopoolETHs\n *\n * The base router is a multicall style router inspired by Uniswap v3 with built-in features for permit,\n * WETH9 wrap/unwrap, and ERC20 token pulling/sweeping/approving. It includes methods for the four mutable\n * ERC4626 functions deposit/mint/withdraw/redeem as well.\n *\n * These can all be arbitrarily composed using the multicall functionality of the router.\n *\n * NOTE the router is capable of pulling any approved token from your wallet. This is only possible when\n * your address is msg.sender, but regardless be careful when interacting with the router or ERC4626 Vaults.\n * The router makes no special considerations for unique ERC20 implementations such as fee on transfer.\n * There are no built in protections for unexpected behavior beyond enforcing the minSharesOut is received.\n */\ninterface IAutopilotRouterBase {\n    /// @notice thrown when amount of assets received is below the min set by caller\n    error MinAmountError();\n\n    /// @notice thrown when amount of shares received is below the min set by caller\n    error MinSharesError();\n\n    /// @notice thrown when amount of assets received is above the max set by caller\n    error MaxAmountError();\n\n    /// @notice thrown when amount of shares received is above the max set by caller\n    error MaxSharesError();\n\n    /// @notice thrown when timestamp is too old\n    error TimestampTooOld();\n\n    /**\n     * @notice mint `shares` from an ERC4626 vault.\n     * @param vault The AutopoolETH to mint shares from.\n     * @param to The destination of ownership shares.\n     * @param shares The amount of shares to mint from `vault`.\n     * @param maxAmountIn The max amount of assets used to mint.\n     * @return amountIn the amount of assets used to mint by `to`.\n     * @dev throws MaxAmountError\n     */\n    function mint(\n        IAutopool vault,\n        address to,\n        uint256 shares,\n        uint256 maxAmountIn\n    ) external payable returns (uint256 amountIn);\n\n    /**\n     * @notice deposit `amount` to an ERC4626 vault.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param amount The amount of assets to deposit to `vault`.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function deposit(\n        IAutopool vault,\n        address to,\n        uint256 amount,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice withdraw `amount` from an ERC4626 vault.\n     * @param vault The AutopoolETH to withdraw assets from.\n     * @param to The destination of assets.\n     * @param amount The amount of assets to withdraw from vault.\n     * @param maxSharesOut The max amount of shares burned for assets requested.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MaxSharesError\n     */\n    function withdraw(\n        IAutopool vault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem `shares` shares from a AutopoolETH\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param to The destination of assets.\n     * @param shares The amount of shares to redeem from vault.\n     * @param minAmountOut The min amount of assets received by `to`.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeem(\n        IAutopool vault,\n        address to,\n        uint256 shares,\n        uint256 minAmountOut\n    ) external payable returns (uint256 amountOut);\n\n    /// @notice Stakes vault token to corresponding rewarder.\n    /// @param vault IERC20 instance of an Autopool to stake to.\n    /// @param maxAmount Maximum amount for user to stake.  Amount > balanceOf(user) will stake all present tokens.\n    /// @return staked Returns total amount staked.\n    function stakeVaultToken(IERC20 vault, uint256 maxAmount) external payable returns (uint256 staked);\n\n    /// @notice Unstakes vault token from corresponding rewarder.\n    /// @param vault IAutopool instance of the vault token to withdraw.\n    /// @param rewarder Rewarder to withdraw from.\n    /// @param maxAmount Amount of vault token to withdraw Amount > balanceOf(user) will withdraw all owned tokens.\n    /// @param claim Claiming rewards or not on unstaking.\n    /// @return withdrawn Amount of vault token withdrawn.\n    function withdrawVaultToken(\n        IAutopool vault,\n        IMainRewarder rewarder,\n        uint256 maxAmount,\n        bool claim\n    ) external payable returns (uint256 withdrawn);\n\n    /// @notice Claims rewards on user stake of vault token.\n    /// @param vault IAutopool instance of vault token to claim rewards for.\n    /// @param rewarder Rewarder to claim rewards from.\n    /// @param recipient Address to claim rewards for.\n    function claimAutopoolRewards(IAutopool vault, IMainRewarder rewarder, address recipient) external payable;\n\n    /// @notice Checks if timestamp is expired. Purpose is to check the execution deadline with the multicall.\n    /// @param timestamp Timestamp to check.\n    /// @dev throws TimestampTooOld. Payable to allow for multicall.\n    function expiration(uint256 timestamp) external payable;\n}\n"},{"file_path":"src/interfaces/ISystemRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\nimport { IWETH9 } from \"src/interfaces/utils/IWETH9.sol\";\nimport { IAccToke } from \"src/interfaces/staking/IAccToke.sol\";\nimport { IAutopoolRegistry } from \"src/interfaces/vault/IAutopoolRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { ICurveResolver } from \"src/interfaces/utils/ICurveResolver.sol\";\nimport { IAutopilotRouter } from \"src/interfaces/vault/IAutopilotRouter.sol\";\nimport { IAutopoolFactory } from \"src/interfaces/vault/IAutopoolFactory.sol\";\nimport { ISystemSecurity } from \"src/interfaces/security/ISystemSecurity.sol\";\nimport { IDestinationRegistry } from \"src/interfaces/destinations/IDestinationRegistry.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { IDestinationVaultRegistry } from \"src/interfaces/vault/IDestinationVaultRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { IStatsCalculatorRegistry } from \"src/interfaces/stats/IStatsCalculatorRegistry.sol\";\nimport { IAsyncSwapperRegistry } from \"src/interfaces/liquidation/IAsyncSwapperRegistry.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IIncentivesPricingStats } from \"src/interfaces/stats/IIncentivesPricingStats.sol\";\nimport { IMessageProxy } from \"src/interfaces/messageProxy/IMessageProxy.sol\";\n\n/// @notice Root most registry contract for the system\ninterface ISystemRegistry {\n    /// @notice Get the TOKE contract for the system\n    /// @return toke instance of TOKE used in the system\n    function toke() external view returns (IERC20Metadata);\n\n    /// @notice Get the referenced WETH contract for the system\n    /// @return weth contract pointer\n    function weth() external view returns (IWETH9);\n\n    /// @notice Get the AccToke staking contract\n    /// @return accToke instance of the accToke contract for the system\n    function accToke() external view returns (IAccToke);\n\n    /// @notice Get the AutopoolRegistry for this system\n    /// @return registry instance of the registry for this system\n    function autoPoolRegistry() external view returns (IAutopoolRegistry registry);\n\n    /// @notice Get the destination Vault registry for this system\n    /// @return registry instance of the registry for this system\n    function destinationVaultRegistry() external view returns (IDestinationVaultRegistry registry);\n\n    /// @notice Get the access Controller for this system\n    /// @return controller instance of the access controller for this system\n    function accessController() external view returns (IAccessController controller);\n\n    /// @notice Get the destination template registry for this system\n    /// @return registry instance of the registry for this system\n    function destinationTemplateRegistry() external view returns (IDestinationRegistry registry);\n\n    /// @notice Auto Pilot Router\n    /// @return router instance of the system\n    function autoPoolRouter() external view returns (IAutopilotRouter router);\n\n    /// @notice Vault factory lookup by type\n    /// @return vaultFactory instance of the vault factory for this vault type\n    function getAutopoolFactoryByType(bytes32 vaultType) external view returns (IAutopoolFactory vaultFactory);\n\n    /// @notice Get the stats calculator registry for this system\n    /// @return registry instance of the registry for this system\n    function statsCalculatorRegistry() external view returns (IStatsCalculatorRegistry registry);\n\n    /// @notice Get the root price oracle for this system\n    /// @return oracle instance of the root price oracle for this system\n    function rootPriceOracle() external view returns (IRootPriceOracle oracle);\n\n    /// @notice Get the async swapper registry for this system\n    /// @return registry instance of the registry for this system\n    function asyncSwapperRegistry() external view returns (IAsyncSwapperRegistry registry);\n\n    /// @notice Get the swap router for this system\n    /// @return router instance of the swap router for this system\n    function swapRouter() external view returns (ISwapRouter router);\n\n    /// @notice Get the curve resolver for this system\n    /// @return resolver instance of the curve resolver for this system\n    function curveResolver() external view returns (ICurveResolver resolver);\n\n    /// @notice Verify if given address is registered as Reward Token\n    /// @param rewardToken token address to verify\n    /// @return bool that indicates true if token is registered and false if not\n    function isRewardToken(address rewardToken) external view returns (bool);\n\n    /// @notice Get the system security instance for this system\n    /// @return security instance of system security for this system\n    function systemSecurity() external view returns (ISystemSecurity security);\n\n    /// @notice Get the Incentive Pricing Stats\n    /// @return incentivePricing the incentive pricing contract\n    function incentivePricing() external view returns (IIncentivesPricingStats);\n\n    /// @notice Get the Message Proxy\n    /// @return Message proxy contract\n    function messageProxy() external view returns (IMessageProxy);\n\n    /// @notice Get the receiving router contract.\n    /// @return Receiving router contract\n    function receivingRouter() external view returns (address);\n\n    /// @notice Check if an additional contract of type is valid in the system\n    /// @return True if the contract is a valid for the given type\n    function isValidContract(bytes32 contractType, address contractAddress) external view returns (bool);\n\n    /// @notice Returns the additional contract of the given type\n    /// @dev Revert if not set\n    function getUniqueContract(bytes32 contractType) external view returns (address);\n\n    /// @notice Returns all unique contracts configured\n    function listUniqueContracts() external view returns (bytes32[] memory contractTypes, address[] memory addresses);\n\n    /// @notice Returns all additional contract types configured\n    function listAdditionalContractTypes() external view returns (bytes32[] memory);\n\n    /// @notice Returns configured additional contracts by type\n    /// @param contractType Type of contract to list\n    function listAdditionalContracts(bytes32 contractType) external view returns (address[] memory);\n}\n"},{"file_path":"src/interfaces/strategy/IStrategy.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\n\ninterface IStrategy {\n    /* ******************************** */\n    /*      Events                      */\n    /* ******************************** */\n    event DestinationVaultAdded(address destination);\n    event DestinationVaultRemoved(address destination);\n    event WithdrawalQueueSet(address[] destinations);\n    event AddedToRemovalQueue(address destination);\n    event RemovedFromRemovalQueue(address destination);\n\n    error InvalidDestinationVault();\n\n    error RebalanceFailed(string message);\n\n    /// @notice gets the list of supported destination vaults for the Autopool/Strategy\n    /// @return _destinations List of supported destination vaults\n    function getDestinations() external view returns (address[] memory _destinations);\n\n    /// @notice add supported destination vaults for the Autopool/Strategy\n    /// @param _destinations The list of destination vaults to add\n    function addDestinations(address[] calldata _destinations) external;\n\n    /// @notice remove supported destination vaults for the Autopool/Strategy\n    /// @param _destinations The list of destination vaults to remove\n    function removeDestinations(address[] calldata _destinations) external;\n\n    /// @param destinationIn The address / lp token of the destination vault that will increase\n    /// @param tokenIn The address of the underlyer token that will be provided by the swapper\n    /// @param amountIn The amount of the underlying LP tokens that will be received\n    /// @param destinationOut The address of the destination vault that will decrease\n    /// @param tokenOut The address of the underlyer token that will be received by the swapper\n    /// @param amountOut The amount of the tokenOut that will be received by the swapper\n    struct RebalanceParams {\n        address destinationIn;\n        address tokenIn;\n        uint256 amountIn;\n        address destinationOut;\n        address tokenOut;\n        uint256 amountOut;\n    }\n\n    /// @param destination The address / lp token of the destination vault\n    /// @param baseApr Base Apr is the yield generated by staking rewards\n    /// @param feeApr Yield for pool trading fees\n    /// @param incentiveApr Incentives for LP\n    /// @param safeTotalSupply Safe supply for LP tokens\n    /// @param priceReturn Return from price movement to & away from peg\n    /// @param maxDiscount Max discount to peg\n    /// @param maxPremium Max premium to peg\n    /// @param ownedShares Shares owned for this destination\n    /// @param compositeReturn Total return combined from the individual yield components\n    /// @param pricePerShare Price per share\n    struct SummaryStats {\n        address destination;\n        uint256 baseApr;\n        uint256 feeApr;\n        uint256 incentiveApr;\n        uint256 safeTotalSupply;\n        int256 priceReturn;\n        int256 maxDiscount;\n        int256 maxPremium;\n        uint256 ownedShares;\n        int256 compositeReturn;\n        uint256 pricePerShare;\n    }\n\n    /// @notice rebalance the Autopool from the tokenOut (decrease) to the tokenIn (increase)\n    /// This uses a flash loan to receive the tokenOut to reduce the working capital requirements of the swapper\n    /// @param receiver The contract receiving the tokens, needs to implement the\n    /// `onFlashLoan(address user, address token, uint256 amount, uint256 fee, bytes calldata)` interface\n    /// @param params Parameters by which to perform the rebalance\n    /// @param data A data parameter to be passed on to the `receiver` for any custom use\n    function flashRebalance(\n        IERC3156FlashBorrower receiver,\n        RebalanceParams calldata params,\n        bytes calldata data\n    ) external;\n}\n"},{"file_path":"src/interfaces/liquidation/IAsyncSwapper.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nstruct SwapParams {\n    /// @dev The address of the token to be sold.\n    address sellTokenAddress;\n    /// @dev The amount of tokens to be sold.\n    uint256 sellAmount;\n    /// @dev The address of the token to be bought.\n    address buyTokenAddress;\n    /// @dev The expected minimum amount of tokens to be bought.\n    uint256 buyAmount;\n    /// @dev Data payload to be used for complex swap operations.\n    bytes data;\n    /// @dev Extra data payload reserved for future development. This field allows for additional information\n    /// or functionality to be added without changing the struct and interface.\n    bytes extraData;\n    /// @dev Execution deadline in timestamp format\n    uint256 deadline;\n}\n\ninterface IAsyncSwapper {\n    error TokenAddressZero();\n    error SwapFailed();\n    error InsufficientBuyAmountReceived(uint256 buyTokenAmountReceived, uint256 buyAmount);\n    error InsufficientSellAmount();\n    error InsufficientBuyAmount();\n    error InsufficientBalance(uint256 balanceNeeded, uint256 balanceAvailable);\n\n    event Swapped(\n        address indexed sellTokenAddress,\n        address indexed buyTokenAddress,\n        uint256 sellAmount,\n        uint256 buyAmount,\n        uint256 buyTokenAmountReceived\n    );\n\n    /**\n     * @notice Swaps sellToken for buyToken\n     * @param swapParams Encoded swap data\n     * @return buyTokenAmountReceived The amount of buyToken received from the swap\n     */\n    function swap(SwapParams memory swapParams) external returns (uint256 buyTokenAmountReceived);\n}\n"},{"file_path":"src/interfaces/oracles/IRootPriceOracle.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity 0.8.17;\n\n/// @notice Retrieve a price for any token used in the system\ninterface IRootPriceOracle {\n    /// @notice Returns a fair price for the provided token in ETH\n    /// @param token token to get the price of\n    /// @return price the price of the token in ETH\n    function getPriceInEth(address token) external returns (uint256 price);\n\n    /// @notice Returns a spot price for the provided token in ETH, utilizing specified liquidity pool\n    /// @param token token to get the spot price of\n    /// @param pool liquidity pool to be used for price determination\n    /// @return price the spot price of the token in ETH based on the provided pool\n    function getSpotPriceInEth(address token, address pool) external returns (uint256);\n\n    /// @notice Returns a price for base token in quote token.\n    /// @dev Requires both tokens to be registered.\n    /// @param base Address of base token.\n    /// @param quote Address of quote token.\n    /// @return price Price of the base token in quote token.\n    function getPriceInQuote(address base, address quote) external returns (uint256 price);\n\n    /// @notice Retrieve the price of LP token based on the reserves\n    /// @param lpToken LP token to get the price of\n    /// @param pool liquidity pool to be used for price determination\n    /// @param quoteToken token to quote the price in\n    function getRangePricesLP(\n        address lpToken,\n        address pool,\n        address quoteToken\n    ) external returns (uint256 spotPriceInQuote, uint256 safePriceInQuote, bool isSpotSafe);\n\n    /// @notice Returns floor or ceiling price of the supplied lp token in terms of requested quote.\n    /// @dev  Floor price: the minimum price among all the spot prices and safe prices of the tokens in the pool.\n    ///       Ceiling price: the maximum price among all the spot prices and safe prices of the tokens in the pool.\n    /// @param pool Address of pool to get spot pricing from.\n    /// @param lpToken Address of the lp token to price.\n    /// @param inQuote Address of desired quote token.\n    /// @param ceiling Bool indicating whether to get floor or ceiling price.\n    /// @return floorOrCeilingPerLpToken Floor or ceiling price of the lp token.\n    function getFloorCeilingPrice(\n        address pool,\n        address lpToken,\n        address inQuote,\n        bool ceiling\n    ) external returns (uint256 floorOrCeilingPerLpToken);\n\n    function getFloorPrice(address, address, address) external returns (uint256 price);\n\n    function getCeilingPrice(address, address, address) external returns (uint256 price);\n}\n"},{"file_path":"src/interfaces/rewarders/IRewards.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2024 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\n/**\n *  @title Validates and distributes Vault token rewards based on the\n *  the signed and submitted payloads\n */\ninterface IRewards {\n    struct Recipient {\n        uint256 chainId;\n        uint256 cycle;\n        address wallet;\n        uint256 amount;\n    }\n\n    event SignerSet(address newSigner);\n    event Claimed(uint256 cycle, address recipient, uint256 amount);\n\n    /// @notice Get the underlying token rewards are paid in\n    /// @return Token address\n    function vaultToken() external view returns (IERC20);\n\n    /// @notice Get the current payload signer;\n    /// @return Signer address\n    function rewardsSigner() external view returns (address);\n\n    /// @notice Check the amount an account has already claimed\n    /// @param account Account to check\n    /// @return Amount already claimed\n    function claimedAmounts(address account) external view returns (uint256);\n\n    /// @notice Get the amount that is claimable based on the provided payload\n    /// @param recipient Published rewards payload\n    /// @return Amount claimable if the payload is signed\n    function getClaimableAmount(Recipient calldata recipient) external view returns (uint256);\n\n    /// @notice Change the signer used to validate payloads\n    /// @param newSigner The new address that will be signing rewards payloads\n    function setSigner(address newSigner) external;\n\n    /// @notice Claim your rewards\n    /// @param recipient Published rewards payload\n    /// @param v v component of the payload signature\n    /// @param r r component of the payload signature\n    /// @param s s component of the payload signature\n    function claim(Recipient calldata recipient, uint8 v, bytes32 r, bytes32 s) external returns (uint256);\n\n    /// @notice Claim rewards on behalf of another account , invoked primarily by the router\n    /// @param recipient Published rewards payload\n    /// @param v v component of the payload signature\n    /// @param r r component of the payload signature\n    /// @param s s component of the payload signature\n    function claimFor(Recipient calldata recipient, uint8 v, bytes32 r, bytes32 s) external returns (uint256);\n\n    /// @notice Generate the hash of the payload\n    /// @param recipient Published rewards payload\n    /// @return Hash of the payload\n    function genHash(Recipient memory recipient) external view returns (bytes32);\n}\n"},{"file_path":"src/interfaces/messageProxy/IMessageProxy.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Send messages to our systems on other chains\ninterface IMessageProxy {\n    function sendMessage(bytes32 messageType, bytes memory message) external;\n}\n"},{"file_path":"src/interfaces/destinations/IDestinationRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IDestinationAdapter } from \"src/interfaces/destinations/IDestinationAdapter.sol\";\n\ninterface IDestinationRegistry {\n    event Register(bytes32[] indexed destinationTypes, address[] indexed targets);\n    event Replace(bytes32[] indexed destinationTypes, address[] indexed targets);\n    event Unregister(bytes32[] indexed destinationTypes);\n\n    event Whitelist(bytes32[] indexed destinationTypes);\n    event RemoveFromWhitelist(bytes32[] indexed destinationTypes);\n\n    error InvalidAddress(address addr);\n    error NotAllowedDestination();\n    error DestinationAlreadySet();\n\n    /**\n     * @notice Adds a new addresses of the given destination types\n     * @dev Fails if trying to overwrite previous value of the same destination type\n     * @param destinationTypes Ones from the destination type whitelist\n     * @param targets addresses of the deployed DestinationAdapters, cannot be 0\n     */\n    function register(bytes32[] calldata destinationTypes, address[] calldata targets) external;\n\n    /**\n     * @notice Replaces an addresses of the given destination types\n     * @dev Fails if given destination type was not set previously\n     * @param destinationTypes Ones from the destination type whitelist\n     * @param targets addresses of the deployed DestinationAdapters, cannot be 0\n     */\n    function replace(bytes32[] calldata destinationTypes, address[] calldata targets) external;\n\n    /**\n     * @notice Removes an addresses of the given pre-registered destination types\n     * @param destinationTypes Ones from the destination types whitelist\n     */\n    function unregister(bytes32[] calldata destinationTypes) external;\n\n    /**\n     * @notice Gives an address of the given destination type\n     * @dev Should revert on missing destination\n     * @param destination One from the destination type whitelist\n     */\n    function getAdapter(bytes32 destination) external returns (IDestinationAdapter);\n\n    /**\n     * @notice Adds given destination types to the whitelist\n     * @param destinationTypes Types to whitelist\n     */\n    function addToWhitelist(bytes32[] calldata destinationTypes) external;\n\n    /**\n     * @notice Removes given pre-whitelisted destination types\n     * @param destinationTypes Ones from the destination type whitelist\n     */\n    function removeFromWhitelist(bytes32[] calldata destinationTypes) external;\n\n    /**\n     * @notice Checks if the given destination type is whitelisted\n     * @param destinationType Type to verify\n     */\n    function isWhitelistedDestination(bytes32 destinationType) external view returns (bool);\n}\n"},{"file_path":"src/interfaces/strategy/IAutopoolStrategy.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\n\ninterface IAutopoolStrategy {\n    enum RebalanceDirection {\n        In,\n        Out\n    }\n\n    /// @notice verify that a rebalance (swap between destinations) meets all the strategy constraints\n    /// @dev Signature identical to IStrategy.verifyRebalance\n    function verifyRebalance(\n        IStrategy.RebalanceParams memory,\n        IStrategy.SummaryStats memory\n    ) external returns (bool, string memory message);\n\n    /// @notice called by the Autopool when NAV is updated\n    /// @dev can only be called by the strategy's registered Autopool\n    /// @param navPerShare The navPerShare to record\n    function navUpdate(uint256 navPerShare) external;\n\n    /// @notice called by the Autopool when a rebalance is completed\n    /// @dev can only be called by the strategy's registered Autopool\n    /// @param rebalanceParams The parameters for the rebalance that was executed\n    function rebalanceSuccessfullyExecuted(IStrategy.RebalanceParams memory rebalanceParams) external;\n\n    /// @notice called by the Autopool during rebalance process\n    /// @param rebalanceParams The parameters for the rebalance that was executed\n    function getRebalanceOutSummaryStats(IStrategy.RebalanceParams memory rebalanceParams)\n        external\n        returns (IStrategy.SummaryStats memory outSummary);\n\n    /// @notice Returns all hooks registered on strategy\n    /// @dev Will return zero addresses for unregistered hooks\n    /// @return hooks Array of hook addresses\n    function getHooks() external view returns (address[] memory hooks);\n\n    /// @notice the number of days to pause rebalancing due to NAV decay\n    function pauseRebalancePeriodInDays() external view returns (uint16);\n\n    /// @notice the number of seconds gap between consecutive rebalances\n    function rebalanceTimeGapInSeconds() external view returns (uint256);\n\n    /// @notice destinations trading a premium above maxPremium will be blocked from new capital deployments\n    function maxPremium() external view returns (int256); // 100% = 1e18\n\n    /// @notice destinations trading a discount above maxDiscount will be blocked from new capital deployments\n    function maxDiscount() external view returns (int256); // 100% = 1e18\n\n    /// @notice the allowed staleness of stats data before a revert occurs\n    function staleDataToleranceInSeconds() external view returns (uint40);\n\n    /// @notice the swap cost offset period to initialize the strategy with\n    function swapCostOffsetInitInDays() external view returns (uint16);\n\n    /// @notice the number of violations required to trigger a tightening of the swap cost offset period (1 to 10)\n    function swapCostOffsetTightenThresholdInViolations() external view returns (uint16);\n\n    /// @notice the number of days to decrease the swap offset period for each tightening step\n    function swapCostOffsetTightenStepInDays() external view returns (uint16);\n\n    /// @notice the number of days since a rebalance required to trigger a relaxing of the swap cost offset period\n    function swapCostOffsetRelaxThresholdInDays() external view returns (uint16);\n\n    /// @notice the number of days to increase the swap offset period for each relaxing step\n    function swapCostOffsetRelaxStepInDays() external view returns (uint16);\n\n    // slither-disable-start similar-names\n    /// @notice the maximum the swap cost offset period can reach. This is the loosest the strategy will be\n    function swapCostOffsetMaxInDays() external view returns (uint16);\n\n    /// @notice the minimum the swap cost offset period can reach. This is the most conservative the strategy will be\n    function swapCostOffsetMinInDays() external view returns (uint16);\n\n    /// @notice the number of days for the first NAV decay comparison (e.g., 30 days)\n    function navLookback1InDays() external view returns (uint8);\n\n    /// @notice the number of days for the second NAV decay comparison (e.g., 60 days)\n    function navLookback2InDays() external view returns (uint8);\n\n    /// @notice the number of days for the third NAV decay comparison (e.g., 90 days)\n    function navLookback3InDays() external view returns (uint8);\n    // slither-disable-end similar-names\n\n    /// @notice the maximum slippage that is allowed for a normal rebalance\n    function maxNormalOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when a destination is trimmed due to constraint violations\n    /// recommend setting this higher than maxNormalOperationSlippage\n    function maxTrimOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when a destinationVault has been shutdown\n    /// shutdown for a vault is abnormal and means there is an issue at that destination\n    /// recommend setting this higher than maxNormalOperationSlippage\n    function maxEmergencyOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when the Autopool has been shutdown\n    function maxShutdownOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum discount used for price return\n    function maxAllowedDiscount() external view returns (int256); // 18 precision\n\n    /// @notice model weight used for LSTs base yield, 1e6 is the highest\n    function weightBase() external view returns (uint256);\n\n    /// @notice model weight used for DEX fee yield, 1e6 is the highest\n    function weightFee() external view returns (uint256);\n\n    /// @notice model weight used for incentive yield\n    function weightIncentive() external view returns (uint256);\n\n    /// @notice model weight applied to an LST discount when exiting the position\n    function weightPriceDiscountExit() external view returns (int256);\n\n    /// @notice model weight applied to an LST discount when entering the position\n    function weightPriceDiscountEnter() external view returns (int256);\n\n    /// @notice model weight applied to an LST premium when entering or exiting the position\n    function weightPricePremium() external view returns (int256);\n\n    /// @notice initial value of the swap cost offset to use\n    function swapCostOffsetInit() external view returns (uint16);\n\n    /// @notice initial lst price gap tolerance\n    function defaultLstPriceGapTolerance() external view returns (uint256);\n}\n"},{"file_path":"src/interfaces/vault/IAutopoolFactory.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\ninterface IAutopoolFactory {\n    ///////////////////////////////////////////////////////////////////\n    //                        Vault Creation\n    ///////////////////////////////////////////////////////////////////\n\n    /**\n     * @notice Spin up a new AutopoolETH\n     * @param strategy Strategy template address\n     * @param symbolSuffix Symbol suffix of the new token\n     * @param descPrefix Description prefix of the new token\n     * @param salt Vault creation salt\n     * @param extraParams Any extra data needed for the vault\n     */\n    function createVault(\n        address strategy,\n        string memory symbolSuffix,\n        string memory descPrefix,\n        bytes32 salt,\n        bytes calldata extraParams\n    ) external payable returns (address newVaultAddress);\n\n    function addStrategyTemplate(address strategyTemplate) external;\n\n    function removeStrategyTemplate(address strategyTemplate) external;\n\n    /// @notice Returns the template used to create Autopools\n    function template() external returns (address);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n    /**\n     * @dev Returns one of the accounts that have `role`. `index` must be a\n     * value between 0 and {getRoleMemberCount}, non-inclusive.\n     *\n     * Role bearers are not sorted in any particular way, and their ordering may\n     * change at any point.\n     *\n     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n     * you perform all queries on the same block. See the following\n     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n     * for more information.\n     */\n    function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n    /**\n     * @dev Returns the number of accounts that have `role`. Can be used\n     * together with {getRoleMember} to enumerate all bearers of a role.\n     */\n    function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n"},{"file_path":"src/libs/LibAdapter.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\n\nlibrary LibAdapter {\n    using SafeERC20 for IERC20;\n\n    address public constant CURVE_REGISTRY_ETH_ADDRESS_POINTER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    error MinLpAmountNotReached();\n    error LpTokenAmountMismatch();\n    error NoNonZeroAmountProvided();\n    error InvalidBalanceChange();\n\n    // Utils\n    function _approve(IERC20 token, address spender, uint256 amount) internal {\n        uint256 currentAllowance = token.allowance(address(this), spender);\n        if (currentAllowance > 0) {\n            token.safeDecreaseAllowance(spender, currentAllowance);\n        }\n        token.safeIncreaseAllowance(spender, amount);\n    }\n}\n"},{"file_path":"src/interfaces/vault/IDestinationVaultFactory.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\n/// @notice Creates and registers Destination Vaults for the system\ninterface IDestinationVaultFactory is ISystemComponent {\n    /// @notice Creates a vault of the specified type\n    /// @dev vaultType will be bytes32 encoded and checked that a template is registered\n    /// @param vaultType human readable key of the vault template\n    /// @param baseAsset Base asset of the system. WETH/USDC/etc\n    /// @param underlyer Underlying asset the vault will wrap\n    /// @param incentiveCalculator Incentive calculator of the vault\n    /// @param additionalTrackedTokens Any tokens in addition to base and underlyer that should be tracked\n    /// @param salt Contracts are created via CREATE2 with this value\n    /// @param params params to be passed to vaults initialize function\n    /// @return vault address of the newly created destination vault\n    function create(\n        string memory vaultType,\n        address baseAsset,\n        address underlyer,\n        address incentiveCalculator,\n        address[] memory additionalTrackedTokens,\n        bytes32 salt,\n        bytes memory params\n    ) external returns (address vault);\n\n    /// @notice Sets the default reward ratio\n    /// @param rewardRatio new default reward ratio\n    function setDefaultRewardRatio(uint256 rewardRatio) external;\n\n    /// @notice Sets the default reward block duration\n    /// @param blockDuration new default reward block duration\n    function setDefaultRewardBlockDuration(uint256 blockDuration) external;\n}\n"},{"file_path":"src/strategy/StructuredLinkedList.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity =0.8.17;\n\n/**\n * @title StructuredLinkedList\n * @author Vittorio Minacori (https://github.com/vittominacori)\n * @dev An utility library for using sorted linked list data structures in your Solidity project.\n * @notice Adapted from\n * https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/src/contracts/libraries/StructuredLinkedList.sol\n */\nlibrary StructuredLinkedList {\n    uint256 private constant _NULL = 0;\n    uint256 private constant _HEAD = 0;\n\n    bool private constant _PREV = false;\n    bool private constant _NEXT = true;\n\n    struct List {\n        uint256 size;\n        mapping(uint256 => mapping(bool => uint256)) list;\n    }\n\n    /**\n     * @dev Checks if the list exists\n     * @param self stored linked list from contract\n     * @return bool true if list exists, false otherwise\n     */\n    function listExists(List storage self) public view returns (bool) {\n        // if the head nodes previous or next pointers both point to itself, then there are no items in the list\n        if (self.list[_HEAD][_PREV] != _HEAD || self.list[_HEAD][_NEXT] != _HEAD) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Checks if the node exists\n     * @param self stored linked list from contract\n     * @param _node a node to search for\n     * @return bool true if node exists, false otherwise\n     */\n    function nodeExists(List storage self, uint256 _node) public view returns (bool) {\n        if (self.list[_node][_PREV] == _HEAD && self.list[_node][_NEXT] == _HEAD) {\n            if (self.list[_HEAD][_NEXT] == _node) {\n                return true;\n            } else {\n                return false;\n            }\n        } else {\n            return true;\n        }\n    }\n\n    /**\n     * @dev Returns the number of elements in the list\n     * @param self stored linked list from contract\n     * @return uint256\n     */\n    // slither-disable-next-line dead-code\n    function sizeOf(List storage self) public view returns (uint256) {\n        return self.size;\n    }\n\n    /**\n     * @dev Gets the head of the list\n     * @param self stored linked list from contract\n     * @return uint256 the head of the list\n     */\n    function getHead(List storage self) public view returns (uint256) {\n        return self.list[_HEAD][_NEXT];\n    }\n\n    /**\n     * @dev Gets the head of the list\n     * @param self stored linked list from contract\n     * @return uint256 the head of the list\n     */\n    function getTail(List storage self) public view returns (uint256) {\n        return self.list[_HEAD][_PREV];\n    }\n\n    /**\n     * @dev Returns the links of a node as a tuple\n     * @param self stored linked list from contract\n     * @param _node id of the node to get\n     * @return bool, uint256, uint256 true if node exists or false otherwise, previous node, next node\n     */\n    // slither-disable-next-line dead-code\n    function getNode(List storage self, uint256 _node) public view returns (bool, uint256, uint256) {\n        if (!nodeExists(self, _node)) {\n            return (false, 0, 0);\n        } else {\n            return (true, self.list[_node][_PREV], self.list[_node][_NEXT]);\n        }\n    }\n\n    /**\n     * @dev Returns the link of a node `_node` in direction `_direction`.\n     * @param self stored linked list from contract\n     * @param _node id of the node to step from\n     * @param _direction direction to step in\n     * @return bool, uint256 true if node exists or false otherwise, node in _direction\n     */\n    // slither-disable-next-line dead-code\n    function getAdjacent(List storage self, uint256 _node, bool _direction) public view returns (bool, uint256) {\n        if (!nodeExists(self, _node)) {\n            return (false, 0);\n        } else {\n            uint256 adjacent = self.list[_node][_direction];\n            return (adjacent != _HEAD, adjacent);\n        }\n    }\n\n    /**\n     * @dev Returns the link of a node `_node` in direction `_NEXT`.\n     * @param self stored linked list from contract\n     * @param _node id of the node to step from\n     * @return bool, uint256 true if node exists or false otherwise, next node\n     */\n    // slither-disable-next-line dead-code\n    function getNextNode(List storage self, uint256 _node) public view returns (bool, uint256) {\n        return getAdjacent(self, _node, _NEXT);\n    }\n\n    /**\n     * @dev Returns the link of a node `_node` in direction `_PREV`.\n     * @param self stored linked list from contract\n     * @param _node id of the node to step from\n     * @return bool, uint256 true if node exists or false otherwise, previous node\n     */\n    // slither-disable-next-line dead-code\n    function getPreviousNode(List storage self, uint256 _node) public view returns (bool, uint256) {\n        return getAdjacent(self, _node, _PREV);\n    }\n\n    /**\n     * @dev Insert node `_new` beside existing node `_node` in direction `_NEXT`.\n     * @param self stored linked list from contract\n     * @param _node existing node\n     * @param _new  new node to insert\n     * @return bool true if success, false otherwise\n     */\n    // slither-disable-next-line dead-code\n    function insertAfter(List storage self, uint256 _node, uint256 _new) public returns (bool) {\n        return _insert(self, _node, _new, _NEXT);\n    }\n\n    /**\n     * @dev Insert node `_new` beside existing node `_node` in direction `_PREV`.\n     * @param self stored linked list from contract\n     * @param _node existing node\n     * @param _new  new node to insert\n     * @return bool true if success, false otherwise\n     */\n    // slither-disable-next-line dead-code\n    function insertBefore(List storage self, uint256 _node, uint256 _new) public returns (bool) {\n        return _insert(self, _node, _new, _PREV);\n    }\n\n    /**\n     * @dev Removes an entry from the linked list\n     * @param self stored linked list from contract\n     * @param _node node to remove from the list\n     * @return uint256 the removed node\n     */\n    function remove(List storage self, uint256 _node) public returns (uint256) {\n        if ((_node == _NULL) || (!nodeExists(self, _node))) {\n            return 0;\n        }\n        _createLink(self, self.list[_node][_PREV], self.list[_node][_NEXT], _NEXT);\n        delete self.list[_node][_PREV];\n        delete self.list[_node][_NEXT];\n\n        self.size -= 1;\n\n        return _node;\n    }\n\n    /**\n     * @dev Pushes an entry to the head of the linked list\n     * @param self stored linked list from contract\n     * @param _node new entry to push to the head\n     * @return bool true if success, false otherwise\n     */\n    function pushFront(List storage self, uint256 _node) public returns (bool) {\n        return _push(self, _node, _NEXT);\n    }\n\n    /**\n     * @dev Pushes an entry to the tail of the linked list\n     * @param self stored linked list from contract\n     * @param _node new entry to push to the tail\n     * @return bool true if success, false otherwise\n     */\n    function pushBack(List storage self, uint256 _node) public returns (bool) {\n        return _push(self, _node, _PREV);\n    }\n\n    /**\n     * @dev Pops the first entry from the head of the linked list\n     * @param self stored linked list from contract\n     * @return uint256 the removed node\n     */\n    // slither-disable-next-line dead-code\n    function popFront(List storage self) public returns (uint256) {\n        return _pop(self, _NEXT);\n    }\n\n    /**\n     * @dev Pops the first entry from the tail of the linked list\n     * @param self stored linked list from contract\n     * @return uint256 the removed node\n     */\n    // slither-disable-next-line dead-code\n    function popBack(List storage self) public returns (uint256) {\n        return _pop(self, _PREV);\n    }\n\n    /**\n     * @dev Pushes an entry to the head of the linked list\n     * @param self stored linked list from contract\n     * @param _node new entry to push to the head\n     * @param _direction push to the head (_NEXT) or tail (_PREV)\n     * @return bool true if success, false otherwise\n     */\n    function _push(List storage self, uint256 _node, bool _direction) private returns (bool) {\n        return _insert(self, _HEAD, _node, _direction);\n    }\n\n    /**\n     * @dev Pops the first entry from the linked list\n     * @param self stored linked list from contract\n     * @param _direction pop from the head (_NEXT) or the tail (_PREV)\n     * @return uint256 the removed node\n     */\n    // slither-disable-next-line dead-code\n    function _pop(List storage self, bool _direction) private returns (uint256) {\n        uint256 adj;\n        (, adj) = getAdjacent(self, _HEAD, _direction);\n        return remove(self, adj);\n    }\n\n    /**\n     * @dev Insert node `_new` beside existing node `_node` in direction `_direction`.\n     * @param self stored linked list from contract\n     * @param _node existing node\n     * @param _new  new node to insert\n     * @param _direction direction to insert node in\n     * @return bool true if success, false otherwise\n     */\n    function _insert(List storage self, uint256 _node, uint256 _new, bool _direction) private returns (bool) {\n        if (!nodeExists(self, _new) && nodeExists(self, _node)) {\n            uint256 c = self.list[_node][_direction];\n            _createLink(self, _node, _new, _direction);\n            _createLink(self, _new, c, _direction);\n\n            self.size += 1;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * @dev Creates a bidirectional link between two nodes on direction `_direction`\n     * @param self stored linked list from contract\n     * @param _node existing node\n     * @param _link node to link to in the _direction\n     * @param _direction direction to insert node in\n     */\n    function _createLink(List storage self, uint256 _node, uint256 _link, bool _direction) private {\n        self.list[_link][!_direction] = _node;\n        self.list[_node][_direction] = _link;\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/Address.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.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://diligence.consensys.net/posts/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"},{"file_path":"src/interfaces/vault/IDestinationVault.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\nimport { IBaseAssetVault } from \"src/interfaces/vault/IBaseAssetVault.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IDexLSTStats } from \"src/interfaces/stats/IDexLSTStats.sol\";\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\ninterface IDestinationVault is ISystemComponent, IBaseAssetVault, IERC20 {\n    enum VaultShutdownStatus {\n        Active,\n        Deprecated,\n        Exploit\n    }\n\n    error LogicDefect();\n    error BaseAmountReceived(uint256 amount);\n\n    /* ******************************** */\n    /* View                             */\n    /* ******************************** */\n\n    /// @notice A full unit of this vault\n    // solhint-disable-next-line func-name-mixedcase\n    function ONE() external view returns (uint256);\n\n    /// @notice The asset that is deposited into the vault\n    function underlying() external view returns (address);\n\n    /// @notice The asset that rewards and withdrawals to the Autopool are denominated in\n    /// @inheritdoc IBaseAssetVault\n    function baseAsset() external view override returns (address);\n\n    /// @notice Debt balance of underlying asset that is in contract.  This\n    ///     value includes only assets that are known as debt by the rest of the\n    ///     system (i.e. transferred in on rebalance), and does not include\n    ///     extraneous amounts of underlyer that may have ended up in this contract.\n    function internalDebtBalance() external view returns (uint256);\n\n    /// @notice Debt balance of underlying asset staked externally.  This value only\n    ///     includes assets known as debt to the rest of the system, and does not include\n    ///     any assets staked on behalf of the DV in external contracts.\n    function externalDebtBalance() external view returns (uint256);\n\n    /// @notice Returns true value of _underlyer in DV.  Debt + tokens that may have\n    ///     been transferred into the contract outside of rebalance.\n    function internalQueriedBalance() external view returns (uint256);\n\n    /// @notice Returns true value of staked _underlyer in external contract.  This\n    ///     will include any _underlyer that has been staked on behalf of the DV.\n    function externalQueriedBalance() external view returns (uint256);\n\n    /// @notice Balance of underlying debt, sum of `externalDebtBalance()` and `internalDebtBalance()`.\n    function balanceOfUnderlyingDebt() external view returns (uint256);\n\n    /// @notice Rewarder for this vault\n    function rewarder() external view returns (address);\n\n    /// @notice Exchange this destination vault points to\n    function exchangeName() external view returns (string memory);\n\n    /// @notice The type of pool associated with this vault\n    function poolType() external view returns (string memory);\n\n    /// @notice If the pool only deals in ETH when adding or removing liquidity\n    function poolDealInEth() external view returns (bool);\n\n    /// @notice Tokens that base asset can be swapped into\n    function underlyingTokens() external view returns (address[] memory);\n\n    /* ******************************** */\n    /* Events                           */\n    /* ******************************** */\n\n    event Donated(address sender, uint256 amount);\n    event Withdraw(\n        uint256 target, uint256 actual, uint256 debtLoss, uint256 claimLoss, uint256 fromIdle, uint256 fromDebt\n    );\n    event UpdateSignedMessage(bytes32 hash, bool flag);\n\n    /* ******************************** */\n    /* Errors                           */\n    /* ******************************** */\n\n    error ZeroAddress(string paramName);\n    error InvalidShutdownStatus(VaultShutdownStatus status);\n\n    /* ******************************** */\n    /* Functions                        */\n    /* ******************************** */\n\n    /// @notice Setup the contract. These will be cloned so no constructor\n    /// @param baseAsset_ Base asset of the system. WETH/USDC/etc\n    /// @param underlyer_ Underlying asset the vault will wrap\n    /// @param rewarder_ Reward tracker for this vault\n    /// @param incentiveCalculator_ Incentive calculator for this vault\n    /// @param additionalTrackedTokens_ Additional tokens that should be considered 'tracked'\n    /// @param params_ Any extra parameters needed to setup the contract\n    function initialize(\n        IERC20 baseAsset_,\n        IERC20 underlyer_,\n        IMainRewarder rewarder_,\n        address incentiveCalculator_,\n        address[] memory additionalTrackedTokens_,\n        bytes memory params_\n    ) external;\n\n    function getRangePricesLP() external returns (uint256 spotPrice, uint256 safePrice, bool isSpotSafe);\n\n    /// @notice Calculates the current value of a portion of the debt based on shares\n    /// @dev Queries the current value of all tokens we have deployed, whether its a single place, multiple, staked, etc\n    /// @param shares The number of shares to value\n    /// @return value The current value of our debt in terms of the baseAsset\n    function debtValue(uint256 shares) external returns (uint256 value);\n\n    /// @notice Collects any earned rewards from staking, incentives, etc. Transfers to sender\n    /// @dev Should be limited to LIQUIDATOR_MANAGER. Rewards must be collected before claimed\n    /// @return amounts amount of rewards claimed for each token\n    /// @return tokens tokens claimed\n    function collectRewards() external returns (uint256[] memory amounts, address[] memory tokens);\n\n    /// @notice Pull any non-tracked token to the specified destination\n    /// @dev Should be limited to TOKEN_RECOVERY_MANAGER\n    function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external;\n\n    /// @notice Recovers any extra underlying both in DV and staked externally not tracked as debt.\n    /// @dev Should be limited to TOKEN_SAVER_ROLE.\n    /// @param destination The address to send excess underlyer to.\n    function recoverUnderlying(address destination) external;\n\n    /// @notice Deposit underlying to receive destination vault shares\n    /// @param amount amount of base lp asset to deposit\n    function depositUnderlying(uint256 amount) external returns (uint256 shares);\n\n    /// @notice Withdraw underlying by burning destination vault shares\n    /// @param shares amount of destination vault shares to burn\n    /// @param to destination of the underlying asset\n    /// @return amount underlyer amount 'to' received\n    function withdrawUnderlying(uint256 shares, address to) external returns (uint256 amount);\n\n    /// @notice Burn specified shares for underlyer swapped to base asset\n    /// @param shares amount of vault shares to burn\n    /// @param to destination of the base asset\n    /// @return amount base asset amount 'to' received\n    /// @return tokens the tokens burned to get the base asset\n    /// @return tokenAmounts the amount of the tokens burned to get the base asset\n    function withdrawBaseAsset(\n        uint256 shares,\n        address to\n    ) external returns (uint256 amount, address[] memory tokens, uint256[] memory tokenAmounts);\n\n    /// @notice Mark this vault as shutdown so that autoPools can react\n    function shutdown(VaultShutdownStatus reason) external;\n\n    /// @notice True if the vault has been shutdown\n    function isShutdown() external view returns (bool);\n\n    /// @notice Returns the reason for shutdown (or `Active` if not shutdown)\n    function shutdownStatus() external view returns (VaultShutdownStatus);\n\n    /// @notice Stats contract for this vault\n    function getStats() external view returns (IDexLSTStats);\n\n    /// @notice get the marketplace rewards\n    /// @return rewardTokens list of reward token addresses\n    /// @return rewardRates list of reward rates\n    function getMarketplaceRewards() external returns (uint256[] memory rewardTokens, uint256[] memory rewardRates);\n\n    /// @notice Get the address of the underlying pool the vault points to\n    /// @return poolAddress address of the underlying pool\n    function getPool() external view returns (address poolAddress);\n\n    /// @notice Gets the spot price of the underlying LP token\n    /// @dev Price validated to be inside our tolerance against safe price. Will revert if outside.\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getValidatedSpotPrice() external returns (uint256 price);\n\n    /// @notice Gets the safe price of the underlying LP token\n    /// @dev Price validated to be inside our tolerance against spot price. Will revert if outside.\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getValidatedSafePrice() external returns (uint256 price);\n\n    /// @notice Get the lowest price we can get for the LP token\n    /// @dev This price can be attacked is not validate to be in any range\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getUnderlyerFloorPrice() external returns (uint256 price);\n\n    /// @notice Get the highest price we can get for the LP token\n    /// @dev This price can be attacked is not validate to be in any range\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getUnderlyerCeilingPrice() external returns (uint256 price);\n\n    /// @notice Set or unset  a hash as a signed message\n    /// @dev Should be limited to DESTINATION_VAULTS_UPDATER. The set hash is used to validate a signature.\n    /// This signature can be potentially used to claim offchain rewards earned by Destination Vaults.\n    /// @param hash bytes32 hash of a payload\n    /// @param flag boolean flag to indicate a validity of hash\n    function setMessage(bytes32 hash, bool flag) external;\n\n    /// @notice Allows to change the incentive calculator of destination vault\n    /// @dev Only works when vault is shutdown, also validates the calculator before updating\n    /// @param incentiveCalculator address of the new incentive calculator\n    function setIncentiveCalculator(address incentiveCalculator) external;\n\n    /// @notice Allows to change the extension contract\n    /// @dev Should be limited to DESTINATION_VAULT_MANAGER\n    /// @param extension contract address\n    function setExtension(address extension) external;\n\n    /// @notice Calls the execute function of the extension contract\n    /// @dev Should be limited to DESTINATION_VAULT_MANAGER\n    /// @dev Special care should be taken to ensure that balances hasn't been manipulated\n    /// @param data any data that the extension contract needs\n    function executeExtension(bytes calldata data) external;\n\n    /// @notice Returns the max recoup credit given during the withdraw of an undervalued destination\n    function recoupMaxCredit() external view returns (uint256);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"contract ISystemRegistry","name":"_systemRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"AlreadyRegistered","type":"error"},{"inputs":[{"internalType":"string","name":"paramName","type":"string"}],"name":"InvalidParam","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"MappingDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"MissingSpotPriceOracle","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"MissingTokenOracle","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"NoThresholdFound","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"newOracle","type":"address"}],"name":"ReplaceAlreadyMatches","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"oldExpected","type":"address"},{"internalType":"address","name":"oldActual","type":"address"}],"name":"ReplaceOldMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"source1","type":"address"},{"internalType":"address","name":"source2","type":"address"}],"name":"SystemMismatch","type":"error"},{"inputs":[],"name":"UndefinedAddress","type":"error"},{"inputs":[{"internalType":"string","name":"paramName","type":"string"}],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"}],"name":"PoolRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":true,"internalType":"address","name":"newOracle","type":"address"}],"name":"PoolRegistrationReplaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"PoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"SafeSpotPriceThresholdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"oracle","type":"address"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":false,"internalType":"address","name":"newOracle","type":"address"}],"name":"TokenRegistrationReplaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"TokenRemoved","type":"event"},{"inputs":[],"name":"accessController","outputs":[{"internalType":"contract IAccessController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getCeilingPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"address","name":"inQuote","type":"address"},{"internalType":"bool","name":"ceiling","type":"bool"}],"name":"getFloorCeilingPrice","outputs":[{"internalType":"uint256","name":"floorOrCeilingPerLpToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getFloorPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getPriceInEth","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getPriceInQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"quoteToken","type":"address"}],"name":"getRangePricesLP","outputs":[{"internalType":"uint256","name":"spotPriceInQuote","type":"uint256"},{"internalType":"uint256","name":"safePriceInQuote","type":"uint256"},{"internalType":"bool","name":"isSpotSafe","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"name":"getSpotPriceInEth","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getSystemRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolMappings","outputs":[{"internalType":"contract ISpotPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IPriceOracle","name":"oracle","type":"address"}],"name":"registerMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"contract ISpotPriceOracle","name":"oracle","type":"address"}],"name":"registerPoolMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"removePoolMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IPriceOracle","name":"oldOracle","type":"address"},{"internalType":"contract IPriceOracle","name":"newOracle","type":"address"}],"name":"replaceMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"contract ISpotPriceOracle","name":"oldOracle","type":"address"},{"internalType":"contract ISpotPriceOracle","name":"newOracle","type":"address"}],"name":"replacePoolMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"safeSpotPriceThresholds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"setSafeSpotPriceThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenMappings","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":"0x0000000000000000000000002218f90a98b0c070676f249ef44834686daa4285"}