creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/vault/CurveConvexDestinationVault.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\nimport { IWETH9 } from \"src/interfaces/utils/IWETH9.sol\";\nimport { DestinationVault } from \"src/vault/DestinationVault.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { ICurveResolver } from \"src/interfaces/utils/ICurveResolver.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IConvexBooster } from \"src/interfaces/external/convex/IConvexBooster.sol\";\nimport { ConvexStaking } from \"src/destinations/adapters/staking/ConvexAdapter.sol\";\nimport { IBaseRewardPool } from \"src/interfaces/external/convex/IBaseRewardPool.sol\";\nimport { ConvexRewards } from \"src/destinations/adapters/rewards/ConvexRewardsAdapter.sol\";\nimport { CurveV2FactoryCryptoAdapter } from \"src/destinations/adapters/CurveV2FactoryCryptoAdapter.sol\";\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IncentiveCalculatorBase } from \"src/stats/calculators/base/IncentiveCalculatorBase.sol\";\n\n/// @notice Destination Vault to proxy a Curve Pool that goes into Convex\n/// @dev Supports Curve V1 StableSwap, Curve V2 CryptoSwap, and Curve-ng pools\ncontract CurveConvexDestinationVault is DestinationVault {\n    /// @notice Only used to initialize the vault\n    struct InitParams {\n        /// @notice Pool this vault proxies\n        address curvePool;\n        /// @notice Convex reward contract\n        address convexStaking;\n        /// @notice Numeric pool id used to reference Curve pool\n        uint256 convexPoolId;\n        /// @notice Coin index of token we'll perform withdrawals to\n        uint256 baseAssetBurnTokenIndex;\n    }\n\n    string private constant EXCHANGE_NAME = \"curve\";\n\n    /// @notice Coin index of token we'll perform withdrawals to\n    address public immutable defaultStakingRewardToken;\n\n    /// @notice Invalid coin index was provided for withdrawal\n    error InvalidBaseTokenBurnIndex(uint256 provided, uint256 numTokens);\n\n    /// @notice Pool is shutdown\n    error PoolShutdown();\n\n    /* ******************************** */\n    /* State Variables                  */\n    /* ******************************** */\n\n    /// @notice Pool this vault proxies\n    address public curvePool;\n\n    /// @notice LP token this vault proxies\n    /// @dev May be same as curvePool, depends on the pool type\n    address public curveLpToken;\n\n    /// @notice Convex reward contract\n    address public convexStaking;\n\n    /// @notice Convex Booster contract\n    address public immutable convexBooster;\n\n    /// @notice Numeric pool id used to reference Curve pool\n    uint256 public convexPoolId;\n\n    /// @notice Coin index of token we'll perform withdrawals to\n    uint256 public baseAssetBurnTokenIndex;\n\n    /// @dev Tokens that make up the LP token. Meta tokens not broken up\n    address[] private constituentTokens;\n\n    /// @dev Always 0, used as min amounts during withdrawals\n    uint256[] private minAmounts;\n\n    constructor(\n        ISystemRegistry sysRegistry,\n        address _defaultStakingRewardToken,\n        address _convexBooster\n    ) DestinationVault(sysRegistry) {\n        // Zero is valid here if no default token is minted by the reward system\n        // slither-disable-next-line missing-zero-check\n        defaultStakingRewardToken = _defaultStakingRewardToken;\n\n        Errors.verifyNotZero(_convexBooster, \"_convexBooster\");\n        // slither-disable-next-line missing-zero-check\n        convexBooster = _convexBooster;\n    }\n\n    ///@notice Support ETH operations\n    receive() external payable { }\n\n    /// @inheritdoc DestinationVault\n    function initialize(\n        IERC20 baseAsset_,\n        IERC20 underlyer_,\n        IMainRewarder rewarder_,\n        address incentiveCalculator_,\n        address[] memory additionalTrackedTokens_,\n        bytes memory params_\n    ) public virtual override {\n        // Base class has the initializer() modifier to prevent double-setup\n        // If you don't call the base initialize, make sure you protect this call\n        super.initialize(baseAsset_, underlyer_, rewarder_, incentiveCalculator_, additionalTrackedTokens_, params_);\n\n        // We must configure a the curve resolver to setup the vault\n        ICurveResolver curveResolver = _systemRegistry.curveResolver();\n        Errors.verifyNotZero(address(curveResolver), \"curveResolver\");\n\n        // Decode the init params, validate, and save off\n        InitParams memory initParams = abi.decode(params_, (InitParams));\n        Errors.verifyNotZero(initParams.curvePool, \"curvePool\");\n        Errors.verifyNotZero(initParams.convexStaking, \"convexStaking\");\n\n        curvePool = initParams.curvePool;\n        convexStaking = initParams.convexStaking;\n        convexPoolId = initParams.convexPoolId;\n        baseAssetBurnTokenIndex = initParams.baseAssetBurnTokenIndex;\n\n        // Setup pool tokens as tracked. If we want to handle meta pools and their tokens\n        // we will pass them in as additional, not currently a use case\n        // slither-disable-next-line unused-return\n        (address[8] memory tokens, uint256 numTokens, address curveQueriedLpToken,) =\n            curveResolver.resolveWithLpToken(initParams.curvePool);\n\n        Errors.verifyNotZero(numTokens, \"numTokens\");\n\n        // slither-disable-next-line unused-return\n        (address lpToken,,, address crvRewards,, bool _isShutdown) =\n            IConvexBooster(convexBooster).poolInfo(initParams.convexPoolId);\n\n        if (_isShutdown) {\n            revert PoolShutdown();\n        }\n\n        Errors.verifyNotZero(lpToken, \"lpToken\");\n\n        if (curveQueriedLpToken != lpToken) {\n            revert Errors.InvalidParam(\"lpToken\");\n        }\n\n        if (crvRewards != initParams.convexStaking) {\n            revert Errors.InvalidParam(\"crvRewards\");\n        }\n\n        for (uint256 i = 0; i < numTokens; ++i) {\n            address weth = address(_systemRegistry.weth());\n            address token = tokens[i] == LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER ? weth : tokens[i];\n            _addTrackedToken(token);\n            constituentTokens.push(token);\n        }\n\n        if (baseAssetBurnTokenIndex > numTokens - 1) {\n            revert InvalidBaseTokenBurnIndex(baseAssetBurnTokenIndex, numTokens);\n        }\n\n        // Initialize our min amounts for withdrawals to 0 for all tokens\n        minAmounts = new uint256[](numTokens);\n\n        // Checked above\n        // slither-disable-next-line missing-zero-check\n        curveLpToken = lpToken;\n    }\n\n    /// @inheritdoc DestinationVault\n    /// @notice In this vault all underlyer should be staked externally, so internal debt should be 0.\n    function internalDebtBalance() public pure override returns (uint256) {\n        return 0;\n    }\n\n    /// @inheritdoc DestinationVault\n    /// @notice In this vault all underlyer should be staked, and mint is 1:1, so external debt is `totalSupply()`.\n    function externalDebtBalance() public view override returns (uint256) {\n        return totalSupply();\n    }\n\n    /// @notice Get the balance of underlyer currently staked in Convex\n    /// @return Balance of underlyer currently staked in Convex\n    function externalQueriedBalance() public view override returns (uint256) {\n        return IBaseRewardPool(convexStaking).balanceOf(address(this));\n    }\n\n    /// @inheritdoc DestinationVault\n    function exchangeName() external pure override returns (string memory) {\n        return EXCHANGE_NAME;\n    }\n\n    /// @inheritdoc DestinationVault\n    function underlyingTokens() external view override returns (address[] memory result) {\n        uint256 len = constituentTokens.length;\n        result = new address[](len);\n        for (uint256 i = 0; i < len; ++i) {\n            result[i] = constituentTokens[i];\n        }\n    }\n\n    /// @notice Callback during a deposit after the sender has been minted shares (if applicable)\n    /// @dev Should be used for staking tokens into protocols, etc\n    /// @param amount underlying tokens received\n    function _onDeposit(uint256 amount) internal virtual override {\n        ConvexStaking.depositAndStake(IConvexBooster(convexBooster), _underlying, convexStaking, convexPoolId, amount);\n    }\n\n    /// @inheritdoc DestinationVault\n    function _ensureLocalUnderlyingBalance(uint256 amount) internal virtual override {\n        ConvexStaking.withdrawStake(_underlying, convexStaking, amount);\n    }\n\n    /// @inheritdoc DestinationVault\n    function _collectRewards() internal virtual override returns (uint256[] memory amounts, address[] memory tokens) {\n        (amounts, tokens) =\n            ConvexRewards.claimRewards(convexStaking, defaultStakingRewardToken, msg.sender, _trackedTokens);\n    }\n\n    /// @inheritdoc DestinationVault\n    function _burnUnderlyer(uint256 underlyerAmount)\n        internal\n        virtual\n        override\n        returns (address[] memory tokens, uint256[] memory amounts)\n    {\n        // We withdraw everything in one coin to ease swapping\n        // re: minAmount == 0, this call is only made during a user initiated withdraw where slippage is\n        // controlled for at the router\n\n        // We always want our tokens back in WETH so useEth false\n        (tokens, amounts) = CurveV2FactoryCryptoAdapter.removeLiquidity(\n            minAmounts, underlyerAmount, curvePool, curveLpToken, IWETH9(_systemRegistry.weth())\n        );\n    }\n\n    /// @inheritdoc DestinationVault\n    function getPool() public view override returns (address) {\n        return curvePool;\n    }\n\n    function _validateCalculator(address incentiveCalculator) internal view override {\n        if (IncentiveCalculatorBase(incentiveCalculator).resolveLpToken() != _underlying) {\n            revert InvalidIncentiveCalculator();\n        }\n    }\n}\n"
      },
      "src/utils/Errors.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Address } from \"openzeppelin-contracts/utils/Address.sol\";\n\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 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\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        bytes memory call = abi.encodeWithSignature(\"getSystemRegistry()\");\n\n        address registry1 = abi.decode(component1.functionStaticCall(call), (address));\n        address registry2 = abi.decode(component2.functionStaticCall(call), (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"
      },
      "src/libs/LibAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/interfaces/utils/IWETH9.sol": {
        "content": "// 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"
      },
      "src/vault/DestinationVault.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Roles } from \"src/libs/Roles.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\nimport { SecurityBase } from \"src/security/SecurityBase.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { ERC20 } from \"openzeppelin-contracts/token/ERC20/ERC20.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IDestinationVault } from \"src/interfaces/vault/IDestinationVault.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"openzeppelin-contracts/proxy/utils/Initializable.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IDexLSTStats } from \"src/interfaces/stats/IDexLSTStats.sol\";\n\nabstract contract DestinationVault is SecurityBase, ERC20, Initializable, IDestinationVault {\n    using SafeERC20 for IERC20;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    event Recovered(address[] tokens, uint256[] amounts, address[] destinations);\n    event UnderlyerRecovered(address destination, uint256 amount);\n    event UnderlyingWithdraw(uint256 amount, address owner, address to);\n    event BaseAssetWithdraw(uint256 amount, address owner, address to);\n    event UnderlyingDeposited(uint256 amount, address sender);\n    event Shutdown(VaultShutdownStatus reason);\n\n    error ArrayLengthMismatch();\n    error PullingNonTrackedToken(address token);\n    error RecoveringTrackedToken(address token);\n    error RecoveringMoreThanAvailable(address token, uint256 amount, uint256 availableAmount);\n    error NothingToRecover();\n    error DuplicateToken(address token);\n    error VaultShutdown();\n    error InvalidIncentiveCalculator();\n    error PricesOutOfRange(uint256 spot, uint256 safe);\n\n    ISystemRegistry internal immutable _systemRegistry;\n\n    /* ******************************** */\n    /* State Variables                  */\n    /* ******************************** */\n\n    string internal _name;\n    string internal _symbol;\n    uint8 internal _underlyingDecimals;\n\n    address internal _baseAsset;\n    address internal _underlying;\n    // slither-disable-next-line similar-names\n    address internal _incentiveCalculator;\n\n    IMainRewarder internal _rewarder;\n\n    EnumerableSet.AddressSet internal _trackedTokens;\n\n    /// @dev whether or not the vault has been shutdown\n    bool internal _shutdown;\n\n    /// @dev The reason for shutdown (or `Active` if not shutdown)\n    VaultShutdownStatus internal _shutdownStatus;\n\n    constructor(ISystemRegistry sysRegistry) SecurityBase(address(sysRegistry.accessController())) ERC20(\"\", \"\") {\n        _systemRegistry = sysRegistry;\n    }\n\n    modifier onlyLMPVault() {\n        if (!_systemRegistry.lmpVaultRegistry().isVault(msg.sender)) {\n            revert Errors.AccessDenied();\n        }\n        _;\n    }\n\n    modifier notShutdown() {\n        if (_shutdown) {\n            revert VaultShutdown();\n        }\n        _;\n    }\n\n    function initialize(\n        IERC20 baseAsset_,\n        IERC20 underlyer_,\n        IMainRewarder rewarder_,\n        address incentiveCalculator_,\n        address[] memory additionalTrackedTokens_,\n        bytes memory\n    ) public virtual initializer {\n        Errors.verifyNotZero(address(baseAsset_), \"baseAsset_\");\n        Errors.verifyNotZero(address(underlyer_), \"underlyer_\");\n        Errors.verifyNotZero(address(rewarder_), \"rewarder_\");\n        Errors.verifyNotZero(address(incentiveCalculator_), \"incentiveCalculator_\");\n\n        _name = string.concat(\"Tokemak-\", baseAsset_.name(), \"-\", underlyer_.name());\n        _symbol = string.concat(\"toke-\", baseAsset_.symbol(), \"-\", underlyer_.symbol());\n        _underlyingDecimals = underlyer_.decimals();\n\n        _baseAsset = address(baseAsset_);\n        _underlying = address(underlyer_);\n        _rewarder = rewarder_;\n\n        _validateCalculator(incentiveCalculator_);\n\n        // non null address verified above\n        // slither-disable-next-line missing-zero-check\n        _incentiveCalculator = incentiveCalculator_;\n\n        // Setup the tracked tokens\n        _addTrackedToken(address(baseAsset_));\n        _addTrackedToken(address(underlyer_));\n        uint256 attLen = additionalTrackedTokens_.length;\n        for (uint256 i = 0; i < attLen; ++i) {\n            _addTrackedToken(additionalTrackedTokens_[i]);\n        }\n    }\n\n    /// @inheritdoc ERC20\n    function name() public view virtual override(ERC20, IERC20) returns (string memory) {\n        return _name;\n    }\n\n    /// @inheritdoc ERC20\n    function symbol() public view virtual override(ERC20, IERC20) returns (string memory) {\n        return _symbol;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function baseAsset() external view virtual override returns (address) {\n        return _baseAsset;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function underlying() external view virtual override returns (address) {\n        return _underlying;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function balanceOfUnderlyingDebt() public view virtual override returns (uint256) {\n        return internalDebtBalance() + externalDebtBalance();\n    }\n\n    /// @inheritdoc IDestinationVault\n    function internalDebtBalance() public view virtual override returns (uint256);\n\n    /// @inheritdoc IDestinationVault\n    function externalDebtBalance() public view virtual override returns (uint256);\n\n    /// @inheritdoc IDestinationVault\n    function internalQueriedBalance() public view virtual override returns (uint256) {\n        return IERC20(_underlying).balanceOf(address(this));\n    }\n\n    /// @inheritdoc IDestinationVault\n    function externalQueriedBalance() public view virtual override returns (uint256);\n\n    /// @inheritdoc IDestinationVault\n    function rewarder() external view virtual override returns (address) {\n        return address(_rewarder);\n    }\n\n    /// @inheritdoc ERC20\n    function decimals() public view virtual override(ERC20, IERC20) returns (uint8) {\n        return _underlyingDecimals;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function debtValue() public virtual override returns (uint256 value) {\n        value = _debtValue(balanceOfUnderlyingDebt());\n    }\n\n    /// @inheritdoc IDestinationVault\n    function debtValue(uint256 shares) external virtual returns (uint256 value) {\n        value = _debtValue(shares);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function exchangeName() external view virtual override returns (string memory);\n\n    /// @inheritdoc IDestinationVault\n    function underlyingTokens() external view virtual override returns (address[] memory);\n\n    /// @inheritdoc IDestinationVault\n    function collectRewards()\n        external\n        virtual\n        override\n        hasRole(Roles.LIQUIDATOR_ROLE)\n        returns (uint256[] memory amounts, address[] memory tokens)\n    {\n        (amounts, tokens) = _collectRewards();\n    }\n\n    /// @notice Collects any earned rewards from staking, incentives, etc. Transfers to sender\n    /// @return amounts amount of rewards claimed for each token\n    /// @return tokens tokens claimed\n    function _collectRewards() internal virtual returns (uint256[] memory amounts, address[] memory tokens);\n\n    /// @inheritdoc IDestinationVault\n    function shutdown(VaultShutdownStatus reason) external onlyOwner {\n        if (reason == VaultShutdownStatus.Active) {\n            revert InvalidShutdownStatus(reason);\n        }\n\n        _shutdown = true;\n        _shutdownStatus = reason;\n\n        emit Shutdown(reason);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function isShutdown() external view returns (bool) {\n        return _shutdown;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function shutdownStatus() external view returns (VaultShutdownStatus) {\n        return _shutdownStatus;\n    }\n\n    function trackedTokens() public view virtual returns (address[] memory trackedTokensArr) {\n        uint256 arLen = _trackedTokens.length();\n        trackedTokensArr = new address[](arLen);\n        for (uint256 i = 0; i < arLen; ++i) {\n            trackedTokensArr[i] = _trackedTokens.at(i);\n        }\n    }\n\n    /// @notice Checks if given token is tracked by Vault\n    /// @param token Address to verify\n    /// @return bool True if token is within Vault's tracked assets\n    function isTrackedToken(address token) public view virtual returns (bool) {\n        return _trackedTokens.contains(token);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function depositUnderlying(uint256 amount) external onlyLMPVault notShutdown returns (uint256 shares) {\n        Errors.verifyNotZero(amount, \"amount\");\n\n        emit UnderlyingDeposited(amount, msg.sender);\n\n        IERC20(_underlying).safeTransferFrom(msg.sender, address(this), amount);\n        _mint(msg.sender, amount);\n\n        _onDeposit(amount);\n\n        shares = amount;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function withdrawUnderlying(uint256 shares, address to) external onlyLMPVault returns (uint256 amount) {\n        Errors.verifyNotZero(shares, \"shares\");\n        Errors.verifyNotZero(to, \"to\");\n\n        amount = shares;\n\n        emit UnderlyingWithdraw(amount, msg.sender, to);\n\n        // Does a balance check, will revert if trying to burn too much\n        _burn(msg.sender, shares);\n\n        _ensureLocalUnderlyingBalance(amount);\n\n        IERC20(_underlying).safeTransfer(to, amount);\n    }\n\n    /// @notice Ensure that we have the specified balance of the underlyer in the vault itself\n    /// @param amount amount of token\n    function _ensureLocalUnderlyingBalance(uint256 amount) internal virtual;\n\n    /// @notice Callback during a deposit after the sender has been minted shares (if applicable)\n    /// @dev Should be used for staking tokens into protocols, etc\n    /// @param amount underlying tokens received\n    function _onDeposit(uint256 amount) internal virtual;\n\n    /// @inheritdoc IDestinationVault\n    function withdrawBaseAsset(uint256 shares, address to) external returns (uint256 amount) {\n        return _withdrawBaseAsset(msg.sender, shares, to);\n    }\n\n    /// @inheritdoc IDestinationVault\n    //slither-disable-start assembly\n    function estimateWithdrawBaseAsset(uint256 shares, address to, address account) public returns (uint256) {\n        // Check if the function is being called from an external source and not recursively.\n        if (msg.sender != address(this)) {\n            // If `account` is not the zero address during an external call, there's a logic flaw.\n            // External calls to this function must always provide `account` as the zero address.\n            // Only during the recursive call will the `account` be provided.\n            if (account != address(0)) {\n                revert LogicDefect();\n            }\n\n            // Perform a recursive call to this function. The intention is to reach the \"else\" block.\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(\n                abi.encodeWithSelector(this.estimateWithdrawBaseAsset.selector, shares, to, msg.sender)\n            );\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 UnreachableError();\n            }\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            bytes4 balanceAmountSig = bytes4(keccak256(\"BaseAmountReceived(uint256)\"));\n\n            // If the error matches the expected signature, extract the amount from the revert reason and return.\n            if (errorSignature == balanceAmountSig) {\n                // Extract subsequent 32 bytes for uint256\n                uint256 amount;\n                assembly {\n                    amount := mload(add(returnData, 0x24))\n                }\n                return amount;\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 logic to compute the amount. This will be reverted to simulate the action.\n            uint256 amount = _withdrawBaseAsset(account, shares, to);\n\n            // Revert with the computed amount as an error.\n            revert BaseAmountReceived(amount);\n        }\n    }\n    //slither-disable-end assembly\n\n    /// @notice Burn the specified amount of underlyer for the constituent tokens\n    /// @dev May return one or multiple assets. Be as efficient as you can here.\n    /// @param underlyerAmount amount of underlyer to burn\n    /// @return tokens the tokens to swap for base asset\n    /// @return amounts the amounts we have to swap\n    function _burnUnderlyer(uint256 underlyerAmount)\n        internal\n        virtual\n        returns (address[] memory tokens, uint256[] memory amounts);\n\n    function recover(\n        address[] calldata tokens,\n        uint256[] calldata amounts,\n        address[] calldata destinations\n    ) external override hasRole(Roles.TOKEN_RECOVERY_ROLE) {\n        uint256 length = tokens.length;\n        if (length == 0 || length != amounts.length || length != destinations.length) {\n            revert ArrayLengthMismatch();\n        }\n        emit Recovered(tokens, amounts, destinations);\n\n        for (uint256 i = 0; i < tokens.length; ++i) {\n            IERC20 token = IERC20(tokens[i]);\n\n            // Check if it's a really non-tracked token\n            if (isTrackedToken(tokens[i])) revert RecoveringTrackedToken(tokens[i]);\n\n            uint256 tokenBalance = token.balanceOf(address(this));\n            if (tokenBalance < amounts[i]) revert RecoveringMoreThanAvailable(tokens[i], amounts[i], tokenBalance);\n\n            token.safeTransfer(destinations[i], amounts[i]);\n        }\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getValidatedSpotPrice() external returns (uint256 price) {\n        //slither-disable-next-line unused-return\n        (uint256 spotPriceInQuote, uint256 safePriceInQuote, bool isSpotSafe) =\n            _systemRegistry.rootPriceOracle().getRangePricesLP(address(_underlying), getPool(), _baseAsset);\n        if (!isSpotSafe) {\n            revert PricesOutOfRange(spotPriceInQuote, safePriceInQuote);\n        }\n        price = spotPriceInQuote;\n    }\n\n    /// @inheritdoc IDestinationVault\n    function recoverUnderlying(address destination) external override hasRole(Roles.TOKEN_RECOVERY_ROLE) {\n        Errors.verifyNotZero(destination, \"destination\");\n\n        // TODO: Is there a situation where we would expect debt on the vault to be > queried balance on the vault?\n        uint256 externalAmount = externalQueriedBalance() - externalDebtBalance();\n        uint256 totalAmount = externalAmount + internalQueriedBalance() - internalDebtBalance();\n        if (totalAmount > 0) {\n            if (externalAmount > 0) {\n                _ensureLocalUnderlyingBalance(externalAmount);\n            }\n            emit UnderlyerRecovered(destination, totalAmount);\n            IERC20(_underlying).safeTransfer(destination, totalAmount);\n        } else {\n            revert NothingToRecover();\n        }\n    }\n\n    function _addTrackedToken(address token) internal {\n        //slither-disable-next-line unused-return\n        _trackedTokens.add(token);\n    }\n\n    function _debtValue(uint256 shares) private returns (uint256 value) {\n        //slither-disable-next-line unused-return\n        (uint256 spotPriceInQuote, uint256 safePriceInQuote, bool isSpotSafe) =\n            _systemRegistry.rootPriceOracle().getRangePricesLP(address(_underlying), getPool(), _baseAsset);\n        if (!isSpotSafe) {\n            revert PricesOutOfRange(spotPriceInQuote, safePriceInQuote);\n        }\n\n        return (safePriceInQuote * shares) / (10 ** _underlyingDecimals);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getRangePricesLP()\n        external\n        virtual\n        override\n        returns (uint256 spotPrice, uint256 safePrice, bool isSpotSafe)\n    {\n        (spotPrice, safePrice, isSpotSafe) =\n            _systemRegistry.rootPriceOracle().getRangePricesLP(address(_underlying), getPool(), _baseAsset);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getUnderlyerFloorPrice() external virtual override returns (uint256 price) {\n        price = _systemRegistry.rootPriceOracle().getFloorPrice(address(_underlying), getPool(), _baseAsset);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getUnderlyerCeilingPrice() external virtual override returns (uint256 price) {\n        price = _systemRegistry.rootPriceOracle().getCeilingPrice(address(_underlying), getPool(), _baseAsset);\n    }\n\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {\n        if (from == to) {\n            return;\n        }\n\n        if (from != address(0)) {\n            _rewarder.withdraw(from, amount, true);\n        }\n    }\n\n    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override {\n        if (from == to) {\n            return;\n        }\n\n        if (to != address(0)) {\n            _rewarder.stake(to, amount);\n        }\n    }\n\n    function _withdrawBaseAsset(address account, uint256 shares, address to) internal returns (uint256 amount) {\n        Errors.verifyNotZero(shares, \"shares\");\n\n        emit BaseAssetWithdraw(shares, account, to);\n\n        // Does a balance check, will revert if trying to burn too much\n        _burn(account, shares);\n\n        // Accounts for shares that may be staked\n        _ensureLocalUnderlyingBalance(shares);\n\n        (address[] memory tokens, uint256[] memory amounts) = _burnUnderlyer(shares);\n\n        uint256 nTokens = tokens.length;\n        Errors.verifyArrayLengths(nTokens, amounts.length, \"token+amounts\");\n\n        // Swap what we receive if not already in base asset\n        // This fn is only called during a users withdrawal. The user should be making this\n        // call via the LMP Router, or through one of the other routes where\n        // slippage is controlled for. 0 min amount is expected here.\n        ISwapRouter swapRouter = _systemRegistry.swapRouter();\n        for (uint256 i = 0; i < nTokens; ++i) {\n            address token = tokens[i];\n\n            if (token == _baseAsset) {\n                amount += amounts[i];\n            } else {\n                if (amounts[i] > 0) {\n                    LibAdapter._approve(IERC20(token), address(swapRouter), amounts[i]);\n                    amount += swapRouter.swapForQuote(token, amounts[i], _baseAsset, 0);\n                }\n            }\n        }\n\n        if (amount > 0) {\n            IERC20(_baseAsset).safeTransfer(to, amount);\n        }\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getStats() external virtual returns (IDexLSTStats) {\n        return IDexLSTStats(_incentiveCalculator);\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getMarketplaceRewards()\n        external\n        virtual\n        returns (uint256[] memory rewardTokens, uint256[] memory rewardRates)\n    {\n        // TODO Implement\n        return (new uint256[](0), new uint256[](0));\n    }\n\n    /// @inheritdoc IDestinationVault\n    function getPool() public view virtual returns (address poolAddress);\n\n    /// @notice Validates incentive calculator for the destination vault\n    function _validateCalculator(address calculator) internal virtual;\n}\n"
      },
      "src/interfaces/ISystemRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 { ILMPVaultRegistry } from \"./vault/ILMPVaultRegistry.sol\";\nimport { IAccessController } from \"./security/IAccessController.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { ICurveResolver } from \"src/interfaces/utils/ICurveResolver.sol\";\nimport { ILMPVaultRouter } from \"src/interfaces/vault/ILMPVaultRouter.sol\";\nimport { ILMPVaultFactory } from \"src/interfaces/vault/ILMPVaultFactory.sol\";\nimport { ISystemSecurity } from \"src/interfaces/security/ISystemSecurity.sol\";\nimport { IDestinationRegistry } from \"./destinations/IDestinationRegistry.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { ILMPVaultRegistry } from \"src/interfaces/vault/ILMPVaultRegistry.sol\";\nimport { IDestinationVaultRegistry } from \"./vault/IDestinationVaultRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { IDestinationRegistry } from \"src/interfaces/destinations/IDestinationRegistry.sol\";\nimport { IStatsCalculatorRegistry } from \"src/interfaces/stats/IStatsCalculatorRegistry.sol\";\nimport { IAsyncSwapperRegistry } from \"src/interfaces/liquidation/IAsyncSwapperRegistry.sol\";\nimport { IDestinationVaultRegistry } from \"src/interfaces/vault/IDestinationVaultRegistry.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IIncentivesPricingStats } from \"src/interfaces/stats/IIncentivesPricingStats.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 gpToke() external view returns (IAccToke);\n\n    /// @notice Get the LMP Vault registry for this system\n    /// @return registry instance of the registry for this system\n    function lmpVaultRegistry() external view returns (ILMPVaultRegistry 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 LMP Vault Router\n    /// @return router instance of the lmp vault router\n    function lmpVaultRouter() external view returns (ILMPVaultRouter router);\n\n    /// @notice Vault factory lookup by type\n    /// @return vaultFactory instance of the vault factory for this vault type\n    function getLMPVaultFactoryByType(bytes32 vaultType) external view returns (ILMPVaultFactory 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 Register given address as a Reward Token\n    /// @dev Reverts if address is 0 or token was already registered\n    /// @param rewardToken token address to add\n    function addRewardToken(address rewardToken) external;\n\n    /// @notice Removes given address from Reward Token list\n    /// @dev Reverts if address was not registered\n    /// @param rewardToken token address to remove\n    function removeRewardToken(address rewardToken) external;\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"
      },
      "src/interfaces/utils/ICurveResolver.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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    /// If you need a dynamic array of tokens use Arrays.convertFixedCurveTokenArrayToDynamic(tokens,numTokens)\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    /// If you need a dynamic array of tokens use Arrays.convertFixedCurveTokenArrayToDynamic(tokens,numTokens)\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"
      },
      "src/interfaces/rewarders/IMainRewarder.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IBaseRewarder } from \"./IBaseRewarder.sol\";\nimport { IExtraRewarder } from \"src/interfaces/rewarders/IExtraRewarder.sol\";\n\ninterface IMainRewarder is IBaseRewarder {\n    error ExtraRewardsNotAllowed();\n\n    event ExtraRewardAdded(address reward);\n    event ExtraRewardsCleared();\n    event ExtraRewardRemoved(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 Clears the extraRewards array.\n     */\n    function clearExtraRewards() 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 claimExtras If true, claims rewards from linked extra reward contracts.\n     */\n    function getReward(address account, 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 rewarder at the specified index\n     */\n    function getExtraRewarder(uint256 index) external view returns (IExtraRewarder);\n}\n"
      },
      "src/interfaces/external/convex/IConvexBooster.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\n/// @notice main Convex contract(booster.sol) basic interface\ninterface IConvexBooster {\n    /// @notice deposit into convex, receive a tokenized deposit. parameter to stake immediately\n    function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool);\n\n    /// @notice get poolInfo for a poolId\n    function poolInfo(uint256 _pid)\n        external\n        view\n        returns (address lptoken, address token, address gauge, address crvRewards, address stash, bool shutdown);\n}\n"
      },
      "src/destinations/adapters/staking/ConvexAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\n\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\nimport { IBaseRewardPool } from \"src/interfaces/external/convex/IBaseRewardPool.sol\";\nimport { IConvexBooster } from \"src/interfaces/external/convex/IConvexBooster.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\n\nlibrary ConvexStaking {\n    event DeployLiquidity(address lpToken, address staking, uint256 poolId, uint256 amount);\n    event WithdrawLiquidity(address lpToken, address staking, uint256 amount);\n\n    error withdrawStakeFailed();\n    error DepositAndStakeFailed();\n    error PoolIdLpTokenMismatch();\n    error PoolIdStakingMismatch();\n    error PoolShutdown();\n\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    /**\n     * @notice Deposits and stakes Curve LP tokens to Convex\n     * @dev Calls to external contract\n     * @param booster Convex Booster address\n     * @param lpToken Curve LP token to deposit\n     * @param staking Convex reward contract associated with the Curve LP token\n     * @param poolId Convex poolId for the associated Curve LP token\n     * @param amount Quantity of Curve LP token to deposit and stake\n     */\n    function depositAndStake(\n        IConvexBooster booster,\n        address lpToken,\n        address staking,\n        uint256 poolId,\n        uint256 amount\n    ) public {\n        if (address(booster) == address(0)) revert InvalidAddress(address(booster));\n        if (lpToken == address(0)) revert InvalidAddress(address(lpToken));\n        if (staking == address(0)) revert InvalidAddress(address(staking));\n        if (amount == 0) revert MustBeMoreThanZero();\n\n        emit DeployLiquidity(lpToken, staking, poolId, amount);\n\n        _validatePoolInfo(booster, poolId, lpToken, staking);\n\n        LibAdapter._approve(IERC20(lpToken), address(booster), amount);\n\n        IBaseRewardPool rewards = IBaseRewardPool(staking);\n        uint256 rewardsBeforeBalance = rewards.balanceOf(address(this));\n\n        _runDeposit(booster, poolId, amount);\n\n        if (rewards.balanceOf(address(this)) - rewardsBeforeBalance != amount) {\n            revert BalanceMustIncrease();\n        }\n    }\n\n    /**\n     * @notice Withdraws a Curve LP token from Convex\n     * @dev Does not claim available rewards\n     * @dev Calls to external contract\n     * @param lpToken Curve LP token to withdraw\n     * @param staking Convex reward contract associated with the Curve LP token\n     * @param amount Quantity of Curve LP token to withdraw\n     */\n    function withdrawStake(address lpToken, address staking, uint256 amount) public {\n        // slither-disable-start incorrect-equality\n        if (lpToken == address(0)) revert InvalidAddress(lpToken);\n        if (staking == address(0)) revert InvalidAddress(staking);\n        if (amount == 0) revert MustBeMoreThanZero();\n        // slither-disable-end incorrect-equality\n\n        IERC20 lpTokenErc = IERC20(lpToken);\n        uint256 beforeLpBalance = lpTokenErc.balanceOf(address(this));\n\n        IBaseRewardPool rewards = IBaseRewardPool(staking);\n\n        emit WithdrawLiquidity(lpToken, staking, amount);\n\n        bool success = rewards.withdrawAndUnwrap(amount, false);\n        if (!success) revert withdrawStakeFailed();\n\n        uint256 updatedLpBalance = lpTokenErc.balanceOf(address(this));\n        if (updatedLpBalance - beforeLpBalance != amount) {\n            revert BalanceMustIncrease();\n        }\n    }\n\n    /// @dev Separate function to avoid stack-too-deep errors\n    function _runDeposit(IConvexBooster booster, uint256 poolId, uint256 amount) private {\n        bool success = booster.deposit(poolId, amount, true);\n        if (!success) revert DepositAndStakeFailed();\n    }\n\n    /// @dev Separate function to avoid stack-too-deep errors\n    function _validatePoolInfo(IConvexBooster booster, uint256 poolId, address lpToken, address staking) private view {\n        // Partial return values are intentionally ignored. This call provides the most efficient way to get the data.\n        // slither-disable-next-line unused-return\n        (address poolLpToken,,, address crvRewards,, bool shutdown) = booster.poolInfo(poolId);\n        if (lpToken != poolLpToken) revert PoolIdLpTokenMismatch();\n        if (staking != crvRewards) revert PoolIdStakingMismatch();\n        if (shutdown) revert PoolShutdown();\n    }\n}\n"
      },
      "src/interfaces/external/convex/IBaseRewardPool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\ninterface IBaseRewardPool {\n    /// @notice The address of the staking token\n    function stakingToken() external view returns (address);\n\n    /// @notice The address of the reward token\n    function rewardToken() external view returns (IERC20);\n\n    /// @notice The length of the extra rewards array\n    function extraRewardsLength() external view returns (uint256);\n\n    /// @notice The pool PID\n    function pid() external view returns (uint256);\n\n    /// @notice The address of the extra rewards token at a given index\n    function extraRewards(uint256 i) external view returns (address);\n\n    /// @notice Called by a staker to get their allocated rewards\n    function getReward() external returns (bool);\n\n    /// @notice Gives a staker their rewards, with the option of claiming extra rewards\n    function getReward(address _account, bool _claimExtras) external returns (bool);\n\n    /// @notice Get total rewards supply\n    function totalSupply() external view returns (uint256);\n\n    /// @notice Get balance of an address\n    function balanceOf(address _account) external view returns (uint256);\n\n    /// @notice Withdraw directly to curve LP token\n    function withdrawAndUnwrap(uint256 _amount, bool _claim) external returns (bool);\n\n    /// @notice timestamp when reward period ends\n    function periodFinish() external view returns (uint256);\n\n    /// @notice The rate of reward distribution per block.\n    function rewardRate() external view returns (uint256);\n\n    /// @notice The amount of rewards distributed per staked token stored.\n    function rewardPerToken() external view returns (uint256);\n\n    /// @notice The duration for locking the token rewards.\n    function duration() external view returns (uint256);\n}\n"
      },
      "src/destinations/adapters/rewards/ConvexRewardsAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { RewardAdapter } from \"src/destinations/adapters/rewards/RewardAdapter.sol\";\nimport { IBaseRewardPool } from \"src/interfaces/external/convex/IBaseRewardPool.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { IConvexStashToken } from \"src/interfaces/external/convex/IConvexStashToken.sol\";\n\n//slither-disable-start missing-inheritance,low-level-calls\nlibrary ConvexRewards {\n    using SafeERC20 for IERC20;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    /// @notice Claim rewards for Convex staked LP tokens\n    /// @dev tokens can be returned in any order. uint256(0)/address(0) can be returned\n    /// @param gauge the reward contract in Convex\n    /// @param defaultToken the reward token always provided. CVX for Convex\n    /// @param sendTo the destination of the rewarded tokens\n    /// @param trackedTokens tokens that should not be sent off to the 'sendTo'\n    /// @return amounts the amount of each token that was claimed (includes balance already held by caller)\n    /// @return tokens the tokens that were claimed\n    function claimRewards(\n        address gauge,\n        address defaultToken,\n        address sendTo,\n        EnumerableSet.AddressSet storage trackedTokens\n    ) public returns (uint256[] memory amounts, address[] memory tokens) {\n        return _claimRewards(gauge, defaultToken, sendTo, trackedTokens);\n    }\n\n    /// @notice Claim rewards for Convex staked LP tokens\n    /// @dev tokens can be returned in any order. uint256(0)/address(0) can be returned\n    /// @param gauge the reward contract in Convex\n    /// @param defaultToken the reward token always provided. CVX for Convex\n    /// @param trackedTokens tokens that should not be sent off to the 'sendTo'\n    /// @return amounts the amount of each token that was claimed (includes balance already held by caller)\n    /// @return tokens the tokens that were claimed\n    function claimRewards(\n        address gauge,\n        address defaultToken,\n        EnumerableSet.AddressSet storage trackedTokens\n    ) public returns (uint256[] memory amounts, address[] memory tokens) {\n        return _claimRewards(gauge, defaultToken, address(this), trackedTokens);\n    }\n\n    /// @notice Claim rewards for Convex staked LP tokens\n    /// @dev tokens can be returned in any order. uint256(0)/address(0) can be returned\n    /// @param gauge the reward contract in Convex\n    /// @param defaultToken the reward token always provided. CVX for Convex\n    /// @param sendTo the destination of the rewarded tokens\n    /// @param trackedTokens tokens that should not be sent off to the 'sendTo'\n    /// @return amounts the amount of each token that was claimed (includes balance already held by caller)\n    /// @return tokens the tokens that were claimed\n    function _claimRewards(\n        address gauge,\n        address defaultToken,\n        address sendTo,\n        EnumerableSet.AddressSet storage trackedTokens\n    ) internal returns (uint256[] memory amounts, address[] memory tokens) {\n        Errors.verifyNotZero(gauge, \"gauge\");\n        Errors.verifyNotZero(sendTo, \"sendTo\");\n\n        address account = address(this);\n\n        IBaseRewardPool rewardPool = IBaseRewardPool(gauge);\n        uint256 extraRewardsLength = rewardPool.extraRewardsLength();\n\n        // Convex mints their own token as part of the claim, thats the \"defaultToken\"\n        // token in this case.\n        uint256 totalLength = extraRewardsLength + (defaultToken != address(0) ? 2 : 1);\n\n        uint256[] memory amountsClaimed = new uint256[](totalLength);\n        address[] memory rewardTokens = new address[](totalLength);\n\n        // add pool rewards tokens and extra rewards tokens to rewardTokens array\n        IERC20 rewardToken = rewardPool.rewardToken();\n        rewardTokens[extraRewardsLength] = address(rewardToken);\n\n        // Add in the default token\n        if (defaultToken != address(0)) {\n            rewardTokens[totalLength - 1] = defaultToken;\n        }\n\n        // claim rewards\n        if (!rewardPool.getReward(account, true)) {\n            revert RewardAdapter.ClaimRewardsFailed();\n        }\n\n        // Get the amount we are reporting as the claimed amount.\n        // Note, this may include a balance of the token already held by the account\n        for (uint256 i = 0; i < totalLength; ++i) {\n            if (i < extraRewardsLength) {\n                rewardTokens[i] = address(IBaseRewardPool(rewardPool.extraRewards(i)).rewardToken());\n            }\n\n            // Some tokens we want to be sure aren't transferred out or reported as rewards\n            // These are the \"trackedTokens\" from the DestinationVault and usually just include the LP token\n            // It would be odd for the LP token to also be a reward token but still have to check\n            if (trackedTokens.contains(rewardTokens[i])) {\n                rewardTokens[i] = address(0);\n            }\n\n            if (rewardTokens[i] != address(0)) {\n                (amountsClaimed[i], rewardTokens[i]) = safeRewardTokenBalanceOf(rewardTokens[i], account);\n\n                // We don't need to report on it if we don't have a balance\n                // slither-disable-next-line incorrect-equality\n                if (amountsClaimed[i] == 0) {\n                    rewardTokens[i] = address(0);\n                }\n\n                if (sendTo != address(this) && amountsClaimed[i] > 0 && rewardTokens[i] != address(0)) {\n                    IERC20(rewardTokens[i]).safeTransfer(sendTo, amountsClaimed[i]);\n                }\n            }\n        }\n\n        RewardAdapter.emitRewardsClaimed(rewardTokens, amountsClaimed);\n\n        return (amountsClaimed, rewardTokens);\n    }\n\n    /// @notice Get the balance of a reward token taking into stash tokens on Convex\n    /// @dev Sometimes a reward token is a stash token. Sometimes a stash token actually has rewards that are received.\n    /// Sometimes they don't. We're working through the scenarios here.\n    /// @param token The ERC20 to check balance of\n    /// @param wallet The wallet to check the balance of\n    /// @return resultAmount Balance of the token or 0 in case of invalid query\n    /// @return resultToken Actual reward token\n    function safeRewardTokenBalanceOf(\n        address token,\n        address wallet\n    ) private view returns (uint256 resultAmount, address resultToken) {\n        resultAmount = IERC20(token).balanceOf(wallet);\n\n        // If we end up with zero here then one of these scenarios is true:\n        // - Our balance could just very well be 0\n        // - This is a Convex stash token that is hard coded to return 0 for anything but the gauge\n        // Either way, if we have 0, lets check to see if this is a Convex stash token which may have rewards\n        // and we check by trying to query the token() and getting the balance of the underlyer\n        // slither-disable-next-line incorrect-equality\n        if (resultAmount == 0) {\n            // Try for Convex stash token\n            (bool successToken, bytes memory dataToken) =\n                token.staticcall(abi.encodeWithSelector(IConvexStashToken.token.selector));\n\n            if (successToken && dataToken.length > 0) {\n                address stashToken = abi.decode(dataToken, (address));\n\n                (bool isInvalidResult, bytes memory isInvalidData) =\n                    token.staticcall(abi.encodeWithSelector(IConvexStashToken.isInvalid.selector));\n\n                if (isInvalidResult && isInvalidData.length > 0 && !abi.decode(isInvalidData, (bool))) {\n                    resultToken = stashToken;\n                    resultAmount = IERC20(stashToken).balanceOf(wallet);\n                }\n            }\n        } else {\n            resultToken = token;\n        }\n    }\n}\n//slither-disable-end missing-inheritance,low-level-calls\n"
      },
      "src/destinations/adapters/CurveV2FactoryCryptoAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Address } from \"openzeppelin-contracts/utils/Address.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\nimport { IPoolAdapter } from \"src/interfaces/destinations/IPoolAdapter.sol\";\nimport { ICryptoSwapPool, IPool } from \"src/interfaces/external/curve/ICryptoSwapPool.sol\";\nimport { IWETH9 } from \"src/interfaces/utils/IWETH9.sol\";\nimport { LibAdapter } from \"src/libs/LibAdapter.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\n\n//slither-disable-start similar-names\nlibrary CurveV2FactoryCryptoAdapter {\n    event DeployLiquidity(\n        uint256[] amountsDeposited,\n        address[] tokens,\n        // 0 - lpMintAmount\n        // 1 - lpShare\n        // 2 - lpTotalSupply\n        uint256[3] lpAmounts,\n        address poolAddress\n    );\n\n    event WithdrawLiquidity(\n        uint256[] amountsWithdrawn,\n        address[] tokens,\n        // 0 - lpBurnAmount\n        // 1 - lpShare\n        // 2 - lpTotalSupply\n        uint256[3] lpAmounts,\n        address poolAddress\n    );\n\n    /**\n     * @notice Deploy liquidity to Curve pool\n     *  @dev Calls to external contract\n     *  @dev We trust sender to send a true Curve poolAddress.\n     *       If it's not the case it will fail in the remove_liquidity_one_coin part\n     *  @param amounts Amounts of coin to deploy\n     *  @param minLpMintAmount Amount of LP tokens to mint on deposit\n     *  @param poolAddress Curve pool address\n     *  @param lpTokenAddress LP token of the pool to track\n     *  @param weth WETH address on the operating chain\n     *  @param useEth A flag to whether use ETH or WETH for deployment\n     */\n    function addLiquidity(\n        uint256[] calldata amounts,\n        uint256 minLpMintAmount,\n        address poolAddress,\n        address lpTokenAddress,\n        IWETH9 weth,\n        bool useEth\n    ) public {\n        //slither-disable-start reentrancy-events\n        _validateAmounts(amounts);\n        Errors.verifyNotZero(minLpMintAmount, \"minLpMintAmount\");\n        Errors.verifyNotZero(poolAddress, \"poolAddress\");\n        Errors.verifyNotZero(lpTokenAddress, \"lpTokenAddress\");\n        Errors.verifyNotZero(address(weth), \"weth\");\n\n        uint256 nTokens = amounts.length;\n        address[] memory tokens = new address[](nTokens);\n        uint256[] memory coinsBalancesBefore = new uint256[](nTokens);\n        for (uint256 i = 0; i < nTokens; ++i) {\n            uint256 amount = amounts[i];\n            address coin = ICryptoSwapPool(poolAddress).coins(i);\n            tokens[i] = coin;\n            if (amount > 0 && coin != LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER) {\n                LibAdapter._approve(IERC20(coin), poolAddress, amount);\n            }\n            coinsBalancesBefore[i] = coin == LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER\n                ? address(this).balance\n                : IERC20(coin).balanceOf(address(this));\n        }\n\n        uint256 deployed = _runDeposit(amounts, minLpMintAmount, poolAddress, useEth);\n\n        IERC20 lpToken = IERC20(lpTokenAddress);\n\n        _updateWethAddress(tokens, address(weth));\n\n        emit DeployLiquidity(\n            _compareCoinsBalances(coinsBalancesBefore, _getCoinsBalances(tokens, weth, useEth), amounts, true),\n            tokens,\n            [deployed, lpToken.balanceOf(address(this)), lpToken.totalSupply()],\n            poolAddress\n        );\n        //slither-disable-end reentrancy-events\n    }\n\n    /**\n     * @notice Withdraw liquidity from Curve pool\n     *  @dev Calls to external contract\n     *  @dev We trust sender to send a true Curve poolAddress.\n     *       If it's not the case it will fail in the remove_liquidity_one_coin part\n     *  @param amounts Minimum amounts of coin to receive\n     *  @param maxLpBurnAmount Amount of LP tokens to burn in the withdrawal\n     *  @param poolAddress Curve pool address\n     *  @param lpTokenAddress LP token of the pool to burn\n     *  @param weth WETH address on the operating chain\n     *  @return tokens Addresses of the withdrawn tokens\n     *  @return actualAmounts Amounts of the withdrawn tokens\n     */\n    function removeLiquidity(\n        uint256[] memory amounts,\n        uint256 maxLpBurnAmount,\n        address poolAddress,\n        address lpTokenAddress,\n        IWETH9 weth\n    ) public returns (address[] memory tokens, uint256[] memory actualAmounts) {\n        //slither-disable-start reentrancy-events\n        if (amounts.length > 4) {\n            revert Errors.InvalidParam(\"amounts\");\n        }\n        Errors.verifyNotZero(maxLpBurnAmount, \"maxLpBurnAmount\");\n        Errors.verifyNotZero(poolAddress, \"poolAddress\");\n        Errors.verifyNotZero(lpTokenAddress, \"lpTokenAddress\");\n        Errors.verifyNotZero(address(weth), \"weth\");\n\n        uint256[] memory coinsBalancesBefore = new uint256[](amounts.length);\n        tokens = new address[](amounts.length);\n        uint256 ethIndex = 999;\n        for (uint256 i = 0; i < amounts.length; ++i) {\n            address coin = IPool(poolAddress).coins(i);\n            tokens[i] = coin;\n\n            if (coin == LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER) {\n                coinsBalancesBefore[i] = address(this).balance;\n                ethIndex = i;\n            } else {\n                tokens[i] = coin;\n                coinsBalancesBefore[i] = IERC20(coin).balanceOf(address(this));\n            }\n        }\n        uint256 lpTokenBalanceBefore = IERC20(lpTokenAddress).balanceOf(address(this));\n\n        _runWithdrawal(poolAddress, amounts, maxLpBurnAmount);\n\n        uint256 lpTokenBalanceAfter = IERC20(lpTokenAddress).balanceOf(address(this));\n        uint256 lpTokenAmount = lpTokenBalanceBefore - lpTokenBalanceAfter;\n        if (lpTokenAmount > maxLpBurnAmount) {\n            revert LibAdapter.LpTokenAmountMismatch();\n        }\n        actualAmounts = _compareCoinsBalances(\n            coinsBalancesBefore, _getCoinsBalances(tokens, weth, ethIndex != 999 ? true : false), amounts, false\n        );\n\n        if (ethIndex != 999) {\n            // Wrapping up received ETH as system operates with WETH\n            // slither-disable-next-line arbitrary-send-eth\n            weth.deposit{ value: actualAmounts[ethIndex] }();\n        }\n\n        _updateWethAddress(tokens, address(weth));\n\n        emit WithdrawLiquidity(\n            actualAmounts,\n            tokens,\n            [lpTokenAmount, lpTokenBalanceAfter, IERC20(lpTokenAddress).totalSupply()],\n            poolAddress\n        );\n        //slither-disable-end reentrancy-events\n    }\n\n    /**\n     * @dev This is a helper function to replace Curve's Registry pointer\n     * to ETH with WETH address to be compatible with the rest of the system\n     */\n    function _updateWethAddress(address[] memory tokens, address weth) private pure {\n        for (uint256 i = 0; i < tokens.length; ++i) {\n            if (tokens[i] == LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER) {\n                tokens[i] = weth;\n            }\n        }\n    }\n\n    /// @dev Validate to have at least one `amount` > 0 provided and `amounts` is <=4\n    function _validateAmounts(uint256[] memory amounts) internal pure {\n        uint256 nTokens = amounts.length;\n        if (nTokens > 4) {\n            revert Errors.InvalidParam(\"amounts\");\n        }\n        bool nonZeroAmountPresent = false;\n        for (uint256 i = 0; i < nTokens; ++i) {\n            if (amounts[i] != 0) {\n                nonZeroAmountPresent = true;\n                break;\n            }\n        }\n        if (!nonZeroAmountPresent) revert LibAdapter.NoNonZeroAmountProvided();\n    }\n\n    /// @dev Gets balances of pool's ERC-20 tokens or ETH\n    function _getCoinsBalances(\n        address[] memory tokens,\n        IWETH9 weth,\n        bool useEth\n    ) private view returns (uint256[] memory coinsBalances) {\n        uint256 nTokens = tokens.length;\n        coinsBalances = new uint256[](nTokens);\n\n        for (uint256 i = 0; i < nTokens; ++i) {\n            address coin = tokens[i];\n            if (coin == LibAdapter.CURVE_REGISTRY_ETH_ADDRESS_POINTER) {\n                coinsBalances[i] = useEth ? address(this).balance : weth.balanceOf(address(this));\n            } else {\n                coinsBalances[i] = IERC20(coin).balanceOf(address(this));\n            }\n        }\n    }\n\n    /// @dev Validate to have a valid balance change\n    function _compareCoinsBalances(\n        uint256[] memory balancesBefore,\n        uint256[] memory balancesAfter,\n        uint256[] memory amounts,\n        bool isLiqDeployment\n    ) private pure returns (uint256[] memory balanceChange) {\n        uint256 nTokens = amounts.length;\n        balanceChange = new uint256[](nTokens);\n\n        for (uint256 i = 0; i < nTokens; ++i) {\n            uint256 balanceDiff =\n                isLiqDeployment ? balancesBefore[i] - balancesAfter[i] : balancesAfter[i] - balancesBefore[i];\n\n            if (balanceDiff < amounts[i]) {\n                revert LibAdapter.InvalidBalanceChange();\n            }\n            balanceChange[i] = balanceDiff;\n        }\n    }\n\n    function _runDeposit(\n        uint256[] memory amounts,\n        uint256 minLpMintAmount,\n        address poolAddress,\n        bool useEth\n    ) private returns (uint256 deployed) {\n        uint256 nTokens = amounts.length;\n        ICryptoSwapPool pool = ICryptoSwapPool(poolAddress);\n        if (useEth) {\n            // slither-disable-start arbitrary-send-eth\n            if (nTokens == 2) {\n                uint256[2] memory staticParamArray = [amounts[0], amounts[1]];\n                deployed = pool.add_liquidity{ value: amounts[0] }(staticParamArray, minLpMintAmount);\n            } else if (nTokens == 3) {\n                uint256[3] memory staticParamArray = [amounts[0], amounts[1], amounts[2]];\n                deployed = pool.add_liquidity{ value: amounts[0] }(staticParamArray, minLpMintAmount);\n            } else if (nTokens == 4) {\n                uint256[4] memory staticParamArray = [amounts[0], amounts[1], amounts[2], amounts[3]];\n                deployed = pool.add_liquidity{ value: amounts[0] }(staticParamArray, minLpMintAmount);\n            }\n            // slither-disable-end arbitrary-send-eth\n        } else {\n            if (nTokens == 2) {\n                uint256[2] memory staticParamArray = [amounts[0], amounts[1]];\n                deployed = pool.add_liquidity(staticParamArray, minLpMintAmount);\n            } else if (nTokens == 3) {\n                uint256[3] memory staticParamArray = [amounts[0], amounts[1], amounts[2]];\n                deployed = pool.add_liquidity(staticParamArray, minLpMintAmount);\n            } else if (nTokens == 4) {\n                uint256[4] memory staticParamArray = [amounts[0], amounts[1], amounts[2], amounts[3]];\n                deployed = pool.add_liquidity(staticParamArray, minLpMintAmount);\n            }\n        }\n        if (deployed < minLpMintAmount) {\n            revert LibAdapter.MinLpAmountNotReached();\n        }\n    }\n\n    function _runWithdrawal(address poolAddress, uint256[] memory amounts, uint256 maxLpBurnAmount) private {\n        uint256 nTokens = amounts.length;\n        ICryptoSwapPool pool = ICryptoSwapPool(poolAddress);\n        if (nTokens == 2) {\n            uint256[2] memory staticParamArray = [amounts[0], amounts[1]];\n            pool.remove_liquidity(maxLpBurnAmount, staticParamArray);\n        } else if (nTokens == 3) {\n            uint256[3] memory staticParamArray = [amounts[0], amounts[1], amounts[2]];\n            pool.remove_liquidity(maxLpBurnAmount, staticParamArray);\n        } else if (nTokens == 4) {\n            uint256[4] memory staticParamArray = [amounts[0], amounts[1], amounts[2], amounts[3]];\n            pool.remove_liquidity(maxLpBurnAmount, staticParamArray);\n        }\n    }\n\n    //slither-disable-end similar-names\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
        "content": "// 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"
      },
      "src/stats/calculators/base/IncentiveCalculatorBase.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { Initializable } from \"openzeppelin-contracts/proxy/utils/Initializable.sol\";\n\nimport { IBaseRewardPool } from \"src/interfaces/external/convex/IBaseRewardPool.sol\";\nimport { IDexLSTStats } from \"src/interfaces/stats/IDexLSTStats.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IStatsCalculator } from \"src/interfaces/stats/IStatsCalculator.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { IIncentivesPricingStats } from \"src/interfaces/stats/IIncentivesPricingStats.sol\";\nimport { Stats } from \"src/stats/Stats.sol\";\nimport { SystemComponent } from \"src/SystemComponent.sol\";\nimport { SecurityBase } from \"src/security/SecurityBase.sol\";\n\nabstract contract IncentiveCalculatorBase is\n    SystemComponent,\n    SecurityBase,\n    Initializable,\n    IDexLSTStats,\n    IStatsCalculator\n{\n    IDexLSTStats public underlyerStats;\n    IBaseRewardPool public rewarder;\n    address public platformToken; // like cvx\n\n    /// @dev rewarder token address => uint256 safeTotalSupply\n    mapping(address => uint256) public safeTotalSupplies;\n\n    /// @dev rewarder token address => uint256 last snapshot timestamp\n    mapping(address => uint256) public lastSnapshotTimestamps;\n\n    /// @dev rewarder token address => uint256 last snapshot reward per token\n    mapping(address => uint256) public lastSnapshotRewardPerToken;\n\n    /// @dev rewarder token address => uint256 last snapshot reward rate\n    mapping(address => uint256) public lastSnapshotRewardRate;\n\n    /// @dev Last time total APR was recorded.\n    uint256 public lastSnapshotTotalAPR;\n\n    /// @dev Last time an incentive was recorded or distributed.\n    uint256 public lastIncentiveTimestamp;\n\n    /// @dev Last time an non trivial incentive was recorded or distributed.\n    uint256 public decayInitTimestamp;\n\n    /// @dev State variable to indicate non trivial incentive APR was measured last snapshot.\n    bool public decayState;\n\n    /// @dev Incentive credits balance before decay\n    uint8 public incentiveCredits;\n\n    /// @dev Interval between two consecutive snapshot steps during the snapshot process.\n    uint256 public constant SNAPSHOT_INTERVAL = 4 hours;\n\n    /// @dev Non-trivial annual rate set at 0.5% (in fixed point format 1e18 = 1).\n    uint256 public constant NON_TRIVIAL_ANNUAL_RATE = 5e15;\n\n    /// @dev Duration after which a price/data becomes stale.\n    uint40 public constant PRICE_STALE_CHECK = 12 hours;\n\n    /// @dev Cap on allowable credits in the system.\n    uint8 public constant MAX_CREDITS = 48;\n\n    /// @dev The APR Id\n    bytes32 private _aprId;\n\n    /// @dev Enum representing the snapshot status for a given rewarder.\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    struct InitData {\n        address rewarder;\n        address underlyerStats;\n        address platformToken;\n    }\n\n    // Custom error for handling unexpected snapshot statuses\n    error InvalidSnapshotStatus();\n\n    event IncentiveSnapshot(\n        uint256 totalApr,\n        uint256 incentiveCredits,\n        uint256 lastIncentiveTimestamp,\n        bool decayState,\n        uint256 decayInitTimestamp\n    );\n\n    constructor(ISystemRegistry _systemRegistry)\n        SystemComponent(_systemRegistry)\n        SecurityBase(address(_systemRegistry.accessController()))\n    { }\n\n    /// @inheritdoc IStatsCalculator\n    function initialize(bytes32[] calldata, bytes calldata initData) public virtual override initializer {\n        InitData memory decodedInitData = abi.decode(initData, (InitData));\n\n        Errors.verifyNotZero(decodedInitData.rewarder, \"rewarder\");\n        Errors.verifyNotZero(decodedInitData.underlyerStats, \"underlyerStats\");\n        Errors.verifyNotZero(decodedInitData.platformToken, \"platformToken\");\n\n        // slither-disable-start missing-zero-check\n        rewarder = IBaseRewardPool(decodedInitData.rewarder);\n        underlyerStats = IDexLSTStats(decodedInitData.underlyerStats);\n        platformToken = decodedInitData.platformToken;\n        // slither-disable-end missing-zero-check\n\n        lastIncentiveTimestamp = block.timestamp;\n        decayInitTimestamp = block.timestamp;\n\n        decayState = false;\n\n        _aprId = keccak256(abi.encode(\"incentive\", platformToken, decodedInitData.rewarder));\n    }\n\n    /// @inheritdoc IStatsCalculator\n    function getAddressId() external view returns (address) {\n        return platformToken;\n    }\n\n    /// @inheritdoc IStatsCalculator\n    function getAprId() external view returns (bytes32) {\n        return _aprId;\n    }\n\n    /// @inheritdoc IDexLSTStats\n    function current() external returns (DexLSTStatsData memory dexLSTStatsData) {\n        // Fetch base stats\n        DexLSTStatsData memory data = underlyerStats.current();\n\n        uint256 extraRewardsLength = rewarder.extraRewardsLength();\n        // we add 2 to the length to account for the main reward and platform reward\n        uint256 totalRewardsLength = extraRewardsLength + 2;\n        uint8 currentCredits = incentiveCredits;\n\n        address[] memory rewardTokens = new address[](totalRewardsLength);\n        uint256[] memory safeTotalSupply = new uint256[](totalRewardsLength);\n        uint256[] memory annualizedRewardAmounts = new uint256[](totalRewardsLength);\n        uint40[] memory periodFinishForRewards = new uint40[](totalRewardsLength);\n\n        // Determine if incentive credits earned should continue to be decayed\n        if (decayState) {\n            uint256 totalAPR = _computeTotalAPR();\n\n            // Apply additional decay if APR is within tolerance\n            // slither-disable-next-line incorrect-equality\n            if ((totalAPR == 0) || totalAPR < (lastSnapshotTotalAPR + (lastSnapshotTotalAPR / 20))) {\n                // slither-disable-start timestamp\n                uint256 hoursPassed = (block.timestamp - decayInitTimestamp) / 3600;\n                if (hoursPassed > 0) {\n                    currentCredits = _decayCredits(incentiveCredits, hoursPassed);\n                }\n                // slither-disable-end timestamp\n            }\n        }\n\n        // Compute main reward statistics\n        (uint256 safeSupply, address rewardToken, uint256 annualizedReward, uint256 periodFinish) =\n            _getStakingIncentiveStats(rewarder, false);\n\n        // Store main reward stats\n        safeTotalSupply[0] = safeSupply;\n        rewardTokens[0] = rewardToken;\n        annualizedRewardAmounts[0] = annualizedReward;\n        periodFinishForRewards[0] = uint40(periodFinish);\n\n        // Compute platform reward statistics\n        safeTotalSupply[1] = safeSupply;\n        rewardTokens[1] = platformToken;\n        annualizedRewardAmounts[1] = getPlatformTokenMintAmount(platformToken, annualizedReward);\n        periodFinishForRewards[1] = uint40(periodFinish);\n\n        // Loop through and compute stats for each extra rewarder\n        for (uint256 i = 0; i < extraRewardsLength; ++i) {\n            IBaseRewardPool extraReward = IBaseRewardPool(rewarder.extraRewards(i));\n            (safeSupply, rewardToken, annualizedReward, periodFinish) = _getStakingIncentiveStats(extraReward, true);\n\n            // Store stats for the current extra reward\n            rewardTokens[i + 2] = rewardToken;\n            annualizedRewardAmounts[i + 2] = annualizedReward;\n            periodFinishForRewards[i + 2] = uint40(periodFinish);\n            safeTotalSupply[i + 2] += safeSupply;\n        }\n\n        // Compile aggregated data into the result struct\n        data.stakingIncentiveStats = StakingIncentiveStats({\n            safeTotalSupply: safeTotalSupply[0], // supply across all rewarders\n            rewardTokens: rewardTokens,\n            annualizedRewardAmounts: annualizedRewardAmounts,\n            periodFinishForRewards: periodFinishForRewards,\n            incentiveCredits: currentCredits\n        });\n\n        return data;\n    }\n\n    /**\n     * @notice Determines if a snapshot is needed for the main rewarder or any of the extra rewarders.\n     * @dev _shouldSnapshot returns true if more than 24 hours passed since the last snapshot.\n     *  Incentive credits needs to be updated at least once every 24 hours which is covered by the above check.\n     * @return true if any of the main or extra rewarders require a snapshot, otherwise false.\n     */\n    function shouldSnapshot() public view returns (bool) {\n        // Check if the main rewarder needs a snapshot\n        (uint256 rewardRate, uint256 totalSupply, uint256 periodFinish) = _getRewardPoolMetrics(address(rewarder));\n        if (_shouldSnapshot(address(rewarder), rewardRate, periodFinish, totalSupply)) return true;\n\n        // Determine the number of extra rewarders\n        uint256 extraRewardsLength = rewarder.extraRewardsLength();\n\n        // Iterate through extra rewarders to check if any of them need a snapshot\n        for (uint256 i = 0; i < extraRewardsLength; ++i) {\n            address extraRewarder = rewarder.extraRewards(i);\n            (rewardRate, totalSupply, periodFinish) = _getRewardPoolMetrics(extraRewarder);\n            if (_shouldSnapshot(extraRewarder, rewardRate, periodFinish, totalSupply)) return true;\n        }\n\n        // No rewarder requires a snapshot\n        return false;\n    }\n\n    function snapshot() external {\n        // Record a new snapshot of total APR across all rewarders\n        // Also, triggers a new snapshot or finalize snapshot for total supply across all the rewarders\n        // slither-disable-next-line reentrancy-no-eth,reentrancy-benign\n        lastSnapshotTotalAPR = _computeTotalAPR();\n        uint8 currentCredits = incentiveCredits;\n        uint256 elapsedTime = block.timestamp - lastIncentiveTimestamp;\n\n        // If APR is above a threshold and credits are below the cap and 1 day has passed since the last update\n        // slither-disable-next-line timestamp\n        if (lastSnapshotTotalAPR >= NON_TRIVIAL_ANNUAL_RATE && currentCredits < MAX_CREDITS && elapsedTime >= 1 days) {\n            // If APR is above a threshold, increment credits based on time elapsed\n            // Only give credit for whole days, so divide-before-multiply is desired\n            // slither-disable-next-line divide-before-multiply\n            uint8 credits = uint8(2 * (elapsedTime / 1 days)); // 2 credits for each day\n            // Increment credits, but cap at MAX_CREDITS\n            incentiveCredits = uint8(Math.min(currentCredits + credits, MAX_CREDITS));\n            // Update the last incentive timestamp to the current block's timestamp\n            lastIncentiveTimestamp = block.timestamp;\n            decayState = false;\n        } else if (lastSnapshotTotalAPR >= NON_TRIVIAL_ANNUAL_RATE) {\n            decayState = false;\n        } else if (lastSnapshotTotalAPR < NON_TRIVIAL_ANNUAL_RATE) {\n            // Set to decay incentive credits state since APR is 0 or near 0\n            if (!decayState) {\n                decayState = true;\n                decayInitTimestamp = block.timestamp;\n            } else {\n                // If APR is below a threshold, decay credits based on time elapsed\n                // slither-disable-start timestamp\n                uint256 hoursPassed = (block.timestamp - decayInitTimestamp) / 3600;\n                // slither-disable-end timestamp\n                if (hoursPassed > 0 && decayState) {\n                    incentiveCredits = _decayCredits(currentCredits, hoursPassed);\n\n                    // Update the incentive decay init timestamp to current timestamp\n                    decayInitTimestamp = block.timestamp;\n                }\n            }\n            // Update the last incentive timestamp to the current block's timestamp\n            lastIncentiveTimestamp = block.timestamp;\n        }\n\n        // slither-disable-next-line reentrancy-events\n        emit IncentiveSnapshot(\n            lastSnapshotTotalAPR, incentiveCredits, lastIncentiveTimestamp, decayState, decayInitTimestamp\n        );\n    }\n\n    /**\n     * @notice Determines the snapshot status for a given rewarder.\n     * @param _rewarder The address of the rewarder for which to check the snapshot status.\n     * @param rewardRate The current reward rate for the rewarder.\n     * @return The snapshot status for the given rewarder, based on the last snapshot and current block time.\n     */\n    function _snapshotStatus(address _rewarder, uint256 rewardRate) internal view returns (SnapshotStatus) {\n        if (lastSnapshotRewardPerToken[_rewarder] == 0) {\n            return SnapshotStatus.noSnapshot;\n        }\n\n        if (rewardRate != lastSnapshotRewardRate[_rewarder] && lastSnapshotRewardRate[_rewarder] != 0) {\n            // lastSnapshotRewardRate[_rewarder] can be zero if the rewarder was just added\n            return SnapshotStatus.shouldRestart;\n        }\n\n        // slither-disable-next-line timestamp\n        if (block.timestamp < lastSnapshotTimestamps[_rewarder] + SNAPSHOT_INTERVAL) {\n            return SnapshotStatus.tooSoon;\n        }\n\n        return SnapshotStatus.shouldFinalize;\n    }\n\n    /**\n     * @notice Determines whether a snapshot should be taken for the specified rewarder.\n     * @param _rewarder The address of the rewarder to check.\n     * @param _rewardRate The current reward rate for the rewarder.\n     * @param _totalSupply The current total supply staked with the rewarder.\n     * @return True if a snapshot should be taken, false otherwise.\n     */\n    function _shouldSnapshot(\n        address _rewarder,\n        uint256 _rewardRate,\n        uint256 _periodFinish,\n        uint256 _totalSupply\n    ) internal view returns (bool) {\n        SnapshotStatus status = _snapshotStatus(_rewarder, _rewardRate);\n\n        // If the status indicates we should finalize a snapshot, return true.\n        if (status == SnapshotStatus.shouldFinalize || status == SnapshotStatus.shouldRestart) return true;\n\n        // If it's too soon to take another snapshot, return false.\n        if (status == SnapshotStatus.tooSoon) return false;\n\n        uint256 timeBetweenSnapshots = block.timestamp - lastSnapshotTimestamps[_rewarder];\n\n        // If more than 24 hours passed since the last snapshot, take another one.\n        // slither-disable-next-line timestamp\n        if (timeBetweenSnapshots > 24 hours) return true;\n\n        // No further snapshots are needed if reward rate is zero.\n        if (_rewardRate == 0) return false;\n\n        // No further snapshots are needed after the period finishes.\n        // slither-disable-next-line timestamp\n        if (block.timestamp > _periodFinish) return false;\n\n        // Snapshot if there's no supply and still time left in the period.\n        if (_totalSupply == 0) return true;\n\n        // if _rewardRate differs by more than 5% from the last snapshot reward rate, take another snapshot.\n        if (_differsByMoreThanFivePercent(lastSnapshotRewardRate[_rewarder], _rewardRate)) {\n            return true;\n        }\n\n        uint256 safeTotalSupply = safeTotalSupplies[_rewarder];\n\n        // If the staked supply deviates by more than 5% from the safe supply and 8 hours have passed since\n        // the last snapshot, take another snapshot.\n        // slither-disable-next-line timestamp\n        if (_differsByMoreThanFivePercent(safeTotalSupply, _totalSupply) && timeBetweenSnapshots > 8 hours) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * @dev Performs a snapshot on the given rewarder's state.\n     * This function assumes it's being called within the context of a _shouldSnapshot() conditional check.\n     *\n     * @param _rewarder Address of the rewarder for which the snapshot will be performed.\n     * @param totalSupply The total supply of tokens for the rewarder.\n     * @param rewardRate The current reward rate for the rewarder.\n     */\n    function _snapshot(address _rewarder, uint256 totalSupply, uint256 rewardRate) internal {\n        if (totalSupply == 0) {\n            safeTotalSupplies[_rewarder] = 0;\n            lastSnapshotRewardPerToken[_rewarder] = 0;\n            lastSnapshotTimestamps[_rewarder] = block.timestamp;\n            return;\n        }\n\n        SnapshotStatus status = _snapshotStatus(_rewarder, rewardRate);\n        uint256 rewardPerToken = IBaseRewardPool(_rewarder).rewardPerToken();\n\n        // Initialization: When no snapshot exists, start a new snapshot.\n        // Restart: If the reward rate changed, restart the snapshot process.\n        if (status == SnapshotStatus.noSnapshot || status == SnapshotStatus.shouldRestart) {\n            // Increase by one to ensure 0 is only used as an uninitialized value flag.\n            lastSnapshotRewardPerToken[_rewarder] = rewardPerToken + 1;\n            lastSnapshotRewardRate[_rewarder] = rewardRate;\n            lastSnapshotTimestamps[_rewarder] = block.timestamp;\n            return;\n        }\n\n        // Finalization: If a snapshot exists, finalize by calculating the reward accrued\n        // since initialization, then reset the snapshot state.\n        if (status == SnapshotStatus.shouldFinalize) {\n            uint256 lastSnapshotTimestamp = lastSnapshotTimestamps[_rewarder];\n            uint256 lastRewardPerToken = lastSnapshotRewardPerToken[_rewarder];\n            // Subtract one, added during initialization, to ensure 0 is only used as an uninitialized value flag.\n            uint256 diff = rewardPerToken - (lastRewardPerToken - 1);\n\n            uint256 timeBetweenSnapshots = block.timestamp - lastSnapshotTimestamp;\n\n            safeTotalSupplies[_rewarder] = diff == 0 ? 0 : rewardRate * timeBetweenSnapshots * 1e18 / diff;\n            lastSnapshotRewardPerToken[_rewarder] = 0;\n            lastSnapshotTimestamps[_rewarder] = block.timestamp;\n\n            return;\n        }\n\n        // It shouldn't be possible to reach this point.\n        revert InvalidSnapshotStatus();\n    }\n\n    /**\n     * @dev Computes staking incentive statistics for a given rewarder.\n     *\n     * @param _rewarder The rewarder contract for which the stats will be computed.\n     * @param isExtraReward The flag to indicate the type of rewarder.\n     * @return safeTotalSupply The total supply for the rewarder.\n     * @return rewardToken The address of the reward token used by the rewarder.\n     * @return annualizedRewardAmount The annual equivalent of the reward rate.\n     * @return periodFinishForReward The timestamp when the reward period ends for the rewarder.\n     */\n    function _getStakingIncentiveStats(\n        IBaseRewardPool _rewarder,\n        bool isExtraReward\n    )\n        internal\n        view\n        returns (\n            uint256 safeTotalSupply,\n            address rewardToken,\n            uint256 annualizedRewardAmount,\n            uint256 periodFinishForReward\n        )\n    {\n        rewardToken = isExtraReward ? resolveRewardToken(address(_rewarder)) : address(_rewarder.rewardToken());\n\n        if (rewardToken != address(0)) {\n            periodFinishForReward = _rewarder.periodFinish();\n\n            uint256 rewardRate = _rewarder.rewardRate();\n\n            annualizedRewardAmount = rewardRate * Stats.SECONDS_IN_YEAR;\n            safeTotalSupply = safeTotalSupplies[address(_rewarder)];\n\n            return (safeTotalSupply, rewardToken, annualizedRewardAmount, uint40(periodFinishForReward));\n        }\n    }\n\n    /**\n     * @dev Decays credits based on the elapsed time and reward rate.\n     * Credits decay when the current time is past the reward period finish time\n     * or when the reward rate is zero.\n     *\n     * @param currentCredits The current amount of credits.\n     * @return The adjusted amount of credits after potential decay.\n     */\n    function _decayCredits(uint8 currentCredits, uint256 hoursPassed) internal pure returns (uint8) {\n        // slither-disable-start timestamp\n        currentCredits = uint8((hoursPassed > currentCredits) ? 0 : currentCredits - hoursPassed);\n        // slither-disable-end timestamp\n\n        return currentCredits;\n    }\n\n    /**\n     * @notice Checks if the difference between two values is more than 5%.\n     * @param value1 The first value.\n     * @param value2 The second value.\n     * @return A boolean indicating if the difference between the two values is more than 5%.\n     */\n    function _differsByMoreThanFivePercent(uint256 value1, uint256 value2) public pure returns (bool) {\n        if (value1 > value2) {\n            return value1 > (value2 + (value2 / 20)); // value2 / 20 represents 5% of value2\n        } else {\n            return value2 > (value1 + (value1 / 20)); // value1 / 20 represents 5% of value1\n        }\n    }\n\n    function _getIncentivePrice(address _token) internal view returns (uint256) {\n        IIncentivesPricingStats pricingStats = systemRegistry.incentivePricing();\n        (uint256 fastPrice, uint256 slowPrice) = pricingStats.getPrice(_token, PRICE_STALE_CHECK);\n        return Math.min(fastPrice, slowPrice);\n    }\n\n    function _getPriceInEth(address _token) internal returns (uint256) {\n        // the price oracle handles reentrancy issues\n        return systemRegistry.rootPriceOracle().getPriceInEth(_token);\n    }\n\n    function _getRewardPoolMetrics(address _rewarder)\n        internal\n        view\n        returns (uint256 rewardRate, uint256 totalSupply, uint256 periodFinish)\n    {\n        rewardRate = IBaseRewardPool(_rewarder).rewardRate();\n        totalSupply = IBaseRewardPool(_rewarder).totalSupply();\n        periodFinish = IBaseRewardPool(_rewarder).periodFinish();\n    }\n\n    function _computeTotalAPR() internal returns (uint256 apr) {\n        // Get reward pool metrics for the main rewarder and take a snapshot if necessary\n        (uint256 rewardRate, uint256 totalSupply, uint256 periodFinish) = _getRewardPoolMetrics(address(rewarder));\n        if (_shouldSnapshot(address(rewarder), rewardRate, periodFinish, totalSupply)) {\n            _snapshot(address(rewarder), totalSupply, rewardRate);\n        }\n\n        // slither-disable-next-line reentrancy-no-eth\n        uint256 lpPrice = _getPriceInEth(resolveLpToken());\n        address rewardToken = address(rewarder.rewardToken());\n\n        // Compute APR factors for the main rewarder if the period is still active\n\n        apr += _computeAPR(address(rewarder), lpPrice, rewardToken, rewardRate, periodFinish);\n\n        // Compute APR factors for the platform rewarder if the period is still active\n        rewardRate = getPlatformTokenMintAmount(platformToken, rewardRate);\n        apr += _computeAPR(address(rewarder), lpPrice, rewardToken, rewardRate, periodFinish);\n\n        // Determine the number of extra rewarders and process each one\n        uint256 extraRewardsLength = rewarder.extraRewardsLength();\n        for (uint256 i = 0; i < extraRewardsLength; ++i) {\n            address extraRewarder = rewarder.extraRewards(i);\n            (rewardRate, totalSupply, periodFinish) = _getRewardPoolMetrics(extraRewarder);\n\n            // Take a snapshot for the extra rewarder if necessary\n            if (_shouldSnapshot(extraRewarder, rewardRate, periodFinish, totalSupply)) {\n                _snapshot(extraRewarder, totalSupply, rewardRate);\n            }\n            rewardToken = resolveRewardToken(extraRewarder);\n\n            if (rewardToken != address(0)) {\n                // Accumulate APR data from each extra rewarder if the period is still active\n                apr += _computeAPR(extraRewarder, lpPrice, rewardToken, rewardRate, periodFinish);\n            }\n        }\n        return apr;\n    }\n\n    function _computeAPR(\n        address _rewarder,\n        uint256 lpPrice,\n        address rewardToken,\n        uint256 rewardRate,\n        uint256 periodFinish\n    ) internal view returns (uint256 apr) {\n        // slither-disable-start incorrect-equality\n        // slither-disable-next-line timestamp\n        if (block.timestamp > periodFinish || rewardRate == 0) return 0;\n        // slither-disable-end incorrect-equality\n\n        uint256 price = _getIncentivePrice(rewardToken);\n\n        uint256 numerator = rewardRate * Stats.SECONDS_IN_YEAR * price * 1e18;\n        uint256 denominator = safeTotalSupplies[_rewarder] * lpPrice;\n\n        return denominator == 0 ? 0 : numerator / denominator;\n    }\n\n    /// @notice returns the platform tokens earned given the amount of main rewarder tokens\n    function getPlatformTokenMintAmount(\n        address _platformToken,\n        uint256 _annualizedReward\n    ) public view virtual returns (uint256);\n\n    /// @notice returns the address of the stash token for Convex & Aura\n    function resolveRewardToken(address extraRewarder) public view virtual returns (address rewardToken);\n\n    // @notice returns the address of the lp token that is staked into the rewards platform\n    function resolveLpToken() public view virtual returns (address lpToken);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// 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"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// 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"
      },
      "src/libs/Roles.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\n\npragma solidity 0.8.17;\n\nlibrary Roles {\n    // --------------------------------------------------------------------\n    // Central roles list used by all contracts that call AccessController\n    // --------------------------------------------------------------------\n\n    bytes32 public constant REBALANCER_ROLE = keccak256(\"REBALANCER_ROLE\");\n    bytes32 public constant CREATE_POOL_ROLE = keccak256(\"CREATE_POOL_ROLE\");\n    bytes32 public constant CREATE_DESTINATION_VAULT_ROLE = keccak256(\"CREATE_DESTINATION_VAULT_ROLE\");\n    bytes32 public constant REGISTRY_UPDATER = keccak256(\"REGISTRY_UPDATER\");\n\n    bytes32 public constant TOKEN_RECOVERY_ROLE = keccak256(\"TOKEN_RECOVERY_ROLE\");\n    bytes32 public constant DESTINATION_VAULTS_UPDATER = keccak256(\"DESTINATION_VAULTS_UPDATER\");\n    bytes32 public constant SET_WITHDRAWAL_QUEUE_ROLE = keccak256(\"SET_WITHDRAWAL_QUEUE_ROLE\");\n\n    bytes32 public constant DESTINATION_VAULT_OPERATOR_ROLE = keccak256(\"DESTINATION_VAULT_OPERATOR_ROLE\");\n\n    bytes32 public constant DV_REWARD_MANAGER_ROLE = keccak256(\"DV_REWARD_MANAGER_ROLE\");\n    bytes32 public constant LMP_REWARD_MANAGER_ROLE = keccak256(\"LMP_REWARD_MANAGER_ROLE\");\n    bytes32 public constant EXTRA_REWARD_MANAGER_ROLE = keccak256(\"EXTRA_REWARD_MANAGER_ROLE\");\n\n    bytes32 public constant LIQUIDATOR_ROLE = keccak256(\"LIQUIDATOR_ROLE\");\n\n    bytes32 public constant CREATE_STATS_CALC_ROLE = keccak256(\"CREATE_STATS_CALC_ROLE\");\n    bytes32 public constant STATS_CALC_TEMPLATE_MGMT_ROLE = keccak256(\"STATS_CALC_TEMPLATE_MGMT_ROLE\");\n    bytes32 public constant STATS_SNAPSHOT_ROLE = keccak256(\"STATS_SNAPSHOT_ROLE\");\n    bytes32 public constant STATS_INCENTIVE_TOKEN_UPDATER = keccak256(\"STATS_INCENTIVE_TOKEN_UPDATER\");\n\n    bytes32 public constant SOLVER_ROLE = keccak256(\"SOLVER_ROLE\");\n\n    bytes32 public constant LMP_FEE_SETTER_ROLE = keccak256(\"LMP_FEE_SETTER_ROLE\");\n    bytes32 public constant LMP_PERIODIC_FEE_SETTER_ROLE = keccak256(\"LMP_PERIODIC_FEE_SETTER_ROLE\");\n\n    bytes32 public constant EMERGENCY_PAUSER = keccak256(\"EMERGENCY_PAUSER\");\n\n    bytes32 public constant ORACLE_MANAGER_ROLE = keccak256(\"ORACLE_MANAGER_ROLE\");\n\n    bytes32 public constant LSD_BACKING_UPDATER = keccak256(\"LSD_BACKING_UPDATER\");\n\n    bytes32 public constant LMP_UPDATE_DEBT_REPORTING_ROLE = keccak256(\"LMP_UPDATE_DEBT_REPORTING_ROLE\");\n\n    bytes32 public constant AUTO_POOL_ADMIN = keccak256(\"AUTO_POOL_ADMIN\");\n}\n"
      },
      "src/security/SecurityBase.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { Context } from \"openzeppelin-contracts/utils/Context.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"
      },
      "lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
        "content": "// 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"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n    mapping(address => uint256) private _balances;\n\n    mapping(address => mapping(address => uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * The default value of {decimals} is 18. To select a different value for\n     * {decimals} you should overload it.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor(string memory name_, string memory symbol_) {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the value {ERC20} uses, unless this function is\n     * overridden;\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view virtual override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view virtual override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address to, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20}.\n     *\n     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual override returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, amount);\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, allowance(owner, spender) + addedValue);\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        uint256 currentAllowance = allowance(owner, spender);\n        require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n        unchecked {\n            _approve(owner, spender, currentAllowance - subtractedValue);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Moves `amount` of tokens from `from` to `to`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     */\n    function _transfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {\n        require(from != address(0), \"ERC20: transfer from the zero address\");\n        require(to != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, amount);\n\n        uint256 fromBalance = _balances[from];\n        require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n        unchecked {\n            _balances[from] = fromBalance - amount;\n            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n            // decrementing then incrementing.\n            _balances[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        _afterTokenTransfer(from, to, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply += amount;\n        unchecked {\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n            _balances[account] += amount;\n        }\n        emit Transfer(address(0), account, amount);\n\n        _afterTokenTransfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        uint256 accountBalance = _balances[account];\n        require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n        unchecked {\n            _balances[account] = accountBalance - amount;\n            // Overflow not possible: amount <= accountBalance <= totalSupply.\n            _totalSupply -= amount;\n        }\n\n        emit Transfer(account, address(0), amount);\n\n        _afterTokenTransfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(\n        address owner,\n        address spender,\n        uint256 amount\n    ) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n     *\n     * Does not update the allowance amount in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Might emit an {Approval} event.\n     */\n    function _spendAllowance(\n        address owner,\n        address spender,\n        uint256 amount\n    ) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance != type(uint256).max) {\n            require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n            unchecked {\n                _approve(owner, spender, currentAllowance - amount);\n            }\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * will be transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * has been transferred to `to`.\n     * - when `from` is zero, `amount` tokens have been minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {}\n}\n"
      },
      "src/interfaces/swapper/ISwapRouter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISyncSwapper } from \"./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();\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"
      },
      "src/interfaces/vault/IDestinationVault.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 \"./IBaseAssetVault.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IDexLSTStats } from \"src/interfaces/stats/IDexLSTStats.sol\";\n\ninterface IDestinationVault is IBaseAssetVault, IERC20 {\n    enum VaultShutdownStatus {\n        Active,\n        Deprecated,\n        Exploit\n    }\n\n    error LogicDefect();\n    error UnreachableError();\n    error BaseAmountReceived(uint256 amount);\n\n    /* ******************************** */\n    /* View                             */\n    /* ******************************** */\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 LMP is done 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 underlyering 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 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\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    /// @notice Calculates the current value of our debt\n    /// @dev Queries the current value of all tokens we have deployed, whether its a single place, multiple, staked, etc\n    /// @return value The current value of our debt in terms of the baseAsset\n    function debtValue() external returns (uint256 value);\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_ROLE. 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_ROLE\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    function withdrawBaseAsset(uint256 shares, address to) external returns (uint256 amount);\n\n    /// @notice Estimate the base asset amount that can be withdrawn given a certain number of shares. This function\n    /// performs a \"simulation\" of the withdrawal process. It will actually execute the withdrawal, but will then revert\n    /// the transaction, returning the estimated amount in the revert reason.\n    /// @param shares The number of shares to be used in the estimation.\n    /// @param to The address to receive the withdrawn amount.\n    /// @param account Address involved in the withdrawal; Must be set to address(0).\n    /// @return The estimated base asset amount.\n    function estimateWithdrawBaseAsset(uint256 shares, address to, address account) external returns (uint256);\n\n    /// @notice Initiate the shutdown procedures for this vault\n    /// @dev Should pull back tokens from staking locations\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 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 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"
      },
      "lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     * @custom:oz-retyped-from bool\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint8 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        require(\n            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = 1;\n        if (isTopLevelCall) {\n            _initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            _initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: setting the version to 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n        _initialized = version;\n        _initializing = true;\n        _;\n        _initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        require(_initializing, \"Initializable: contract is not initializing\");\n        _;\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        require(!_initializing, \"Initializable: contract is initializing\");\n        if (_initialized < type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _initializing;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
        "content": "// 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"
      },
      "src/interfaces/stats/IDexLSTStats.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Stats } from \"src/stats/Stats.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\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"
      },
      "src/interfaces/staking/IAccToke.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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\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"
      },
      "src/interfaces/vault/ILMPVaultRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Keep track of Vaults created through the Vault Factory\ninterface ILMPVaultRegistry {\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"
      },
      "src/interfaces/security/IAccessController.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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    function setupRole(bytes32 role, address account) external;\n\n    function verifyOwner(address account) external view;\n}\n"
      },
      "src/interfaces/vault/ILMPVaultRouter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ILMPVault } from \"src/interfaces/vault/ILMPVault.sol\";\nimport { ILMPVaultRouterBase } from \"src/interfaces/vault/ILMPVaultRouterBase.sol\";\nimport { IAsyncSwapper, SwapParams } from \"src/interfaces/liquidation/IAsyncSwapper.sol\";\n\n/**\n * @title ILMPVaultRouter Interface\n * @notice Extends the ILMPVaultRouterBase with specific flows to save gas\n */\ninterface ILMPVaultRouter is ILMPVaultRouterBase {\n    /**\n     * ***************************   Deposit ********************************\n     */\n\n    /**\n     * @notice swap and deposit max assets to a LMPVault.\n     * @dev The goal is to deposit whatever amount is received from the swap into the vault such as depositMax.\n     * Balances are checked in the swapper function.\n     * @param swapper The address of the swapper contract.\n     * @param swapParams The swap parameters.\n     * @param vault The ILMPVault contract.\n     * @param to The address to receive the deposited amount.\n     * @param minSharesOut The minimum amount of shares to be received as output.\n     * @return sharesOut The amount of shares deposited into the vault.\n     */\n    function swapAndDepositToVault(\n        address swapper,\n        SwapParams memory swapParams,\n        ILMPVault vault,\n        address to,\n        uint256 minSharesOut\n    ) external returns (uint256 sharesOut);\n\n    /**\n     * @notice deposit max assets to a LMPVault.\n     * @param vault The LMPVault 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(ILMPVault vault, address to, uint256 minSharesOut) external returns (uint256 sharesOut);\n\n    /**\n     * *************************   Withdraw   **********************************\n     */\n\n    /**\n     * @notice withdraw `amount` to a LMPVault.\n     * @param fromVault The LMPVault to withdraw assets from.\n     * @param toVault The LMPVault 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        ILMPVault fromVault,\n        ILMPVault toVault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesIn,\n        uint256 minSharesOut\n    ) external returns (uint256 sharesOut);\n\n    /**\n     * *************************   Redeem    ********************************\n     */\n\n    /**\n     * @notice redeem `shares` to a LMPVault.\n     * @param fromVault The LMPVault to redeem shares from.\n     * @param toVault The LMPVault 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        ILMPVault fromVault,\n        ILMPVault toVault,\n        address to,\n        uint256 shares,\n        uint256 minSharesOut\n    ) external returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem max shares to a LMPVault.\n     * @param vault The LMPVault 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(ILMPVault vault, address to, uint256 minAmountOut) external returns (uint256 amountOut);\n}\n"
      },
      "src/interfaces/vault/ILMPVaultFactory.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\ninterface ILMPVaultFactory {\n    ///////////////////////////////////////////////////////////////////\n    //                        Vault Creation\n    ///////////////////////////////////////////////////////////////////\n\n    /**\n     * @notice Spin up a new LMPVault\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 returns (address newVaultAddress);\n\n    function addStrategyTemplate(address strategyTemplate) external;\n\n    function removeStrategyTemplate(address strategyTemplate) external;\n}\n"
      },
      "src/interfaces/security/ISystemSecurity.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 view returns (bool);\n}\n"
      },
      "src/interfaces/destinations/IDestinationRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/interfaces/oracles/IRootPriceOracle.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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    /// @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"
      },
      "src/interfaces/vault/IDestinationVaultRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/interfaces/stats/IStatsCalculatorRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 Register a new stats calculator\n    /// @param calculator address of the calculator\n    function register(address calculator) external;\n}\n"
      },
      "src/interfaces/liquidation/IAsyncSwapperRegistry.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/interfaces/stats/IIncentivesPricingStats.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/interfaces/rewarders/IBaseRewarder.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IStakeTracking } from \"src/interfaces/rewarders/IStakeTracking.sol\";\n\ninterface IBaseRewarder {\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, 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    /**\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 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"
      },
      "src/interfaces/rewarders/IExtraRewarder.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IBaseRewarder } from \"./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     */\n    function getReward(address account) external;\n}\n"
      },
      "src/destinations/adapters/rewards/RewardAdapter.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\n/// @title Common functionality for reward adapter libraries\nlibrary RewardAdapter {\n    error ClaimRewardsFailed();\n\n    event RewardsClaimed(address[] tokens, uint256[] amounts);\n\n    /// @notice Emit RewardsClaimed(address[],uint256[]) event common to all reward claim libraries\n    /// @param tokens reward token addresses claimed\n    /// @param amounts amounts of each token claimed\n    function emitRewardsClaimed(address[] memory tokens, uint256[] memory amounts) internal {\n        emit RewardsClaimed(tokens, amounts);\n    }\n}\n"
      },
      "src/interfaces/external/convex/IConvexStashToken.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\ninterface IConvexStashToken {\n    /// @notice Returns actual reward token\n    function token() external view returns (address);\n\n    /// @notice Returns whether the current stash token is invalid\n    function isInvalid() external view returns (bool);\n\n    /// @notice Returns current balance of the given wallet\n    /// @param wallet address to balance for\n    function balanceOf(address wallet) external view returns (uint256);\n}\n"
      },
      "src/interfaces/destinations/IPoolAdapter.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\nimport { IDestinationAdapter } from \"./IDestinationAdapter.sol\";\n\n/**\n * @title IPoolAdapter\n * @dev This is an interface to mark adapters as ones that are dedicated for liquidity deployment/withdrawal\n *      to/from pools of a different protocols and to be registered in Destination Registry.\n *      It shares unified functions to implement with basic params and `extraParams`\n *      which can contain protocol or pool-specific params.\n */\ninterface IPoolAdapter is IDestinationAdapter {\n    /**\n     * @notice Deploy liquidity to the associated destination\n     * @dev Calls into external contract\n     * @param amounts Amounts of corresponding tokens to deploy\n     * @param minLpMintAmount Min amount of LP tokens to mint in the deployment\n     * @param extraParams Encoded params that are specific to the given destination\n     */\n    function addLiquidity(uint256[] calldata amounts, uint256 minLpMintAmount, bytes calldata extraParams) external;\n\n    /**\n     * @notice Withdraw liquidity from the associated destination\n     * @dev Calls into external contract\n     * @param amounts Amounts of corresponding tokens to withdraw\n     * @param maxLpBurnAmount Max amount of LP tokens to burn in the withdrawal\n     * @param extraParams Encoded params that are specific to the given destination\n     * @return actualAmounts Amounts of tokens that were actually withdrawn\n     */\n    function removeLiquidity(\n        uint256[] calldata amounts,\n        uint256 maxLpBurnAmount,\n        bytes calldata extraParams\n    ) external returns (uint256[] memory actualAmounts);\n}\n"
      },
      "src/interfaces/external/curve/ICryptoSwapPool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\nimport { IPool } from \"./IPool.sol\";\n\n/* solhint-disable func-name-mixedcase, var-name-mixedcase */\ninterface ICryptoSwapPool is IPool {\n    function token() external returns (address);\n\n    // slither-disable-start naming-convention\n    function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount) external payable returns (uint256);\n\n    function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount) external payable returns (uint256);\n\n    function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount) external payable returns (uint256);\n\n    function remove_liquidity(uint256 amount, uint256[2] memory min_amounts) external;\n\n    function remove_liquidity(uint256 amount, uint256[3] memory min_amounts) external;\n\n    function remove_liquidity(uint256 amount, uint256[4] memory min_amounts) external;\n\n    function remove_liquidity_one_coin(uint256 token_amount, uint256 i, uint256 min_amount) external;\n    // slither-disable-end naming-convention\n\n    function get_virtual_price() external returns (uint256);\n\n    function claim_admin_fees() external;\n}\n"
      },
      "src/interfaces/stats/IStatsCalculator.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "src/stats/Stats.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IStatsCalculator } from \"src/interfaces/stats/IStatsCalculator.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\n\nlibrary Stats {\n    uint256 public constant SECONDS_IN_YEAR = 365 * 24 * 60 * 60;\n    uint256 public constant DEX_FEE_APR_SNAPSHOT_INTERVAL = 24 * 60 * 60; // daily\n    uint256 public constant DEX_FEE_APR_FILTER_INIT_INTERVAL = 9 * 24 * 60 * 60; // 9 days\n    uint256 public constant DEX_FEE_ALPHA = 1e17; // 0.1; must be less than 1e18\n\n    uint256 public constant INCENTIVE_INFO_SNAPSHOT_INTERVAL = 24 * 60 * 60; // daily\n\n    address public constant CURVE_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n    /// @notice thrown if end timestamp is before start timestamp\n    error IncorrectTimestamps();\n\n    /// @notice thrown if a divisor is zero\n    error ZeroDivisor();\n\n    /// @notice thrown if expecting a negative change but get a positive change\n    error NonNegativeChange();\n\n    /// @dev When registering dependent calculators, use this value for tokens/pools/etc that should be ignored\n    bytes32 public constant NOOP_APR_ID = keccak256(abi.encode(\"NOOP_APR_ID\"));\n\n    error CalculatorAssetMismatch(bytes32 aprId, address calculator, address coin);\n\n    error DependentAprIdsMismatchTokens(uint256 numDependentAprIds, uint256 numCoins);\n\n    /// @notice Generate an id for a stat calc representing a base ERC20\n    /// @dev For rETH/stETH/cbETH etc. Do not use for pools, LP tokens, staking platforms.\n    /// @param tokenAddress address of the token\n    function generateRawTokenIdentifier(address tokenAddress) internal pure returns (bytes32) {\n        return keccak256(abi.encode(\"erc20\", tokenAddress));\n    }\n\n    /// @notice Generate an aprId for a curve pool\n    /// @param poolAddress address of the curve pool\n    function generateCurvePoolIdentifier(address poolAddress) internal pure returns (bytes32) {\n        return keccak256(abi.encode(\"curve\", poolAddress));\n    }\n\n    /// @notice Generate an aprId for a balancer pool\n    /// @param poolAddress address of the balancer pool\n    function generateBalancerPoolIdentifier(address poolAddress) internal pure returns (bytes32) {\n        return keccak256(abi.encode(\"balancer\", poolAddress));\n    }\n\n    function calculateAnnualizedChangeMinZero(\n        uint256 startTimestamp,\n        uint256 startValue,\n        uint256 endTimestamp,\n        uint256 endValue\n    ) internal pure returns (uint256) {\n        if (startValue == 0) revert ZeroDivisor();\n        if (endTimestamp <= startTimestamp) revert IncorrectTimestamps();\n        if (endValue <= startValue) return 0;\n\n        uint256 unannualized = (endValue * 1e18) / startValue - 1e18;\n        uint256 timeDiff = endTimestamp - startTimestamp;\n\n        return unannualized * SECONDS_IN_YEAR / timeDiff;\n    }\n\n    function calculateUnannualizedNegativeChange(\n        uint256 startValue,\n        uint256 endValue\n    ) internal pure returns (uint256) {\n        if (startValue == 0) revert ZeroDivisor();\n        if (endValue >= startValue) revert NonNegativeChange();\n\n        return (startValue - endValue) * 1e18 / startValue;\n    }\n\n    function getFilteredValue(\n        uint256 alpha,\n        uint256 priorValue,\n        uint256 currentValue\n    ) internal pure returns (uint256) {\n        if (alpha > 1e18 || alpha == 0) revert Errors.InvalidParam(\"alpha\");\n        return ((priorValue * (1e18 - alpha)) + (currentValue * alpha)) / 1e18;\n    }\n}\n"
      },
      "src/SystemComponent.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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    function getSystemRegistry() external view returns (address) {\n        return address(systemRegistry);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": {
        "content": "// 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"
      },
      "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "src/interfaces/swapper/ISyncSwapper.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 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"
      },
      "src/interfaces/vault/IBaseAssetVault.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\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"
      },
      "src/interfaces/stats/ILSTStats.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { Stats } from \"src/stats/Stats.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\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[5] discountTimestampByPercent; // each index is the timestamp that the token reached that discount\n        uint256[] slashingCosts;\n        uint256[] slashingTimestamps;\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"
      },
      "lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol": {
        "content": "// 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"
      },
      "src/interfaces/vault/ILMPVault.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IERC4626 } from \"openzeppelin-contracts/interfaces/IERC4626.sol\";\nimport { IERC20Permit } from \"openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { IDestinationVault } from \"src/interfaces/vault/IDestinationVault.sol\";\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\nimport { LMPDebt } from \"src/vault/libs/LMPDebt.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\n\ninterface ILMPVault 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 totalAssetsHighMarkThe last totalAssets amount we took fees at\n    /// @param totalAssetsHighMarkTimestamp The last timestamp we updated the high water mark\n    /// @param nextPeriodicFeeTake Timestamp of next periodic fee to be taken.\n    /// @param periodicFeeSink Address that receives periodic fee.\n    /// @param periodicFeeBps Current periodic fee.  100% == 10000.\n    /// @param pendingPeriodicFeeBps Pending periodic fee. Used as placeholder for new `periodicFeeBps` within\n    /// range of fee take time.\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 nextPeriodicFeeTake;\n        address periodicFeeSink;\n        uint256 periodicFeeBps;\n        uint256 pendingPeriodicFeeBps;\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 RewarderSet(address rewarder);\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 AmountExceedsAllowance(uint256 shares, uint256 allowed);\n    error InvalidShutdownStatus(VaultShutdownStatus status);\n\n    error WithdrawalFailed();\n    error DepositFailed();\n    error InsufficientFundsInDestinations(uint256 deficit);\n    error WithdrawalIncomplete();\n\n    /// @notice Query the type of vault\n    function vaultType() external view returns (bytes32);\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 Remove emptied destination vault from pending removal queue\n    function removeFromRemovalQueue(address vaultToRemove) external;\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 LMP/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 (LMPDebt.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 LMPVault\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"
      },
      "src/interfaces/vault/ILMPVaultRouterBase.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity >=0.8.7;\n\nimport { ILMPVault } from \"./ILMPVault.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title LMPVault Router Base Interface\n * @notice A canonical router between LMPVaults\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 ILMPVaultRouterBase {\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    /**\n     * @notice mint `shares` from an ERC4626 vault.\n     * @param vault The LMPVault 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        ILMPVault 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 LMPVault 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        ILMPVault 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 LMPVault 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     * @param unwrapWETH If true, unwrap WETH9 to ETH before sending to `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MaxSharesError\n     */\n    function withdraw(\n        ILMPVault vault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesOut,\n        bool unwrapWETH\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem `shares` shares from a LMPVault\n     * @param vault The LMPVault 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     * @param unwrapWETH If true, unwrap WETH9 to ETH before sending to `to`.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeem(\n        ILMPVault vault,\n        address to,\n        uint256 shares,\n        uint256 minAmountOut,\n        bool unwrapWETH\n    ) external payable returns (uint256 amountOut);\n\n    /// @notice Stakes vault token to corresponding rewarder.\n    /// @param vault IERC20 instance of an LMP vault 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 returns (uint256 staked);\n\n    /// @notice Unstakes vault token from corresponding rewarder.\n    /// @param vault ILMPVault 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        ILMPVault vault,\n        IMainRewarder rewarder,\n        uint256 maxAmount,\n        bool claim\n    ) external returns (uint256 withdrawn);\n\n    /// @notice Claims rewards on user stake of vault token.\n    /// @param vault ILMPVault instance of vault token to claim rewards for.\n    /// @param rewarder Rewarder to claim rewards from.\n    function claimRewards(ILMPVault vault, IMainRewarder rewarder) external;\n}\n"
      },
      "src/interfaces/liquidation/IAsyncSwapper.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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}\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"
      },
      "src/interfaces/destinations/IDestinationAdapter.sol": {
        "content": "// 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"
      },
      "src/interfaces/vault/IDestinationVaultFactory.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.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"
      },
      "src/interfaces/rewarders/IStakeTracking.sol": {
        "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.17;\n\ninterface IStakeTracking {\n    function totalSupply() external view returns (uint256);\n    function balanceOf(address account) external view returns (uint256);\n}\n"
      },
      "src/interfaces/external/curve/IPool.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\n//slither-disable-next-line name-reused\ninterface IPool {\n    function coins(uint256 i) external view returns (address);\n\n    function balances(uint256 i) external view returns (uint256);\n\n    // These method used for cases when Pool is a LP token at the same time\n    function balanceOf(address account) external returns (uint256);\n\n    // These method used for cases when Pool is a LP token at the same time\n    function totalSupply() external returns (uint256);\n}\n"
      },
      "src/interfaces/ISystemComponent.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      },
      "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
        "content": "// 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"
      },
      "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/IERC20.sol\";\nimport \"../token/ERC20/extensions/IERC20Metadata.sol\";\n\n/**\n * @dev Interface of the ERC4626 \"Tokenized Vault Standard\", as defined in\n * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].\n *\n * _Available since v4.7._\n */\ninterface IERC4626 is IERC20, IERC20Metadata {\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed sender,\n        address indexed receiver,\n        address indexed owner,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /**\n     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n     *\n     * - MUST be an ERC-20 token contract.\n     * - MUST NOT revert.\n     */\n    function asset() external view returns (address assetTokenAddress);\n\n    /**\n     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n     *\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     */\n    function totalAssets() external view returns (uint256 totalManagedAssets);\n\n    /**\n     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n     * scenario where all the conditions are met.\n     *\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     */\n    function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n     * scenario where all the conditions are met.\n     *\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     */\n    function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n     * through a deposit call.\n     *\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     */\n    function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n    /**\n     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n     * current on-chain conditions.\n     *\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     */\n    function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n     *\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     */\n    function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n    /**\n     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\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     */\n    function maxMint(address receiver) external view returns (uint256 maxShares);\n\n    /**\n     * @dev 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     *\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     */\n    function previewMint(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n     *\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     */\n    function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n    /**\n     * @dev 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     *\n     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n     * - MUST NOT revert.\n     */\n    function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n    /**\n     * @dev 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     *\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     */\n    function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n     *\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     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    ) external returns (uint256 shares);\n\n    /**\n     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n     * through a redeem call.\n     *\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     */\n    function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n    /**\n     * @dev 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     *\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     */\n    function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n     *\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     */\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    ) external returns (uint256 assets);\n}\n"
      },
      "src/interfaces/strategy/IStrategy.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IDestinationVault } from \"../vault/IDestinationVault.sol\";\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 LMP/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 LMP/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 LMP/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    /// @param slashingCost The loss due to slashing of the backing\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        uint256 slashingCost;\n    }\n\n    /// @notice rebalance the LMP 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"
      },
      "src/vault/libs/LMPDebt.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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 { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IDestinationVaultRegistry } from \"src/interfaces/vault/IDestinationVaultRegistry.sol\";\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\nimport { ILMPStrategy } from \"src/interfaces/strategy/ILMPStrategy.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { ILMPVault } from \"src/interfaces/vault/ILMPVault.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\n\nlibrary LMPDebt {\n    using Math for uint256;\n    using SafeERC20 for IERC20;\n    using WithdrawalQueue for StructuredLinkedList.List;\n    using EnumerableSet for EnumerableSet.AddressSet;\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\n    event DestinationDebtReporting(\n        address destination, LMPDebt.IdleDebtUpdates debtInfo, uint256 claimed, uint256 claimGasUsed\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    }\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        ILMPStrategy lmpStrategy,\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            LMPDebt.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) = lmpStrategy.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    ) external returns (IdleDebtUpdates memory result) {\n        result = _handleRebalanceIn(destInfo, dvIn, tokenIn, depositAmount);\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                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        // TODO: Trace the use of this fn and ensure that every is handling is pricesWereSafe\n\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        // We won't update anything\n        if (!isSpotSafe) {\n            return result;\n        }\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        // TODO: Evaluate whether to round these up so we don't accumulate small amounts\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 = true;\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 => LMPDebt.DestinationInfo) storage destinationInfo,\n        ILMPVault.TotalAssetPurpose purpose\n    ) external returns (uint256) {\n        IDestinationVault destVault = IDestinationVault(debtReportQueue.peekHead());\n        uint256 recalculatedTotalAssets = ILMPVault(address(this)).totalAssets(purpose);\n        uint256 divisor = 10 ** ILMPVault(address(this)).decimals();\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 == ILMPVault.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 == ILMPVault.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) / divisor;\n\n                if (purpose == ILMPVault.TotalAssetPurpose.Deposit && staleDebt > newValue) {\n                    newValue = staleDebt;\n                } else if (purpose == ILMPVault.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        EnumerableSet.AddressSet storage _destinations,\n        mapping(address => LMPDebt.DestinationInfo) storage destinationInfo,\n        uint256 numToProcess\n    ) external returns (IdleDebtUpdates memory result) {\n        // For destinations we can't process atm due to price discrepancies\n        address[] memory reprocesses = new address[](numToProcess);\n        uint256 reprocessCount = 0;\n\n        numToProcess = Math.min(numToProcess, debtReportQueue.sizeOf());\n\n        for (uint256 i = 0; i < numToProcess; ++i) {\n            IDestinationVault destVault = IDestinationVault(debtReportQueue.popHead());\n\n            if (!_destinations.contains(address(destVault))) {\n                revert InvalidDestination(address(destVault));\n            }\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(ILMPVault(address(this)).asset()).balanceOf(address(this));\n            IMainRewarder(destVault.rewarder()).getReward(address(this), false);\n            uint256 claimedRewardValue =\n                IERC20(ILMPVault(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            LMPDebt.IdleDebtUpdates memory debtResult = _recalculateDestInfo(\n                destinationInfo[address(destVault)], destVault, currentShareBalance, currentShareBalance\n            );\n\n            // If they aren't safe then just continue and we'll get it on the next go around\n            // No updates were made in recalculateDestInfo if prices weren't safe\n            if (!debtResult.pricesWereSafe) {\n                reprocesses[reprocessCount] = address(destVault);\n                ++reprocessCount;\n                continue;\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        // Add the destinations that had bad prices back to the queue in their original order\n        if (reprocessCount > 0) {\n            for (uint256 i = reprocessCount - 1; i >= 0; --i) {\n                debtReportQueue.addToHead(address(reprocesses[i]));\n            }\n        }\n    }\n\n    function _initiateWithdrawInfo(\n        uint256 assets,\n        ILMPVault.AssetBreakdown storage assetBreakdown\n    ) private view returns (WithdrawInfo memory info) {\n        uint256 idle = assetBreakdown.totalIdle;\n        info = WithdrawInfo({\n            currentIdle: idle,\n            assetsFromIdle: assets >= idle ? idle : 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        });\n\n        info.totalAssetsToPull = assets - info.assetsFromIdle;\n        info.assetsToPull = assets - info.assetsFromIdle;\n\n        if (info.totalAssetsToPull > info.totalMinDebt) {\n            revert TooFewAssets(assets, info.currentIdle + info.totalMinDebt);\n        }\n    }\n\n    function withdraw(\n        uint256 assets,\n        uint256 applicableTotalAssets,\n        ILMPVault.AssetBreakdown storage assetBreakdown,\n        StructuredLinkedList.List storage withdrawalQueue,\n        mapping(address => LMPDebt.DestinationInfo) storage destinationInfo\n    ) external 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 the destinations in\n        // the while loop below\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            if (address(destVault) == address(0)) {\n                // TODO: This may be some NULL value too, check the underlying library\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                    // 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            // Destination Vaults always burn the exact amount we instruct them to\n\n            uint256 pulledAssets = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));\n\n            info.assetsPulled += pulledAssets;\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            // The assets we calculated to pull are from the minDebt number we track so\n            // we'll use that one to ensure we properly account for slippage (the `pulled` var below)\n            // The other two debt numbers we just need to keep up to date.\n            uint256 debtMinDecrease = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMinDecrease += debtMinDecrease;\n\n            info.debtDecrease += destinationInfo[address(destVault)].cachedDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMaxDecrease += destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\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                break;\n            }\n\n            info.assetsToPull -= pulledAssets;\n\n            // // Any deficiency in the amount we received is slippage. debtDecrease is what we expected\n            // // to receive. If we received any extra, that's great we'll roll it forward so we burn\n            // // less on the next loop.\n            // uint256 pulled = Math.max(debtMinDecrease, pulledAssets);\n            // if (pulled >= info.assetsToPull) {\n            //     // We either have enough assets, or we've burned the max debt we're allowed\n            //     info.assetsToPull = 0;\n            //     break;\n            // } else {\n            //     info.assetsToPull -= pulled;\n            // }\n        }\n\n        // info.assetsToPull isn't safe to use past this point.\n        // It may or may not be accurate from the previous loop\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 = ILMPVault(address(this)).convertToShares(\n            Math.max(actualAssets, debtBurned),\n            applicableTotalAssets,\n            ILMPVault(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    /// @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        ILMPVault.AssetBreakdown storage assetBreakdown,\n        StructuredLinkedList.List storage withdrawalQueue,\n        mapping(address => LMPDebt.DestinationInfo) storage destinationInfo\n    ) external 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        while (info.assetsToPull > 0) {\n            IDestinationVault destVault = IDestinationVault(withdrawalQueue.peekHead());\n            if (address(destVault) == address(0)) {\n                // TODO: This may be some NULL value too, check the underlying library\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            // Destination Vaults always burn the exact amount we instruct them to\n\n            uint256 pulledAssets = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));\n\n            info.assetsPulled += pulledAssets;\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            // The assets we calculated to pull are from the minDebt number we track so\n            // we'll use that one to ensure we properly account for slippage (the `pulled` var below)\n            // The other two debt numbers we just need to keep up to date.\n            uint256 debtMinDecrease = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMinDecrease += debtMinDecrease;\n\n            info.debtDecrease += destinationInfo[address(destVault)].cachedDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMaxDecrease += destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\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            }\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                break;\n            }\n\n            // Any deficiency in the amount we received is slippage. debtDecrease is what we expected\n            // to receive. If we received any extra, that's great we'll roll it forward so we burn\n            // less on the next loop.\n            uint256 pulled = Math.max(debtMinDecrease, pulledAssets);\n            if (pulled >= info.assetsToPull) {\n                // We either have enough assets, or we've burned the max debt we're allowed\n                info.assetsToPull = 0;\n                break;\n            } else {\n                info.assetsToPull -= pulled;\n            }\n        }\n\n        // info.totalAssetsToPull isn't safe to use past this point.\n        // It may or may not be accurate from the previous loop\n\n        debtBurned = info.assetsFromIdle + info.debtMinDecrease;\n\n        actualShares = ILMPVault(address(this)).convertToShares(\n            debtBurned, applicableTotalAssets, ILMPVault(address(this)).totalSupply(), Math.Rounding.Up\n        );\n        actualAssets = info.assetsFromIdle + info.assetsPulled;\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"
      },
      "lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol": {
        "content": "// 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"
      },
      "src/interfaces/strategy/ILMPStrategy.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. All rights reserved.\npragma solidity 0.8.17;\n\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\n\ninterface ILMPStrategy {\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 LMPVault when NAV is updated\n    /// @dev can only be called by the strategy's registered LMPVault\n    /// @param navPerShare The navPerShare to record\n    function navUpdate(uint256 navPerShare) external;\n\n    /// @notice called by the LMPVault when a rebalance is completed\n    /// @dev can only be called by the strategy's registered LMPVault\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 LMPVault 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"
      },
      "src/strategy/StructuredLinkedList.sol": {
        "content": "// 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"
      },
      "src/strategy/WithdrawalQueue.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Ops Ltd. 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"
      }
    },
    "settings": {
      "remappings": [
        "forge-std/=lib/forge-std/src/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "src/=src/",
        "test/=test/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
        "erc4626-tests/=lib/erc4626-tests/",
        "prb-math/=lib/prb-math/",
        "crytic/properties/=lib/properties/",
        "ERC4626/=lib/properties/lib/ERC4626/contracts/",
        "properties/=lib/properties/contracts/",
        "solmate/=lib/properties/lib/solmate/src/",
        "usingtellor/=lib/usingtellor/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs"
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "london",
      "libraries": {
        "src/destinations/adapters/CurveV2FactoryCryptoAdapter.sol": {
          "CurveV2FactoryCryptoAdapter": "0xe888089dbcb9a7e82eb0b7c94cd70b5d6b78879c"
        },
        "src/destinations/adapters/rewards/ConvexRewardsAdapter.sol": {
          "ConvexRewards": "0xe6e408ffc1d470e03557265f31cb8e3bc1c31ad9"
        },
        "src/destinations/adapters/staking/ConvexAdapter.sol": {
          "ConvexStaking": "0xaad81f0a7f0dfca386b7b154d2877d6e9058ff4f"
        }
      }
    }
  }}