{"message":"OK","result":[{"AdditionalSources":[{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IAccessControlEnumerable } from \"openzeppelin-contracts/access/IAccessControlEnumerable.sol\";\n\ninterface IAccessController is IAccessControlEnumerable {\n    error AccessDenied();\n\n    /**\n     * @notice Setup a role for an account\n     * @param role The role to setup\n     * @param account The account to setup the role for\n     */\n    function setupRole(bytes32 role, address account) external;\n\n    /**\n     * @notice Verify if an account is an owner. Reverts if not\n     * @param account The account to verify\n     */\n    function verifyOwner(\n        address account\n    ) external view;\n}\n","Filename":"src/interfaces/security/IAccessController.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/utils/math/Math.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nlibrary Roles {\n    // Naming Conventions:\n    // - Use MANAGER, CREATOR, UPDATER, ..., for roles primarily managing on-chain activities.\n    // - Use EXECUTOR for roles that trigger off-chain initiated actions.\n    // - Group roles by functional area for clarity.\n\n    // Destination Vault Management\n    bytes32 public constant DESTINATION_VAULT_FACTORY_MANAGER = keccak256(\"CREATE_DESTINATION_VAULT_ROLE\");\n    bytes32 public constant DESTINATION_VAULT_REGISTRY_MANAGER = keccak256(\"DESTINATION_VAULT_REGISTRY_MANAGER\");\n    bytes32 public constant DESTINATION_VAULT_MANAGER = keccak256(\"DESTINATION_VAULT_MANAGER\");\n    bytes32 public constant FLUID_DESTINATION_VAULT_MANAGER = keccak256(\"FLUID_DESTINATION_VAULT_MANAGER\");\n    bytes32 public constant DESTINATION_MERKLE_CLAIM_MANAGER = keccak256(\"DESTINATION_MERKLE_CLAIM_MANAGER\");\n\n    // Auto Pool Factory and Registry Management\n    bytes32 public constant AUTO_POOL_REGISTRY_UPDATER = keccak256(\"REGISTRY_UPDATER\");\n    bytes32 public constant AUTO_POOL_FACTORY_MANAGER = keccak256(\"AUTO_POOL_FACTORY_MANAGER\");\n    bytes32 public constant AUTO_POOL_FACTORY_VAULT_CREATOR = keccak256(\"CREATE_POOL_ROLE\");\n\n    // Auto Pool Management\n    bytes32 public constant AUTO_POOL_DESTINATION_UPDATER = keccak256(\"DESTINATION_VAULTS_UPDATER\");\n    bytes32 public constant AUTO_POOL_FEE_UPDATER = keccak256(\"AUTO_POOL_FEE_SETTER_ROLE\");\n    bytes32 public constant AUTO_POOL_PERIODIC_FEE_UPDATER = keccak256(\"AUTO_POOL_PERIODIC_FEE_SETTER_ROLE\");\n    bytes32 public constant AUTO_POOL_REWARD_MANAGER = keccak256(\"AUTO_POOL_REWARD_MANAGER_ROLE\");\n    bytes32 public constant AUTO_POOL_MANAGER = keccak256(\"AUTO_POOL_ADMIN\");\n    bytes32 public constant REBALANCER = keccak256(\"REBALANCER_ROLE\");\n    bytes32 public constant STATS_HOOK_POINTS_ADMIN = keccak256(\"STATS_HOOK_POINTS_ADMIN\");\n\n    // Reward Management\n    bytes32 public constant LIQUIDATOR_MANAGER = keccak256(\"LIQUIDATOR_ROLE\");\n    bytes32 public constant DV_REWARD_MANAGER = keccak256(\"DV_REWARD_MANAGER_ROLE\");\n    bytes32 public constant REWARD_LIQUIDATION_MANAGER = keccak256(\"REWARD_LIQUIDATION_MANAGER\");\n    bytes32 public constant EXTRA_REWARD_MANAGER = keccak256(\"EXTRA_REWARD_MANAGER_ROLE\");\n    bytes32 public constant REWARD_LIQUIDATION_EXECUTOR = keccak256(\"REWARD_LIQUIDATION_EXECUTOR\");\n    bytes32 public constant BANK_SWAP_MANAGER = keccak256(\"BANK_SWAP_MANAGER\");\n\n    // Statistics and Reporting\n    bytes32 public constant STATS_CALC_REGISTRY_MANAGER = keccak256(\"STATS_CALC_REGISTRY_MANAGER\");\n    bytes32 public constant STATS_CALC_FACTORY_MANAGER = keccak256(\"CREATE_STATS_CALC_ROLE\");\n    bytes32 public constant STATS_CALC_FACTORY_TEMPLATE_MANAGER = keccak256(\"STATS_CALC_TEMPLATE_MGMT_ROLE\");\n\n    bytes32 public constant STATS_SNAPSHOT_EXECUTOR = keccak256(\"STATS_SNAPSHOT_ROLE\");\n    bytes32 public constant STATS_INCENTIVE_TOKEN_UPDATER = keccak256(\"STATS_INCENTIVE_TOKEN_UPDATER\");\n    bytes32 public constant STATS_GENERAL_MANAGER = keccak256(\"STATS_GENERAL_MANAGER\");\n    bytes32 public constant STATS_LST_ETH_TOKEN_EXECUTOR = keccak256(\"STATS_LST_ETH_TOKEN_EXECUTOR\");\n    bytes32 public constant STATS_CACHE_SET_TRANSIENT_EXECUTOR = keccak256(\"STATS_CACHE_SET_TRANSIENT_EXECUTOR\");\n\n    // Emergency Management\n    bytes32 public constant EMERGENCY_PAUSER = keccak256(\"EMERGENCY_PAUSER\");\n    bytes32 public constant SEQUENCER_OVERRIDE_MANAGER = keccak256(\"SEQUENCER_OVERRIDE_MANAGER\");\n\n    // Miscellaneous Roles\n    bytes32 public constant SOLVER = keccak256(\"SOLVER_ROLE\");\n    bytes32 public constant AUTO_POOL_REPORTING_EXECUTOR = keccak256(\"AUTO_POOL_UPDATE_DEBT_REPORTING_ROLE\");\n    bytes32 public constant STRATEGY_HOOK_CONFIGURATION = keccak256(\"STRATEGY_HOOK_CONFIGURATION\");\n\n    // Swapper Roles\n    bytes32 public constant SWAP_ROUTER_MANAGER = keccak256(\"SWAP_ROUTER_MANAGER\");\n\n    // Price Oracles Roles\n    bytes32 public constant ORACLE_MANAGER = keccak256(\"ORACLE_MANAGER_ROLE\");\n    bytes32 public constant CUSTOM_ORACLE_EXECUTOR = keccak256(\"CUSTOM_ORACLE_EXECUTOR\");\n    bytes32 public constant MAVERICK_FEE_ORACLE_EXECUTOR = keccak256(\"MAVERICK_FEE_ORACLE_MANAGER\");\n\n    // AccToke Roles\n    bytes32 public constant ACC_TOKE_MANAGER = keccak256(\"ACC_TOKE_MANAGER\");\n\n    // Admin Roles\n    bytes32 public constant TOKEN_RECOVERY_MANAGER = keccak256(\"TOKEN_RECOVERY_ROLE\");\n    bytes32 public constant INFRASTRUCTURE_MANAGER = keccak256(\"INFRASTRUCTURE_MANAGER\");\n\n    // Cross chain communications roles\n    bytes32 public constant MESSAGE_PROXY_MANAGER = keccak256(\"MESSAGE_PROXY_MANAGER\");\n    bytes32 public constant MESSAGE_PROXY_EXECUTOR = keccak256(\"MESSAGE_PROXY_EXECUTOR\");\n    bytes32 public constant RECEIVING_ROUTER_MANAGER = keccak256(\"RECEIVING_ROUTER_MANAGER\");\n    bytes32 public constant RECEIVING_ROUTER_EXECUTOR = keccak256(\"RECEIVING_ROUTER_EXECUTOR\");\n}\n","Filename":"src/libs/Roles.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n    enum RecoverError {\n        NoError,\n        InvalidSignature,\n        InvalidSignatureLength,\n        InvalidSignatureS,\n        InvalidSignatureV // Deprecated in v4.8\n    }\n\n    function _throwError(RecoverError error) private pure {\n        if (error == RecoverError.NoError) {\n            return; // no error: do nothing\n        } else if (error == RecoverError.InvalidSignature) {\n            revert(\"ECDSA: invalid signature\");\n        } else if (error == RecoverError.InvalidSignatureLength) {\n            revert(\"ECDSA: invalid signature length\");\n        } else if (error == RecoverError.InvalidSignatureS) {\n            revert(\"ECDSA: invalid signature 's' value\");\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature` or error string. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {toEthSignedMessageHash} on it.\n     *\n     * Documentation for signature generation:\n     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n        if (signature.length == 65) {\n            bytes32 r;\n            bytes32 s;\n            uint8 v;\n            // ecrecover takes the signature parameters, and the only way to get them\n            // currently is to use assembly.\n            /// @solidity memory-safe-assembly\n            assembly {\n                r := mload(add(signature, 0x20))\n                s := mload(add(signature, 0x40))\n                v := byte(0, mload(add(signature, 0x60)))\n            }\n            return tryRecover(hash, v, r, s);\n        } else {\n            return (address(0), RecoverError.InvalidSignatureLength);\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature`. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {toEthSignedMessageHash} on it.\n     */\n    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, signature);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n     *\n     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(\n        bytes32 hash,\n        bytes32 r,\n        bytes32 vs\n    ) internal pure returns (address, RecoverError) {\n        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n        uint8 v = uint8((uint256(vs) >> 255) + 27);\n        return tryRecover(hash, v, r, s);\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n     *\n     * _Available since v4.2._\n     */\n    function recover(\n        bytes32 hash,\n        bytes32 r,\n        bytes32 vs\n    ) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     *\n     * _Available since v4.3._\n     */\n    function tryRecover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address, RecoverError) {\n        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n        // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n        //\n        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n        // these malleable signatures as well.\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            return (address(0), RecoverError.InvalidSignatureS);\n        }\n\n        // If the signature is valid (and not malleable), return the signer address\n        address signer = ecrecover(hash, v, r, s);\n        if (signer == address(0)) {\n            return (address(0), RecoverError.InvalidSignature);\n        }\n\n        return (signer, RecoverError.NoError);\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function recover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address) {\n        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n        _throwError(error);\n        return recovered;\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n     * produces hash corresponding to the one signed with the\n     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n     * JSON-RPC method as part of EIP-191.\n     *\n     * See {recover}.\n     */\n    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n        // 32 is the length in bytes of hash,\n        // enforced by the type signature above\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Message, created from `s`. This\n     * produces hash corresponding to the one signed with the\n     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n     * JSON-RPC method as part of EIP-191.\n     *\n     * See {recover}.\n     */\n    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n    }\n\n    /**\n     * @dev Returns an Ethereum Signed Typed Data, created from a\n     * `domainSeparator` and a `structHash`. This produces hash corresponding\n     * to the one signed with the\n     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n     * JSON-RPC method as part of EIP-712.\n     *\n     * See {recover}.\n     */\n    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n    }\n}\n","Filename":"lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { Address } from \"openzeppelin-contracts/utils/Address.sol\";\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\n// solhint-disable max-line-length\nlibrary Errors {\n    using Address for address;\n    ///////////////////////////////////////////////////////////////////\n    //                       Set errors\n    ///////////////////////////////////////////////////////////////////\n\n    error AccessDenied();\n    error ZeroAddress(string paramName);\n    error ZeroAmount();\n    error InsufficientBalance(address token);\n    error AssetNotAllowed(address token);\n    error NotImplemented();\n    error InvalidAddress(address addr);\n    error InvalidParam(string paramName);\n    error InvalidParams();\n    error UnsafePrice(address token, uint256 spotPrice, uint256 safePrice);\n    error AlreadySet(string param);\n    error AlreadyRegistered(address param);\n    error SlippageExceeded(uint256 expected, uint256 actual);\n    error ArrayLengthMismatch(uint256 length1, uint256 length2, string details);\n\n    error ItemNotFound();\n    error ItemExists();\n    error MissingRole(bytes32 role, address user);\n    error RegistryItemMissing(string item);\n    error NotRegistered();\n    // Used to check storage slot is empty before setting.\n    error MustBeZero();\n    // Used to check storage slot set before deleting.\n    error MustBeSet();\n\n    error ApprovalFailed(address token);\n    error FlashLoanFailed(address token, uint256 amount);\n\n    error SystemMismatch(address source1, address source2);\n\n    error InvalidToken(address token);\n    error UnreachableError();\n\n    error InvalidSigner(address signer);\n\n    error InvalidChainId(uint256 chainId);\n\n    error SenderMismatch(address recipient, address sender);\n\n    error UnsupportedMessage(bytes32 messageType, bytes message);\n\n    error NotSupported();\n\n    error InvalidConfiguration();\n\n    error InvalidDataReturned();\n\n    function verifyNotZero(address addr, string memory paramName) internal pure {\n        if (addr == address(0)) {\n            revert ZeroAddress(paramName);\n        }\n    }\n\n    function verifyNotZero(bytes32 key, string memory paramName) internal pure {\n        if (key == bytes32(0)) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotEmpty(string memory val, string memory paramName) internal pure {\n        if (bytes(val).length == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifyNotZero(uint256 num, string memory paramName) internal pure {\n        if (num == 0) {\n            revert InvalidParam(paramName);\n        }\n    }\n\n    function verifySystemsMatch(address component1, address component2) internal view {\n        address registry1 =\n            abi.decode(component1.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));\n        address registry2 =\n            abi.decode(component2.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));\n\n        if (registry1 != registry2) {\n            revert SystemMismatch(component1, component2);\n        }\n    }\n\n    function verifyArrayLengths(uint256 length1, uint256 length2, string memory details) internal pure {\n        if (length1 != length2) {\n            revert ArrayLengthMismatch(length1, length2, details);\n        }\n    }\n}\n","Filename":"src/utils/Errors.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { Roles } from \"src/libs/Roles.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { ISystemSecurity } from \"src/interfaces/security/ISystemSecurity.sol\";\n\n/**\n * @notice Contract which allows children to implement an emergency stop mechanism that can be trigger\n * by an account that has been granted the EMERGENCY_PAUSER role.\n * Makes available the `whenNotPaused` and `whenPaused` modifiers.\n * Respects a system level pause from the System Security.\n */\nabstract contract Pausable {\n    IAccessController private immutable _accessController;\n    ISystemSecurity private immutable _systemSecurity;\n\n    /// @dev Emitted when the pause is triggered by `account`.\n    event Paused(address account);\n\n    /// @dev Emitted when the pause is lifted by `account`.\n    event Unpaused(address account);\n\n    error IsPaused();\n    error IsNotPaused();\n\n    bool private _paused;\n\n    modifier whenNotPaused() {\n        _requireNotPaused();\n        _;\n    }\n\n    modifier whenPaused() {\n        _requirePaused();\n        _;\n    }\n\n    modifier isPauser() {\n        if (!_accessController.hasRole(Roles.EMERGENCY_PAUSER, msg.sender)) {\n            revert Errors.AccessDenied();\n        }\n        _;\n    }\n\n    constructor(\n        ISystemRegistry systemRegistry\n    ) {\n        Errors.verifyNotZero(address(systemRegistry), \"systemRegistry\");\n\n        // Validate the registry is in a state we can use it\n        IAccessController accessController = systemRegistry.accessController();\n        if (address(accessController) == address(0)) {\n            revert Errors.RegistryItemMissing(\"accessController\");\n        }\n        ISystemSecurity systemSecurity = systemRegistry.systemSecurity();\n        if (address(systemSecurity) == address(0)) {\n            revert Errors.RegistryItemMissing(\"systemSecurity\");\n        }\n\n        _accessController = accessController;\n        _systemSecurity = systemSecurity;\n    }\n\n    /// @notice Returns true if the contract or system is paused, and false otherwise.\n    function paused() public virtual returns (bool) {\n        return _paused || _systemSecurity.isSystemPaused();\n    }\n\n    /// @notice Pauses the contract\n    /// @dev Reverts if already paused or not EMERGENCY_PAUSER role\n    function pause() external virtual isPauser {\n        if (_paused) {\n            revert IsPaused();\n        }\n\n        _paused = true;\n\n        emit Paused(msg.sender);\n    }\n\n    /// @notice Unpauses the contract\n    /// @dev Reverts if not paused or not EMERGENCY_PAUSER role\n    function unpause() external virtual isPauser {\n        if (!_paused) {\n            revert IsNotPaused();\n        }\n\n        _paused = false;\n\n        emit Unpaused(msg.sender);\n    }\n\n    /// @dev Throws if the contract or system is paused.\n    function _requireNotPaused() internal virtual {\n        if (paused()) {\n            revert IsPaused();\n        }\n    }\n\n    /// @dev Throws if the contract or system is not paused.\n    function _requirePaused() internal virtual {\n        if (!paused()) {\n            revert IsNotPaused();\n        }\n    }\n}\n","Filename":"src/security/Pausable.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\n/// @title Capture information about a pool or destination\ninterface IStatsCalculator {\n    /// @notice thrown when no snapshot is taken\n    error NoSnapshotTaken();\n\n    /// @notice The id for this instance of a calculator\n    function getAprId() external view returns (bytes32);\n\n    /// @notice The id of the underlying asset/pool/destination this calculator represents\n    /// @dev This may be a generated address\n    function getAddressId() external view returns (address);\n\n    /// @notice Setup the calculator after it has been copied\n    /// @dev Should only be executed one time\n    /// @param dependentAprIds apr ids that cover the dependencies of this calculator\n    /// @param initData setup data specific to this type of calculator\n    function initialize(bytes32[] calldata dependentAprIds, bytes calldata initData) external;\n\n    /// @notice Capture stat data about this setup\n    function snapshot() external;\n\n    /// @notice Indicates if a snapshot should be taken\n    /// @return takeSnapshot if true then a snapshot should be taken. If false, calling snapshot will do nothing\n    function shouldSnapshot() external view returns (bool takeSnapshot);\n\n    /// @dev Enum representing the snapshot status for a given rewarder (Convex and Aura) or reward token (Maverick)\n    enum SnapshotStatus {\n        noSnapshot, // Indicates that no snapshot has been taken yet for the rewarder.\n        tooSoon, // Indicates that it's too soon to take another snapshot since the last one.\n        shouldFinalize, // Indicates that the conditions are met for finalizing a snapshot.\n        shouldRestart // Indicates that the conditions are met for restarting a snapshot.\n\n    }\n}\n","Filename":"src/interfaces/stats/IStatsCalculator.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\n// solhint-disable no-inline-assembly\n\n/// @notice Library for byte related operations.\n/// @notice Copied at\n/// https://github.com/Vectorized/solady/blob/ccaed15a964891aa729c9d22670304e88584a480/src/utils/LibBytes.sol\n/// @notice Pragma updated from 8.4 to 8.24\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)\nlibrary LibBytes {\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                          STRUCTS                           */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Goated bytes storage struct that totally MOGs, no cap, fr.\n    /// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.\n    /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.\n    struct BytesStorage {\n        bytes32 _spacer;\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         CONSTANTS                          */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev The constant returned when the `search` is not found in the bytes.\n    uint256 internal constant NOT_FOUND = type(uint256).max;\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                  BYTE STORAGE OPERATIONS                   */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Sets the value of the bytes storage `$` to `s`.\n    function set(BytesStorage storage $, bytes memory s) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(s)\n            let packed := or(0xff, shl(8, n))\n            for { let i := 0 } 1 { } {\n                if iszero(gt(n, 0xfe)) {\n                    i := 0x1f\n                    packed := or(n, shl(8, mload(add(s, i))))\n                    if iszero(gt(n, i)) { break }\n                }\n                let o := add(s, 0x20)\n                mstore(0x00, $.slot)\n                for { let p := keccak256(0x00, 0x20) } 1 { } {\n                    sstore(add(p, shr(5, i)), mload(add(o, i)))\n                    i := add(i, 0x20)\n                    if iszero(lt(i, n)) { break }\n                }\n                break\n            }\n            sstore($.slot, packed)\n        }\n    }\n\n    /// @dev Sets the value of the bytes storage `$` to `s`.\n    function setCalldata(BytesStorage storage $, bytes calldata s) internal {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let packed := or(0xff, shl(8, s.length))\n            for { let i := 0 } 1 { } {\n                if iszero(gt(s.length, 0xfe)) {\n                    i := 0x1f\n                    packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))\n                    if iszero(gt(s.length, i)) { break }\n                }\n                mstore(0x00, $.slot)\n                for { let p := keccak256(0x00, 0x20) } 1 { } {\n                    sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))\n                    i := add(i, 0x20)\n                    if iszero(lt(i, s.length)) { break }\n                }\n                break\n            }\n            sstore($.slot, packed)\n        }\n    }\n\n    /// @dev Sets the value of the bytes storage `$` to the empty bytes.\n    function clear(\n        BytesStorage storage $\n    ) internal {\n        delete $._spacer;\n    }\n\n    /// @dev Returns whether the value stored is `$` is the empty bytes \"\".\n    function isEmpty(\n        BytesStorage storage $\n    ) internal view returns (bool) {\n        return uint256($._spacer) & 0xff == uint256(0);\n    }\n\n    /// @dev Returns the length of the value stored in `$`.\n    function length(\n        BytesStorage storage $\n    ) internal view returns (uint256 result) {\n        result = uint256($._spacer);\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := and(0xff, result)\n            result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))\n        }\n    }\n\n    /// @dev Returns the value stored in `$`.\n    function get(\n        BytesStorage storage $\n    ) internal view returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(0x40)\n            let o := add(result, 0x20)\n            let packed := sload($.slot)\n            let n := shr(8, packed)\n            for { let i := 0 } 1 { } {\n                if iszero(eq(or(packed, 0xff), packed)) {\n                    mstore(o, packed)\n                    n := and(0xff, packed)\n                    i := 0x1f\n                    if iszero(gt(n, i)) { break }\n                }\n                mstore(0x00, $.slot)\n                for { let p := keccak256(0x00, 0x20) } 1 { } {\n                    mstore(add(o, i), sload(add(p, shr(5, i))))\n                    i := add(i, 0x20)\n                    if iszero(lt(i, n)) { break }\n                }\n                break\n            }\n            mstore(result, n) // Store the length of the memory.\n            mstore(add(o, n), 0) // Zeroize the slot after the bytes.\n            mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.\n        }\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                      BYTES OPERATIONS                      */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.\n    function replace(\n        bytes memory subject,\n        bytes memory needle,\n        bytes memory replacement\n    ) internal pure returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(0x40)\n            let needleLen := mload(needle)\n            let replacementLen := mload(replacement)\n            let d := sub(result, subject) // Memory difference.\n            let i := add(subject, 0x20) // Subject bytes pointer.\n            mstore(0x00, add(i, mload(subject))) // End of subject.\n            if iszero(gt(needleLen, mload(subject))) {\n                let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)\n                let h := 0 // The hash of `needle`.\n                if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }\n                let s := mload(add(needle, 0x20))\n                for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 { } {\n                    let t := mload(i)\n                    // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.\n                    if iszero(shr(m, xor(t, s))) {\n                        if h {\n                            if iszero(eq(keccak256(i, needleLen), h)) {\n                                mstore(add(i, d), t)\n                                i := add(i, 1)\n                                if iszero(lt(i, subjectSearchEnd)) { break }\n                                continue\n                            }\n                        }\n                        // Copy the `replacement` one word at a time.\n                        for { let j := 0 } 1 { } {\n                            mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))\n                            j := add(j, 0x20)\n                            if iszero(lt(j, replacementLen)) { break }\n                        }\n                        d := sub(add(d, replacementLen), needleLen)\n                        if needleLen {\n                            i := add(i, needleLen)\n                            if iszero(lt(i, subjectSearchEnd)) { break }\n                            continue\n                        }\n                    }\n                    mstore(add(i, d), t)\n                    i := add(i, 1)\n                    if iszero(lt(i, subjectSearchEnd)) { break }\n                }\n            }\n            let end := mload(0x00)\n            let n := add(sub(d, add(result, 0x20)), end)\n            // Copy the rest of the bytes one word at a time.\n            for { } lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }\n            let o := add(i, d)\n            mstore(o, 0) // Zeroize the slot after the bytes.\n            mstore(0x40, add(o, 0x20)) // Allocate memory.\n            mstore(result, n) // Store the length.\n        }\n    }\n\n    /// @dev Returns the byte index of the first location of `needle` in `subject`,\n    /// needleing from left to right, starting from `from`.\n    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.\n    function indexOf(bytes memory subject, bytes memory needle, uint256 from) internal pure returns (uint256 result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := not(0) // Initialize to `NOT_FOUND`.\n            for { let subjectLen := mload(subject) } 1 { } {\n                if iszero(mload(needle)) {\n                    result := from\n                    if iszero(gt(from, subjectLen)) { break }\n                    result := subjectLen\n                    break\n                }\n                let needleLen := mload(needle)\n                let subjectStart := add(subject, 0x20)\n\n                subject := add(subjectStart, from)\n                let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)\n                let m := shl(3, sub(0x20, and(needleLen, 0x1f)))\n                let s := mload(add(needle, 0x20))\n\n                if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }\n\n                if iszero(lt(needleLen, 0x20)) {\n                    for { let h := keccak256(add(needle, 0x20), needleLen) } 1 { } {\n                        if iszero(shr(m, xor(mload(subject), s))) {\n                            if eq(keccak256(subject, needleLen), h) {\n                                result := sub(subject, subjectStart)\n                                break\n                            }\n                        }\n                        subject := add(subject, 1)\n                        if iszero(lt(subject, end)) { break }\n                    }\n                    break\n                }\n                for { } 1 { } {\n                    if iszero(shr(m, xor(mload(subject), s))) {\n                        result := sub(subject, subjectStart)\n                        break\n                    }\n                    subject := add(subject, 1)\n                    if iszero(lt(subject, end)) { break }\n                }\n                break\n            }\n        }\n    }\n\n    /// @dev Returns the byte index of the first location of `needle` in `subject`,\n    /// needleing from left to right.\n    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.\n    function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {\n        return indexOf(subject, needle, 0);\n    }\n\n    /// @dev Returns the byte index of the first location of `needle` in `subject`,\n    /// needleing from right to left, starting from `from`.\n    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.\n    function lastIndexOf(\n        bytes memory subject,\n        bytes memory needle,\n        uint256 from\n    ) internal pure returns (uint256 result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            for { } 1 { } {\n                result := not(0) // Initialize to `NOT_FOUND`.\n                let needleLen := mload(needle)\n                if gt(needleLen, mload(subject)) { break }\n                let w := result\n\n                let fromMax := sub(mload(subject), needleLen)\n                if iszero(gt(fromMax, from)) { from := fromMax }\n\n                let end := add(add(subject, 0x20), w)\n                subject := add(add(subject, 0x20), from)\n                if iszero(gt(subject, end)) { break }\n                // As this function is not too often used,\n                // we shall simply use keccak256 for smaller bytecode size.\n                for { let h := keccak256(add(needle, 0x20), needleLen) } 1 { } {\n                    if eq(keccak256(subject, needleLen), h) {\n                        result := sub(subject, add(end, 1))\n                        break\n                    }\n                    subject := add(subject, w) // `sub(subject, 1)`.\n                    if iszero(gt(subject, end)) { break }\n                }\n                break\n            }\n        }\n    }\n\n    /// @dev Returns the byte index of the first location of `needle` in `subject`,\n    /// needleing from right to left.\n    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.\n    function lastIndexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {\n        return lastIndexOf(subject, needle, type(uint256).max);\n    }\n\n    /// @dev Returns true if `needle` is found in `subject`, false otherwise.\n    function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {\n        return indexOf(subject, needle) != NOT_FOUND;\n    }\n\n    /// @dev Returns whether `subject` starts with `needle`.\n    function startsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(needle)\n            // Just using keccak256 directly is actually cheaper.\n            let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))\n            result := lt(gt(n, mload(subject)), t)\n        }\n    }\n\n    /// @dev Returns whether `subject` ends with `needle`.\n    function endsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(needle)\n            let notInRange := gt(n, mload(subject))\n            // `subject + 0x20 + max(subject.length - needle.length, 0)`.\n            let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))\n            // Just using keccak256 directly is actually cheaper.\n            result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)\n        }\n    }\n\n    /// @dev Returns `subject` repeated `times`.\n    function repeat(bytes memory subject, uint256 times) internal pure returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let l := mload(subject) // Subject length.\n            if iszero(or(iszero(times), iszero(l))) {\n                result := mload(0x40)\n                subject := add(subject, 0x20)\n                let o := add(result, 0x20)\n                for { } 1 { } {\n                    // Copy the `subject` one word at a time.\n                    for { let j := 0 } 1 { } {\n                        mstore(add(o, j), mload(add(subject, j)))\n                        j := add(j, 0x20)\n                        if iszero(lt(j, l)) { break }\n                    }\n                    o := add(o, l)\n                    times := sub(times, 1)\n                    if iszero(times) { break }\n                }\n                mstore(o, 0) // Zeroize the slot after the bytes.\n                mstore(0x40, add(o, 0x20)) // Allocate memory.\n                mstore(result, sub(o, add(result, 0x20))) // Store the length.\n            }\n        }\n    }\n\n    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n    /// `start` and `end` are byte offsets.\n    function slice(bytes memory subject, uint256 start, uint256 end) internal pure returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let l := mload(subject) // Subject length.\n            if iszero(gt(l, end)) { end := l }\n            if iszero(gt(l, start)) { start := l }\n            if lt(start, end) {\n                result := mload(0x40)\n                let n := sub(end, start)\n                let i := add(subject, start)\n                let w := not(0x1f)\n                // Copy the `subject` one word at a time, backwards.\n                for { let j := and(add(n, 0x1f), w) } 1 { } {\n                    mstore(add(result, j), mload(add(i, j)))\n                    j := add(j, w) // `sub(j, 0x20)`.\n                    if iszero(j) { break }\n                }\n                let o := add(add(result, 0x20), n)\n                mstore(o, 0) // Zeroize the slot after the bytes.\n                mstore(0x40, add(o, 0x20)) // Allocate memory.\n                mstore(result, n) // Store the length.\n            }\n        }\n    }\n\n    /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.\n    /// `start` is a byte offset.\n    function slice(bytes memory subject, uint256 start) internal pure returns (bytes memory result) {\n        result = slice(subject, start, type(uint256).max);\n    }\n\n    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n    /// `start` and `end` are byte offsets. Faster than Solidity's native slicing.\n    function sliceCalldata(\n        bytes calldata subject,\n        uint256 start,\n        uint256 end\n    ) internal pure returns (bytes calldata result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))\n            start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))\n            result.offset := add(subject.offset, start)\n            result.length := mul(lt(start, end), sub(end, start))\n        }\n    }\n\n    /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.\n    /// `start` is a byte offset. Faster than Solidity's native slicing.\n    function sliceCalldata(bytes calldata subject, uint256 start) internal pure returns (bytes calldata result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))\n            result.offset := add(subject.offset, start)\n            result.length := mul(lt(start, subject.length), sub(subject.length, start))\n        }\n    }\n\n    /// @dev Reduces the size of `subject` to `n`.\n    /// If `n` is greater than the size of `subject`, this will be a no-op.\n    function truncate(bytes memory subject, uint256 n) internal pure returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := subject\n            mstore(mul(lt(n, mload(result)), result), n)\n        }\n    }\n\n    /// @dev Returns a copy of `subject`, with the length reduced to `n`.\n    /// If `n` is greater than the size of `subject`, this will be a no-op.\n    function truncatedCalldata(bytes calldata subject, uint256 n) internal pure returns (bytes calldata result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result.offset := subject.offset\n            result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))\n        }\n    }\n\n    /// @dev Returns all the indices of `needle` in `subject`.\n    /// The indices are byte offsets.\n    function indicesOf(bytes memory subject, bytes memory needle) internal pure returns (uint256[] memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let searchLen := mload(needle)\n            if iszero(gt(searchLen, mload(subject))) {\n                result := mload(0x40)\n                let i := add(subject, 0x20)\n                let o := add(result, 0x20)\n                let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)\n                let h := 0 // The hash of `needle`.\n                if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }\n                let s := mload(add(needle, 0x20))\n                for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 { } {\n                    let t := mload(i)\n                    // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.\n                    if iszero(shr(m, xor(t, s))) {\n                        if h {\n                            if iszero(eq(keccak256(i, searchLen), h)) {\n                                i := add(i, 1)\n                                if iszero(lt(i, subjectSearchEnd)) { break }\n                                continue\n                            }\n                        }\n                        mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.\n                        o := add(o, 0x20)\n                        i := add(i, searchLen) // Advance `i` by `searchLen`.\n                        if searchLen {\n                            if iszero(lt(i, subjectSearchEnd)) { break }\n                            continue\n                        }\n                    }\n                    i := add(i, 1)\n                    if iszero(lt(i, subjectSearchEnd)) { break }\n                }\n                mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.\n                // Allocate memory for result.\n                // We allocate one more word, so this array can be recycled for {split}.\n                mstore(0x40, add(o, 0x20))\n            }\n        }\n    }\n\n    /// @dev Returns a arrays of bytess based on the `delimiter` inside of the `subject` bytes.\n    function split(bytes memory subject, bytes memory delimiter) internal pure returns (bytes[] memory result) {\n        uint256[] memory indices = indicesOf(subject, delimiter);\n        /// @solidity memory-safe-assembly\n        assembly {\n            let w := not(0x1f)\n            let indexPtr := add(indices, 0x20)\n            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n            mstore(add(indicesEnd, w), mload(subject))\n            mstore(indices, add(mload(indices), 1))\n            for { let prevIndex := 0 } 1 { } {\n                let index := mload(indexPtr)\n                mstore(indexPtr, 0x60)\n                if iszero(eq(index, prevIndex)) {\n                    let element := mload(0x40)\n                    let l := sub(index, prevIndex)\n                    mstore(element, l) // Store the length of the element.\n                    // Copy the `subject` one word at a time, backwards.\n                    for { let o := and(add(l, 0x1f), w) } 1 { } {\n                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n                        o := add(o, w) // `sub(o, 0x20)`.\n                        if iszero(o) { break }\n                    }\n                    mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.\n                    // Allocate memory for the length and the bytes, rounded up to a multiple of 32.\n                    mstore(0x40, add(element, and(add(l, 0x3f), w)))\n                    mstore(indexPtr, element) // Store the `element` into the array.\n                }\n                prevIndex := add(index, mload(delimiter))\n                indexPtr := add(indexPtr, 0x20)\n                if iszero(lt(indexPtr, indicesEnd)) { break }\n            }\n            result := indices\n            if iszero(mload(delimiter)) {\n                result := add(indices, 0x20)\n                mstore(result, sub(mload(indices), 2))\n            }\n        }\n    }\n\n    /// @dev Returns a concatenated bytes of `a` and `b`.\n    /// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.\n    function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(0x40)\n            let w := not(0x1f)\n            let aLen := mload(a)\n            // Copy `a` one word at a time, backwards.\n            for { let o := and(add(aLen, 0x20), w) } 1 { } {\n                mstore(add(result, o), mload(add(a, o)))\n                o := add(o, w) // `sub(o, 0x20)`.\n                if iszero(o) { break }\n            }\n            let bLen := mload(b)\n            let output := add(result, aLen)\n            // Copy `b` one word at a time, backwards.\n            for { let o := and(add(bLen, 0x20), w) } 1 { } {\n                mstore(add(output, o), mload(add(b, o)))\n                o := add(o, w) // `sub(o, 0x20)`.\n                if iszero(o) { break }\n            }\n            let totalLen := add(aLen, bLen)\n            let last := add(add(result, 0x20), totalLen)\n            mstore(last, 0) // Zeroize the slot after the bytes.\n            mstore(result, totalLen) // Store the length.\n            mstore(0x40, add(last, 0x20)) // Allocate memory.\n        }\n    }\n\n    /// @dev Returns whether `a` equals `b`.\n    function eq(bytes memory a, bytes memory b) internal pure returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n        }\n    }\n\n    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes.\n    function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // These should be evaluated on compile time, as far as possible.\n            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n            let x := not(or(m, or(b, add(m, and(b, m)))))\n            let r := shl(7, iszero(iszero(shr(128, x))))\n            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n            r := or(r, shl(4, lt(0xffff, shr(r, x))))\n            r := or(r, shl(3, lt(0xff, shr(r, x))))\n            // forgefmt: disable-next-item\n            result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n                xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n        }\n    }\n\n    /// @dev Directly returns `a` without copying.\n    function directReturn(\n        bytes memory a\n    ) internal pure {\n        assembly {\n            // Assumes that the bytes does not start from the scratch space.\n            let retStart := sub(a, 0x20)\n            let retUnpaddedSize := add(mload(a), 0x40)\n            // Right pad with zeroes. Just in case the bytes is produced\n            // by a method that doesn't zero right pad.\n            mstore(add(retStart, retUnpaddedSize), 0)\n            mstore(retStart, 0x20) // Store the return offset.\n            // End the transaction, returning the bytes.\n            return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))\n        }\n    }\n\n    /// @dev Directly returns `a` with minimal copying.\n    function directReturn(\n        bytes[] memory a\n    ) internal pure {\n        assembly {\n            let n := mload(a) // `a.length`.\n            let o := add(a, 0x20) // Start of elements in `a`.\n            let u := a // Highest memory slot.\n            let w := not(0x1f)\n            for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {\n                let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.\n                let s := mload(c) // `a[i]`.\n                let l := mload(s) // `a[i].length`.\n                let r := and(l, 0x1f) // `a[i].length % 32`.\n                let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.\n                // If `s` comes before `o`, or `s` is not zero right padded.\n                if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {\n                    let m := mload(0x40)\n                    mstore(m, l) // Copy `a[i].length`.\n                    for { } 1 { } {\n                        mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.\n                        z := add(z, w) // `sub(z, 0x20)`.\n                        if iszero(z) { break }\n                    }\n                    let e := add(add(m, 0x20), l)\n                    mstore(e, 0) // Zeroize the slot after the copied bytes.\n                    mstore(0x40, add(e, 0x20)) // Allocate memory.\n                    s := m\n                }\n                mstore(c, sub(s, o)) // Convert to calldata offset.\n                let t := add(l, add(s, 0x20))\n                if iszero(lt(t, u)) { u := t }\n            }\n            let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.\n            mstore(retStart, 0x20) // Store the return offset.\n            return(retStart, add(0x40, sub(u, retStart))) // End the transaction.\n        }\n    }\n\n    /// @dev Returns the word at `offset`, without any bounds checks.\n    /// To load an address, you can use `address(bytes20(load(a, offset)))`.\n    function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := mload(add(add(a, 0x20), offset))\n        }\n    }\n\n    /// @dev Returns the word at `offset`, without any bounds checks.\n    /// To load an address, you can use `address(bytes20(loadCalldata(a, offset)))`.\n    function loadCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes32 result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := calldataload(add(a.offset, offset))\n        }\n    }\n\n    /// @dev Returns empty calldata bytes. For silencing the compiler.\n    function emptyCalldata() internal pure returns (bytes calldata result) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            result.length := 0\n        }\n    }\n}\n","Filename":"src/external/solady/LibBytes.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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","Filename":"src/interfaces/ISystemComponent.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\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 { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { AutopoolState, ProcessRebalanceParams } from \"src/vault/libs/AutopoolState.sol\";\nimport { AutopoolStrategyHooks } from \"src/vault/libs/AutopoolStrategyHooks.sol\";\nimport { IStrategyHook, HookFunctionIndex } from \"src/interfaces/strategy/IStrategyHook.sol\";\n\nlibrary AutopoolDebt {\n    using Math for uint256;\n    using SafeERC20 for IERC20;\n    using WithdrawalQueue for StructuredLinkedList.List;\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using AutopoolToken for AutopoolToken.TokenData;\n\n    /// @notice Max time a cached debt report can be used\n    uint256 public constant MAX_DEBT_REPORT_AGE_SECONDS = 1 days;\n\n    error VaultShutdown();\n    error WithdrawShareCalcInvalid(uint256 currentShares, uint256 cachedShares);\n    error RebalanceFailed(string message);\n    error InvalidPrices();\n    error InvalidTotalAssetPurpose();\n    error InvalidDestination(address destination);\n    error TooFewAssets(uint256 requested, uint256 actual);\n    error SharesAndAssetsReceived(uint256 assets, uint256 shares);\n    error AmountExceedsAllowance(uint256 shares, uint256 allowed);\n    error PositivePriceRecoupNotCovered(uint256 remaining);\n    error RebalanceDestinationsMatch();\n    error InsufficientAssets(address asset);\n    error RebalanceDestinationUnderlyerMismatch(address destination, address trueUnderlyer, address providedUnderlyer);\n    error OnlyRebalanceToIdleAvailable();\n    error UnregisteredDestination(address dest);\n\n    event DestinationDebtReporting(\n        address destination, AutopoolDebt.IdleDebtUpdates debtInfo, uint256 claimed, uint256 claimGasUsed\n    );\n    event NewNavShareFeeMark(uint256 navPerShare, uint256 timestamp);\n    event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    event Withdraw(\n        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares\n    );\n\n    struct DestinationInfo {\n        /// @notice Current underlying value at the destination vault\n        /// @dev Used for calculating totalDebt, mid point of min and max\n        uint256 cachedDebtValue;\n        /// @notice Current minimum underlying value at the destination vault\n        /// @dev Used for calculating totalDebt during withdrawal\n        uint256 cachedMinDebtValue;\n        /// @notice Current maximum underlying value at the destination vault\n        /// @dev Used for calculating totalDebt of the deposit\n        uint256 cachedMaxDebtValue;\n        /// @notice Last block timestamp this info was updated\n        uint256 lastReport;\n        /// @notice How many shares of the destination vault we owned at last report\n        uint256 ownedShares;\n    }\n\n    struct IdleDebtUpdates {\n        bool pricesWereSafe;\n        uint256 totalIdleDecrease;\n        uint256 totalIdleIncrease;\n        uint256 totalDebtIncrease;\n        uint256 totalDebtDecrease;\n        uint256 totalMinDebtIncrease;\n        uint256 totalMinDebtDecrease;\n        uint256 totalMaxDebtIncrease;\n        uint256 totalMaxDebtDecrease;\n    }\n\n    struct AssetChanges {\n        uint256 startingIdle;\n        uint256 startingDebt;\n        uint256 startingTotalSupply;\n        uint256 newIdle;\n        uint256 newDebt;\n        uint256 endingTotalSupply;\n    }\n\n    struct RebalanceOutParams {\n        /// Address that will received the withdrawn underlyer\n        address receiver;\n        /// The \"out\" destination vault\n        address destinationOut;\n        /// The amount of tokenOut that will be withdrawn\n        uint256 amountOut;\n        /// The underlyer for destinationOut\n        address tokenOut;\n        IERC20 _baseAsset;\n        bool _shutdown;\n    }\n\n    /// @dev In memory struct only for managing vars in _withdraw\n    struct WithdrawInfo {\n        uint256 currentIdle;\n        uint256 assetsFromIdle;\n        uint256 totalAssetsToPull;\n        uint256 assetsToPull;\n        uint256 assetsPulled;\n        uint256 idleIncrease;\n        uint256 debtDecrease;\n        uint256 debtMinDecrease;\n        uint256 debtMaxDecrease;\n        uint256 totalMinDebt;\n        uint256 destinationRound;\n        uint256 lastRoundSlippage;\n        uint256 expectedAssets;\n        uint256 remainingRecoup;\n    }\n\n    struct FlashRebalanceParams {\n        IERC20 baseAsset;\n        bool shutdown;\n    }\n\n    struct FlashResultInfo {\n        uint256 tokenInBalanceBefore;\n        uint256 tokenInBalanceAfter;\n        bytes32 flashResult;\n    }\n\n    function processRebalance(\n        AutopoolState storage $,\n        ProcessRebalanceParams memory args,\n        bytes calldata data,\n        bytes memory hooks\n    ) external returns (AutopoolDebt.AssetChanges memory updates) {\n        validateRebalanceParams($, args);\n\n        updates.startingIdle = $.assetBreakdown.totalIdle;\n        updates.startingDebt = $.assetBreakdown.totalDebt;\n\n        AutopoolDebt.IdleDebtUpdates memory result = flashRebalance($, args, data, hooks);\n\n        updates.newIdle = updates.startingIdle + result.totalIdleIncrease - result.totalIdleDecrease;\n        updates.newDebt = updates.startingDebt + result.totalDebtIncrease - result.totalDebtDecrease;\n\n        $.assetBreakdown.totalIdle = updates.newIdle;\n        $.assetBreakdown.totalDebt = updates.newDebt;\n        $.assetBreakdown.totalDebtMin =\n            $.assetBreakdown.totalDebtMin + result.totalMinDebtIncrease - result.totalMinDebtDecrease;\n        $.assetBreakdown.totalDebtMax =\n            $.assetBreakdown.totalDebtMax + result.totalMaxDebtIncrease - result.totalMaxDebtDecrease;\n    }\n\n    function flashRebalance(\n        AutopoolState storage $,\n        ProcessRebalanceParams memory args,\n        bytes calldata data,\n        bytes memory hooks\n    ) private returns (IdleDebtUpdates memory result) {\n        DestinationInfo storage destInfoOut = $.destinationInfo[args.rebalanceParams.destinationOut];\n        DestinationInfo storage destInfoIn = $.destinationInfo[args.rebalanceParams.destinationIn];\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceStart),\n            abi.encodeCall(IStrategyHook.onRebalanceStart, (args, msg.sender))\n        );\n\n        // Handle decrease (shares going \"Out\", cashing in shares and sending underlying back to swapper)\n        // If the tokenOut is _asset we assume they are taking idle\n        // which is already in the contract\n        result = _handleRebalanceOut(\n            AutopoolDebt.RebalanceOutParams({\n                receiver: address(args.receiver),\n                destinationOut: args.rebalanceParams.destinationOut,\n                amountOut: args.rebalanceParams.amountOut,\n                tokenOut: args.rebalanceParams.tokenOut,\n                _baseAsset: args.baseAsset,\n                _shutdown: $.shutdown\n            }),\n            destInfoOut\n        );\n\n        if (!result.pricesWereSafe) {\n            revert InvalidPrices();\n        }\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceOutAssetsReady),\n            abi.encodeCall(IStrategyHook.onRebalanceOutAssetsReady, (args, msg.sender))\n        );\n\n        // Handle increase (shares coming \"In\", getting underlying from the swapper and trading for new shares)\n\n        FlashResultInfo memory flashResultInfo;\n        // get \"before\" counts\n        flashResultInfo.tokenInBalanceBefore = IERC20(args.rebalanceParams.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 =\n            args.receiver.onFlashLoan(msg.sender, args.rebalanceParams.tokenIn, args.rebalanceParams.amountIn, 0, data);\n\n        // We assume the solver will send us the assets\n        flashResultInfo.tokenInBalanceAfter = IERC20(args.rebalanceParams.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\n                    < flashResultInfo.tokenInBalanceBefore + args.rebalanceParams.amountIn\n        ) {\n            revert Errors.FlashLoanFailed(args.rebalanceParams.tokenIn, args.rebalanceParams.amountIn);\n        }\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceInAssetsReturned),\n            abi.encodeCall(IStrategyHook.onRebalanceInAssetsReturned, (args, msg.sender))\n        );\n\n        if (args.rebalanceParams.tokenIn != address(args.baseAsset)) {\n            IdleDebtUpdates memory inDebtResult = _handleRebalanceIn(\n                destInfoIn,\n                IDestinationVault(args.rebalanceParams.destinationIn),\n                args.rebalanceParams.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        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceDestinationVaultUpdated),\n            abi.encodeCall(IStrategyHook.onRebalanceDestinationVaultUpdated, (args, msg.sender))\n        );\n    }\n\n    function validateRebalanceParams(AutopoolState storage $, ProcessRebalanceParams memory args) private view {\n        address autopool = address(this);\n\n        Errors.verifyNotZero(args.rebalanceParams.destinationIn, \"destinationIn\");\n        Errors.verifyNotZero(args.rebalanceParams.destinationOut, \"destinationOut\");\n        Errors.verifyNotZero(args.rebalanceParams.tokenIn, \"tokenIn\");\n        Errors.verifyNotZero(args.rebalanceParams.tokenOut, \"tokenOut\");\n        Errors.verifyNotZero(args.rebalanceParams.amountIn, \"amountIn\");\n        Errors.verifyNotZero(args.rebalanceParams.amountOut, \"amountOut\");\n\n        ensureDestinationRegistered(autopool, args.rebalanceParams.destinationIn);\n        ensureDestinationRegistered(autopool, args.rebalanceParams.destinationOut);\n\n        // when a vault is shutdown, rebalancing can only pull assets from destinations back to the vault\n        if ($.shutdown && args.rebalanceParams.destinationIn != autopool) {\n            revert OnlyRebalanceToIdleAvailable();\n        }\n\n        if (args.rebalanceParams.destinationIn == args.rebalanceParams.destinationOut) {\n            revert RebalanceDestinationsMatch();\n        }\n\n        address baseAsset = address(args.baseAsset);\n\n        // if the in/out destination is the AutopoolETH then the in/out token must be the baseAsset\n        // if the in/out is not the AutopoolETH then the in/out token must match the destinations underlying token\n        if (args.rebalanceParams.destinationIn == autopool) {\n            if (args.rebalanceParams.tokenIn != baseAsset) {\n                revert RebalanceDestinationUnderlyerMismatch(\n                    args.rebalanceParams.destinationIn, args.rebalanceParams.tokenIn, baseAsset\n                );\n            }\n        } else {\n            IDestinationVault inDest = IDestinationVault(args.rebalanceParams.destinationIn);\n            if (args.rebalanceParams.tokenIn != inDest.underlying()) {\n                revert RebalanceDestinationUnderlyerMismatch(\n                    args.rebalanceParams.destinationIn, inDest.underlying(), args.rebalanceParams.tokenIn\n                );\n            }\n        }\n\n        if (args.rebalanceParams.destinationOut == autopool) {\n            if (args.rebalanceParams.tokenOut != baseAsset) {\n                revert RebalanceDestinationUnderlyerMismatch(\n                    args.rebalanceParams.destinationOut, args.rebalanceParams.tokenOut, baseAsset\n                );\n            }\n            if (args.rebalanceParams.amountOut > $.assetBreakdown.totalIdle) {\n                revert InsufficientAssets(args.rebalanceParams.tokenOut);\n            }\n        } else {\n            IDestinationVault outDest = IDestinationVault(args.rebalanceParams.destinationOut);\n            if (args.rebalanceParams.tokenOut != outDest.underlying()) {\n                revert RebalanceDestinationUnderlyerMismatch(\n                    args.rebalanceParams.destinationOut, outDest.underlying(), args.rebalanceParams.tokenOut\n                );\n            }\n            if (args.rebalanceParams.amountOut > outDest.balanceOf(autopool)) {\n                revert InsufficientAssets(args.rebalanceParams.tokenOut);\n            }\n        }\n    }\n\n    function ensureDestinationRegistered(address autopool, address dest) private view {\n        if (dest == address(autopool)) return;\n        if (\n            !(\n                IAutopool(autopool).isDestinationRegistered(dest)\n                    || IAutopool(autopool).isDestinationQueuedForRemoval(dest)\n            )\n        ) {\n            revert UnregisteredDestination(dest);\n        }\n    }\n\n    /// @notice Perform deposit and debt info update for the \"in\" destination during a rebalance\n    /// @dev This \"in\" function performs less validations than its \"out\" version\n    /// @param dvIn The \"in\" destination vault\n    /// @param tokenIn The underlyer for dvIn\n    /// @param depositAmount The amount of tokenIn that will be deposited\n    /// @return result Changes in debt values\n    function _handleRebalanceIn(\n        DestinationInfo storage destInfo,\n        IDestinationVault dvIn,\n        address tokenIn,\n        uint256 depositAmount\n    ) private returns (IdleDebtUpdates memory result) {\n        LibAdapter._approve(IERC20(tokenIn), address(dvIn), depositAmount);\n\n        // Snapshot our current shares so we know how much to back out\n        uint256 originalShareBal = dvIn.balanceOf(address(this));\n\n        // deposit to dv\n        uint256 newShares = dvIn.depositUnderlying(depositAmount);\n\n        // Update the debt info snapshot\n        result = _recalculateDestInfo(destInfo, dvIn, originalShareBal, originalShareBal + newShares);\n    }\n\n    function oldestDebtReporting(\n        AutopoolState storage $\n    ) public view returns (uint256) {\n        return $.destinationInfo[$.debtReportQueue.peekHead()].lastReport;\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\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            // 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    function recalculateDestInfo(\n        DestinationInfo storage destInfo,\n        IDestinationVault destVault,\n        uint256 originalShares,\n        uint256 currentShares\n    ) external returns (IdleDebtUpdates memory result) {\n        result = _recalculateDestInfo(destInfo, destVault, originalShares, currentShares);\n    }\n\n    /// @dev Will not revert on unsafe prices. Up to the caller.\n    function _recalculateDestInfo(\n        DestinationInfo storage destInfo,\n        IDestinationVault destVault,\n        uint256 originalShares,\n        uint256 currentShares\n    ) private returns (IdleDebtUpdates memory result) {\n        // Figure out what to back out of our totalDebt number.\n        // We could have had withdraws since the last snapshot which means our\n        // cached currentDebt number should be decreased based on the remaining shares\n        // totalDebt is decreased using the same proportion of shares method during withdrawals\n        // so this should represent whatever is remaining.\n\n        // Prices are per LP token and whether or not the prices are safe to use\n        // If they aren't safe then just continue and we'll get it on the next go around\n\n        (uint256 spotPrice, uint256 safePrice, bool isSpotSafe) = destVault.getRangePricesLP();\n\n        // Calculate what we're backing out based on the original shares\n        uint256 minPrice = spotPrice > safePrice ? safePrice : spotPrice;\n        uint256 maxPrice = spotPrice > safePrice ? spotPrice : safePrice;\n\n        // If we previously had shares, calculate how much of our cached numbers\n        // still remain as this will be deducted from the overall debt numbers\n        // over time\n        uint256 prevOwnedShares = destInfo.ownedShares;\n        if (prevOwnedShares > 0) {\n            result.totalDebtDecrease = (destInfo.cachedDebtValue * originalShares) / prevOwnedShares;\n            result.totalMinDebtDecrease = (destInfo.cachedMinDebtValue * originalShares) / prevOwnedShares;\n            result.totalMaxDebtDecrease = (destInfo.cachedMaxDebtValue * originalShares) / prevOwnedShares;\n        }\n\n        // The overall debt value is the mid point of min and max\n        uint256 div = 10 ** destVault.decimals();\n        uint256 newDebtValue = (minPrice * currentShares + maxPrice * currentShares) / (div * 2);\n\n        result.pricesWereSafe = isSpotSafe;\n        result.totalDebtIncrease = newDebtValue;\n        result.totalMinDebtIncrease = minPrice * currentShares / div;\n        result.totalMaxDebtIncrease = maxPrice * currentShares / div;\n\n        // Save our current new values\n        destInfo.cachedDebtValue = newDebtValue;\n        destInfo.cachedMinDebtValue = result.totalMinDebtIncrease;\n        destInfo.cachedMaxDebtValue = result.totalMaxDebtIncrease;\n        destInfo.lastReport = block.timestamp;\n        destInfo.ownedShares = currentShares;\n    }\n\n    function totalAssetsTimeChecked(\n        AutopoolState storage $,\n        IAutopool.TotalAssetPurpose purpose\n    ) external returns (uint256) {\n        IDestinationVault destVault = IDestinationVault($.debtReportQueue.peekHead());\n        uint256 recalculatedTotalAssets = IAutopool(address(this)).totalAssets(purpose);\n\n        while (address(destVault) != address(0)) {\n            uint256 lastReport = $.destinationInfo[address(destVault)].lastReport;\n\n            if (lastReport + MAX_DEBT_REPORT_AGE_SECONDS > block.timestamp) {\n                // Its not stale\n\n                // This report is OK, we don't need to recalculate anything\n                break;\n            } else {\n                // It is stale, recalculate\n\n                //slither-disable-next-line unused-return\n                uint256 currentShares = destVault.balanceOf(address(this));\n                uint256 staleDebt;\n                uint256 extremePrice;\n\n                // Figure out exactly which price to use based on its purpose\n                if (purpose == IAutopool.TotalAssetPurpose.Deposit) {\n                    // We use max value so that anything deposited is worth less\n                    extremePrice = destVault.getUnderlyerCeilingPrice();\n\n                    // Round down. We are subtracting this value out of the total so some left\n                    // behind just increases the value which is what we want\n                    staleDebt = $.destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                        currentShares, $.destinationInfo[address(destVault)].ownedShares, Math.Rounding.Down\n                    );\n                } else if (purpose == IAutopool.TotalAssetPurpose.Withdraw) {\n                    // We use min value so that we value the shares as worth less\n                    extremePrice = destVault.getUnderlyerFloorPrice();\n                    // Round up. We are subtracting this value out of the total so if we take a little\n                    // extra it just decreases the value which is what we want\n                    staleDebt = $.destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                        currentShares, $.destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n                    );\n                } else {\n                    revert InvalidTotalAssetPurpose();\n                }\n\n                // Back out our stale debt, add in its new value\n                // Our goal is to find the most conservative value in each situation. If the current\n                // value we have represents that, then use it. Otherwise, use the new one.\n\n                uint256 newValue = (currentShares * extremePrice) / destVault.ONE();\n\n                if (purpose == IAutopool.TotalAssetPurpose.Deposit && staleDebt > newValue) {\n                    newValue = staleDebt;\n                } else if (purpose == IAutopool.TotalAssetPurpose.Withdraw && staleDebt < newValue) {\n                    newValue = staleDebt;\n                }\n\n                recalculatedTotalAssets = recalculatedTotalAssets + newValue - staleDebt;\n            }\n\n            destVault = IDestinationVault($.debtReportQueue.getAdjacent(address(destVault), true));\n        }\n\n        return recalculatedTotalAssets;\n    }\n\n    function updateDebtReporting(\n        AutopoolState storage $,\n        uint256 numToProcess,\n        bytes memory hooks\n    ) external returns (AssetChanges memory changes) {\n        IdleDebtUpdates memory result;\n\n        // Persist our change in idle and debt\n        changes.startingIdle = $.assetBreakdown.totalIdle;\n        changes.startingDebt = $.assetBreakdown.totalDebt;\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            // Get the reward value we've earned. DV rewards are always in terms of base asset\n            // We track the gas used purely for off-chain stats purposes\n            // Main rewarder on DV's store the earned and liquidated rewards\n            // Extra rewarders are disabled at the DV level\n            uint256 claimGasUsed = gasleft();\n            uint256 beforeBaseAsset = IERC20(IAutopool(address(this)).asset()).balanceOf(address(this));\n            IMainRewarder(destVault.rewarder()).getReward(address(this), address(this), false);\n            uint256 claimedRewardValue =\n                IERC20(IAutopool(address(this)).asset()).balanceOf(address(this)) - beforeBaseAsset;\n            result.totalIdleIncrease += claimedRewardValue;\n\n            // Recalculate the debt info figuring out the change in\n            // total debt value we can roll up later\n            uint256 currentShareBalance = destVault.balanceOf(address(this));\n\n            AutopoolDebt.IdleDebtUpdates memory debtResult = _recalculateDestInfo(\n                $.destinationInfo[address(destVault)], destVault, currentShareBalance, currentShareBalance\n            );\n\n            result.totalDebtDecrease += debtResult.totalDebtDecrease;\n            result.totalDebtIncrease += debtResult.totalDebtIncrease;\n            result.totalMinDebtDecrease += debtResult.totalMinDebtDecrease;\n            result.totalMinDebtIncrease += debtResult.totalMinDebtIncrease;\n            result.totalMaxDebtDecrease += debtResult.totalMaxDebtDecrease;\n            result.totalMaxDebtIncrease += debtResult.totalMaxDebtIncrease;\n\n            // If we no longer have shares, then there's no reason to continue reporting on the destination.\n            // The strategy will only call for the info if its moving \"out\" of the destination\n            // and that will only happen if we have shares.\n            // A rebalance where we move \"in\" to the position will refresh the data at that time\n            if (currentShareBalance > 0) {\n                $.debtReportQueue.addToTail(address(destVault));\n            }\n\n            claimGasUsed -= gasleft();\n\n            emit DestinationDebtReporting(address(destVault), debtResult, claimedRewardValue, claimGasUsed);\n\n            AutopoolStrategyHooks.executeHooks(\n                hooks,\n                uint256(HookFunctionIndex.onDestinationDebtReport),\n                abi.encodeCall(IStrategyHook.onDestinationDebtReport, (address(destVault), debtResult))\n            );\n        }\n\n        changes.newIdle = changes.startingIdle + result.totalIdleIncrease;\n        changes.newDebt = changes.startingDebt + result.totalDebtIncrease - result.totalDebtDecrease;\n\n        $.assetBreakdown.totalIdle = changes.newIdle;\n        $.assetBreakdown.totalDebt = changes.newDebt;\n        $.assetBreakdown.totalDebtMin =\n            $.assetBreakdown.totalDebtMin + result.totalMinDebtIncrease - result.totalMinDebtDecrease;\n        $.assetBreakdown.totalDebtMax =\n            $.assetBreakdown.totalDebtMax + result.totalMaxDebtIncrease - result.totalMaxDebtDecrease;\n    }\n\n    function _initiateWithdrawInfo(\n        uint256 assets,\n        IAutopool.AssetBreakdown storage assetBreakdown\n    ) private view returns (WithdrawInfo memory) {\n        uint256 idle = assetBreakdown.totalIdle;\n        WithdrawInfo memory info = WithdrawInfo({\n            currentIdle: idle,\n            // If idle can cover the full amount, then we want to pull all assets from there\n            // Otherwise, we want to pull from the market and only get idle if we exhaust the market\n            assetsFromIdle: assets > idle ? 0 : assets,\n            totalAssetsToPull: 0,\n            assetsToPull: 0,\n            assetsPulled: 0,\n            idleIncrease: 0,\n            debtDecrease: 0,\n            debtMinDecrease: 0,\n            debtMaxDecrease: 0,\n            totalMinDebt: assetBreakdown.totalDebtMin,\n            destinationRound: 0,\n            lastRoundSlippage: 0,\n            expectedAssets: 0,\n            remainingRecoup: 0\n        });\n\n        info.totalAssetsToPull = assets - info.assetsFromIdle;\n\n        // This var we use to track our progress later\n        info.assetsToPull = assets - info.assetsFromIdle;\n\n        // Idle + minDebt is the maximum amount of assets/debt we could burn during a withdraw.\n        // If the user is request more than that (like during a withdraw) we can just revert\n        // early without trying\n        if (info.totalAssetsToPull > info.currentIdle + info.totalMinDebt) {\n            revert TooFewAssets(assets, info.currentIdle + info.totalMinDebt);\n        }\n\n        return info;\n    }\n\n    function withdraw(\n        AutopoolState storage $,\n        uint256 assets,\n        uint256 applicableTotalAssets\n    ) public returns (uint256 actualAssets, uint256 actualShares, uint256 debtBurned) {\n        WithdrawInfo memory info = _initiateWithdrawInfo(assets, $.assetBreakdown);\n\n        // Pull the market if there aren't enough funds in idle to cover the entire amount\n\n        // This flow is not bounded by a set number of shares. The user has requested X assets\n        // and a variable number of shares to burn so we don't have easy break out points like we do\n        // during redeem (like using debt burned). When we get slippage here and don't meet the requested assets\n        // we need to keep going if we can. This is tricky if we consider that (most of) our destinations are\n        // LP positions and we'll be swapping assets, so we can expect some slippage. Even\n        // if our minDebtValue numbers are up to date and perfectly accurate slippage could ensure we\n        // are always receiving less than we expect/calculate and we never hit the requested assets\n        // even though the owner would have shares to cover it. Under normal/expected conditions, our\n        // minDebtValue is lower than actual and we expect overall value to be going up, so we burn a tad\n        // more than we should and receive a tad more than we expect. This should cover us. However,\n        // in other conditions we have to be sure we aren't endlessly trying to approach 0 so we are tracking\n        // the slippage we received on the last pull, repricing, and applying an increasing multiplier until we either\n        // pull enough to cover or pull them all and/or move to the next destination.\n\n        uint256 dvSharesToBurn;\n        while (info.assetsToPull > 0) {\n            IDestinationVault destVault = IDestinationVault($.withdrawalQueue.peekHead());\n\n            // We've run out of destinations\n            if (address(destVault) == address(0)) {\n                break;\n            }\n\n            uint256 dvShares = destVault.balanceOf(address(this));\n            {\n                uint256 dvSharesValue;\n                if (info.destinationRound == 0) {\n                    // First time pulling\n\n                    // We use the min debt value here because its a withdrawal and we're trying to cover an amount\n                    // of assets. Undervaluing the shares may mean we pull more but given that we expect slippage\n                    // that is desirable.\n                    dvSharesValue = $.destinationInfo[address(destVault)].cachedMinDebtValue * dvShares\n                        / $.destinationInfo[address(destVault)].ownedShares;\n                } else {\n                    // When we've pulled from this destination before, i.e. destinationRound > 0, then we\n                    // know a more accurate exchange rate and its worse than we were expecting.\n                    // We even will pad it a bit as we want to account for any additional slippage we\n                    // may receive by say being farther down an AMM curve.\n\n                    // dvSharesToBurn is the last value we used when pulling from this destination\n                    // info.expectedAssets is how much we expected to get on that last pull\n                    // info.expectedAssets - info.lastRoundSlippage is how much we actually received\n\n                    uint256 paddedSlippage = info.lastRoundSlippage * (info.destinationRound + 10_000) / 10_000;\n\n                    if (paddedSlippage < info.expectedAssets) {\n                        dvSharesValue = (info.expectedAssets - paddedSlippage) * dvShares / dvSharesToBurn;\n                    } else {\n                        // This will just mean we pull all shares\n                        dvSharesValue = 0;\n                    }\n                }\n\n                if (dvSharesValue > info.assetsToPull) {\n                    dvSharesToBurn = (dvShares * info.assetsToPull) / dvSharesValue;\n\n                    // On withdraw, we are trying to meet a specific number of assets without a limit\n                    // on the debt we can burn. Burning 0 due to the valuations here would be an automatic failure\n                    // as we still have assets to satisfy and debt to burn. We at least have to burn 1 even if it\n                    // results in a larger over pull\n                    if (dvSharesToBurn == 0) {\n                        dvSharesToBurn = 1;\n                    }\n\n                    // Only need to set it here because the only time we'll use it is if\n                    // we don't exhaust all shares and have to try the destination again\n                    info.expectedAssets = info.assetsToPull;\n                } else {\n                    dvSharesToBurn = dvShares;\n                }\n            }\n\n            uint256 pulledAssets;\n            uint256 debtValueBurned;\n            // Get the base asset back from the Destination. Also performs a check that we aren't receiving\n            // poor execution on our swaps based on safe prices\n            (info, pulledAssets, debtValueBurned) = _withdrawAssets(info, $.destinationInfo, destVault, dvSharesToBurn);\n\n            info.assetsPulled += pulledAssets;\n\n            if (info.remainingRecoup > 0) {\n                // If the destination is so severely undervalued that it can't cover its own recoup then we have no\n                // recourse but to burn the entire destination and the user would to have to cover the full overage\n                // from the next destinations can get nothing from this one. Should not be allowed.\n                revert PositivePriceRecoupNotCovered(info.remainingRecoup);\n            }\n\n            // If we've exhausted all shares we can remove the withdrawal from the queue\n            // We need to leave it in the debt report queue though so that our destination specific\n            // debt tracking values can be updated\n            if (dvShares == dvSharesToBurn) {\n                $.withdrawalQueue.popAddress(address(destVault));\n                info.destinationRound = 0;\n                info.lastRoundSlippage = 0;\n            } else {\n                // If we didn't burn all the shares and we received enough to cover our\n                // expected that means we'll break out below as we've hit our target\n                unchecked {\n                    if (pulledAssets < info.expectedAssets) {\n                        info.lastRoundSlippage = info.expectedAssets - pulledAssets;\n                        if (info.destinationRound == 0) {\n                            info.destinationRound = 100;\n                        } else {\n                            info.destinationRound *= 2;\n                        }\n                    }\n                }\n            }\n\n            // It's possible we'll get back more assets than we anticipate from a swap\n            // so if we do, throw it in idle and stop processing. You don't get more than we've calculated\n            if (info.assetsPulled >= info.totalAssetsToPull) {\n                info.idleIncrease += info.assetsPulled - info.totalAssetsToPull;\n                info.assetsPulled = info.totalAssetsToPull;\n                info.assetsToPull = 0;\n                break;\n            }\n\n            info.assetsToPull -= pulledAssets;\n        }\n\n        // We didn't get enough assets from the debt pull\n        // See if we can get the rest from idle\n        if (info.assetsPulled < assets && info.currentIdle > 0) {\n            uint256 remaining = assets - info.assetsPulled;\n            if (remaining <= info.currentIdle) {\n                info.assetsFromIdle = remaining;\n            }\n            // We don't worry about the else case because if currentIdle can't\n            // cover remaining then we'll fail the `actualAssets < assets`\n            // check below and revert\n        }\n\n        debtBurned = info.assetsFromIdle + info.debtMinDecrease;\n        actualAssets = info.assetsFromIdle + info.assetsPulled;\n\n        if (actualAssets < assets) {\n            revert TooFewAssets(assets, actualAssets);\n        }\n\n        actualShares = IAutopool(address(this)).convertToShares(\n            Math.max(actualAssets, debtBurned),\n            applicableTotalAssets,\n            IAutopool(address(this)).totalSupply(),\n            Math.Rounding.Up\n        );\n\n        // Subtract what's taken out of idle from totalIdle\n        // We may also have some increase to account for it we over pulled\n        // or received better execution than we were anticipating\n        // slither-disable-next-line events-maths\n        $.assetBreakdown.totalIdle = info.currentIdle + info.idleIncrease - info.assetsFromIdle;\n\n        // Save off our various debt numbers\n        if (info.debtDecrease > $.assetBreakdown.totalDebt) {\n            $.assetBreakdown.totalDebt = 0;\n        } else {\n            $.assetBreakdown.totalDebt -= info.debtDecrease;\n        }\n\n        if (info.debtMinDecrease > info.totalMinDebt) {\n            $.assetBreakdown.totalDebtMin = 0;\n        } else {\n            $.assetBreakdown.totalDebtMin -= info.debtMinDecrease;\n        }\n\n        if (info.debtMaxDecrease > $.assetBreakdown.totalDebtMax) {\n            $.assetBreakdown.totalDebtMax = 0;\n        } else {\n            $.assetBreakdown.totalDebtMax -= info.debtMaxDecrease;\n        }\n    }\n\n    function _withdrawAssets(\n        WithdrawInfo memory info,\n        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo,\n        IDestinationVault destVault,\n        uint256 dvSharesToBurn\n    ) internal returns (WithdrawInfo memory, uint256 pulledAssets, uint256 debtValueBurned) {\n        if (dvSharesToBurn > 0) {\n            address[] memory tokensBurned;\n            uint256[] memory amountsBurned;\n\n            // Destination Vaults always burn the exact amount we instruct them to\n            (pulledAssets, tokensBurned, amountsBurned) = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));\n\n            // Calculate the totalDebt we'll need to remove based on the shares we're burning\n            // We're rounding up here so take care when actually applying to totalDebt\n            debtValueBurned = destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMinDecrease += debtValueBurned;\n\n            info.debtDecrease += destinationInfo[address(destVault)].cachedDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n\n            uint256 maxDebtBurned = destinationInfo[address(destVault)].cachedMaxDebtValue.mulDiv(\n                dvSharesToBurn, destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n            );\n            info.debtMaxDecrease += maxDebtBurned;\n\n            // See if we received a reasonable amount of the base asset back based on the value\n            // of the tokens that were burned.\n            uint256 totalValueBurned;\n            {\n                uint256 tokenLen = tokensBurned.length;\n                IRootPriceOracle rootPriceOracle = ISystemRegistry(destVault.getSystemRegistry()).rootPriceOracle();\n                for (uint256 i = 0; i < tokenLen;) {\n                    totalValueBurned += amountsBurned[i]\n                        * rootPriceOracle.getPriceInQuote(tokensBurned[i], destVault.baseAsset())\n                        / (10 ** IERC20(tokensBurned[i]).decimals());\n                    unchecked {\n                        ++i;\n                    }\n                }\n            }\n\n            // How much, if any, should be dropping into idle?\n            // Anything pulled over debtValueBurned goes to idle, user can't get more than we think its worth.\n            // However, if we pulled less than the current value of the tokens we burned, so long as\n            // that value is greater than debt min, we need to recoup that as well and put it into idle\n\n            uint256 amountToRecoup;\n            if (totalValueBurned > debtValueBurned) {\n                // The shares we burned are worth more than we'll be recouping from the debt burn\n                // the difference we still need to get\n                amountToRecoup = totalValueBurned - debtValueBurned;\n\n                uint256 maxCreditBps = destVault.recoupMaxCredit();\n                uint256 gapCredit = maxDebtBurned - debtValueBurned;\n                uint256 credit = Math.min(gapCredit, debtValueBurned * maxCreditBps / 10_000);\n\n                if (credit > amountToRecoup) {\n                    amountToRecoup = 0;\n                } else {\n                    amountToRecoup -= credit;\n                }\n            }\n\n            // This is done regardless of whether we were under valued. User can still only\n            // get what we've valued it at.\n            if (pulledAssets > debtValueBurned) {\n                uint256 overDebtValue = pulledAssets - debtValueBurned;\n                info.idleIncrease += overDebtValue;\n                pulledAssets -= overDebtValue;\n\n                // Since this is going to idle it goes to satisfy the recoup as well\n                if (amountToRecoup > 0) {\n                    if (amountToRecoup > overDebtValue) {\n                        amountToRecoup -= overDebtValue;\n                    } else {\n                        amountToRecoup = 0;\n                    }\n                }\n            }\n\n            // If we still have a value we need to recoup it means that the debt range credit\n            // as well as what was pulled over the min debt value wasn't enough to cover\n            // the under valued burn. Now we have to try and take it from what is going back\n            // to the user\n            if (amountToRecoup > 0) {\n                if (amountToRecoup > pulledAssets) {\n                    // Recoup is more than we pulled so we'll have some recoup left over\n                    amountToRecoup -= pulledAssets;\n\n                    // Everything that was pulled goes to idle\n                    info.idleIncrease += pulledAssets;\n                    pulledAssets = 0;\n\n                    // We'll have to try and get the remaining amount from another destination\n                    info.remainingRecoup += amountToRecoup;\n                } else {\n                    // We pulled enough assets to cover the recoup\n                    pulledAssets -= amountToRecoup;\n\n                    // Ensure the recoup goes to idle\n                    info.idleIncrease += amountToRecoup;\n                }\n            }\n        }\n\n        return (info, pulledAssets, debtValueBurned);\n    }\n\n    /// @notice Perform a removal of assets via the redeem path where the shares are the limiting factor.\n    /// This means we break out whenever we reach either `assets` retrieved or debt value equivalent to `assets` burned\n    function redeem(\n        AutopoolState storage $,\n        uint256 assets,\n        uint256 applicableTotalAssets\n    ) public returns (uint256 actualAssets, uint256 actualShares, uint256 debtBurned) {\n        WithdrawInfo memory info = _initiateWithdrawInfo(assets, $.assetBreakdown);\n\n        // If not enough funds in idle, then pull what we need from destinations\n        bool exhaustedDestinations = false;\n        while (info.assetsToPull > 0) {\n            IDestinationVault destVault = IDestinationVault($.withdrawalQueue.peekHead());\n            if (address(destVault) == address(0)) {\n                exhaustedDestinations = true;\n                break;\n            }\n\n            uint256 dvShares = destVault.balanceOf(address(this));\n            uint256 dvSharesToBurn = dvShares;\n            {\n                // Valuing these shares higher, rounding up, will result in us burning less of them\n                // in the event we don't burn all of them. Good thing.\n                uint256 dvSharesValue = $.destinationInfo[address(destVault)].cachedMinDebtValue.mulDiv(\n                    dvSharesToBurn, $.destinationInfo[address(destVault)].ownedShares, Math.Rounding.Up\n                );\n\n                // If the dv shares we own are worth more than we need, limit the shares to burn\n                // Any extra we get will be dropped into idle\n                if (dvSharesValue > info.assetsToPull) {\n                    uint256 limitedShares = (dvSharesToBurn * info.assetsToPull) / dvSharesValue;\n\n                    // Final set for the actual shares we'll burn later\n                    dvSharesToBurn = limitedShares;\n                }\n            }\n\n            uint256 pulledAssets;\n            uint256 debtValueBurned;\n            // Get the base asset back from the Destination. Also performs a check that we aren't receiving\n            // poor execution on our swaps based on safe prices\n            // slither-disable-next-line unused-return\n            (info, pulledAssets, debtValueBurned) = _withdrawAssets(info, $.destinationInfo, destVault, dvSharesToBurn);\n\n            // If we've exhausted all shares we can remove the destination from the withdrawal queue\n            // We need to leave it in the debt report queue though so that our destination specific\n            // debt tracking values can be updated\n            if (dvShares == dvSharesToBurn) {\n                $.withdrawalQueue.popAddress(address(destVault));\n            }\n\n            info.assetsPulled += pulledAssets;\n\n            // Any deficiency in the amount we received is slippage.\n            // There is a round up on debtValueBurned so just making sure it never under flows here\n            // _withdrawAssets ensures that pulledAssets is always lte debtValueBurned and we always\n            // want to debit the max so we just use debtValueBurned\n            if (debtValueBurned > info.assetsToPull) {\n                info.assetsToPull = 0;\n            } else {\n                info.assetsToPull -= debtValueBurned;\n            }\n\n            // We either have enough assets, or we've burned the max debt we're allowed\n            if (info.assetsToPull == 0) {\n                break;\n            }\n\n            // If we didn't exhaust all of the shares from the destination it means we\n            // assume we will get everything we need from there and everything else is slippage\n            if (dvShares != dvSharesToBurn) {\n                info.assetsToPull = 0;\n                break;\n            }\n        }\n\n        // See if we can pull the remaining recoup from other destinations we may have pulled from\n        if (info.remainingRecoup > 0) {\n            if (info.remainingRecoup > info.assetsPulled) {\n                info.remainingRecoup -= info.assetsPulled;\n                info.idleIncrease += info.assetsPulled;\n                info.assetsPulled = 0;\n            } else {\n                info.assetsPulled -= info.remainingRecoup;\n                info.idleIncrease += info.remainingRecoup;\n                info.remainingRecoup = 0;\n            }\n        }\n\n        // We didn't get enough assets from the debt pull\n        // See if we can get the rest from idle\n        if (info.assetsToPull > 0 && info.currentIdle > 0 && exhaustedDestinations) {\n            if (info.assetsToPull < info.currentIdle) {\n                info.assetsFromIdle = info.assetsToPull;\n            } else {\n                info.assetsFromIdle = info.currentIdle;\n            }\n        }\n\n        debtBurned = info.assetsFromIdle + info.debtMinDecrease;\n        actualAssets = info.assetsFromIdle + info.assetsPulled;\n\n        // If we took from idle, and we have remaining assets to recoup\n        // we need to put some back in idle\n        if (info.remainingRecoup > 0 && info.assetsFromIdle > 0) {\n            // We only need to do this if the idle assets can cover the remaining recoup fully because\n            // we'll be reverting otherwise\n            if (info.assetsFromIdle >= info.remainingRecoup) {\n                // We still need to charge for the recoup so we're going to leave it in debtBurned\n                // but we'll take it back out of actualAssets so it stays in idle. We need to lower\n                // assetsFromIdle as well so that the final numbers get updated too\n                actualAssets -= info.remainingRecoup;\n                info.assetsFromIdle -= info.remainingRecoup;\n                info.remainingRecoup = 0;\n            } else {\n                // Just updating this number so we get an accurate value in the revert below\n                info.remainingRecoup -= info.assetsFromIdle;\n            }\n        }\n\n        // We took everything we could and still can't cover, time to revert\n        if (info.remainingRecoup > 0) {\n            revert PositivePriceRecoupNotCovered(info.remainingRecoup);\n        }\n\n        actualShares = IAutopool(address(this)).convertToShares(\n            debtBurned, applicableTotalAssets, IAutopool(address(this)).totalSupply(), Math.Rounding.Up\n        );\n\n        // Subtract what's taken out of idle from totalIdle\n        // We may also have some increase to account for it we over pulled\n        // or received better execution than we were anticipating\n        // slither-disable-next-line events-maths\n        $.assetBreakdown.totalIdle = info.currentIdle + info.idleIncrease - info.assetsFromIdle;\n\n        // Save off our various debt numbers\n        if (info.debtDecrease > $.assetBreakdown.totalDebt) {\n            $.assetBreakdown.totalDebt = 0;\n        } else {\n            $.assetBreakdown.totalDebt -= info.debtDecrease;\n        }\n\n        if (info.debtMinDecrease > info.totalMinDebt) {\n            $.assetBreakdown.totalDebtMin = 0;\n        } else {\n            $.assetBreakdown.totalDebtMin -= info.debtMinDecrease;\n        }\n\n        if (info.debtMaxDecrease > $.assetBreakdown.totalDebtMax) {\n            $.assetBreakdown.totalDebtMax = 0;\n        } else {\n            $.assetBreakdown.totalDebtMax -= info.debtMaxDecrease;\n        }\n    }\n\n    /**\n     * @notice Function to complete a withdrawal or redeem.  This runs after shares to be burned and assets to be\n     *    transferred are calculated.\n     * @param $ Storage related to the calling Autopool\n     * @param assets Amount of assets to be transferred to receiver.\n     * @param shares Amount of shares to be burned from owner.\n     * @param owner Owner of shares, user to burn shares from.\n     * @param receiver The receiver of the baseAsset.\n     * @param baseAsset Base asset of the Autopool.\n     */\n    function completeWithdrawal(\n        AutopoolState storage $,\n        uint256 assets,\n        uint256 shares,\n        address owner,\n        address receiver,\n        IERC20 baseAsset\n    ) external {\n        if (msg.sender != owner) {\n            uint256 allowed = IAutopool(address(this)).allowance(owner, msg.sender);\n            if (allowed != type(uint256).max) {\n                if (shares > allowed) revert AmountExceedsAllowance(shares, allowed);\n\n                unchecked {\n                    $.token.approve(owner, msg.sender, allowed - shares);\n                }\n            }\n        }\n\n        $.token.burn(owner, shares);\n\n        uint256 ts = IAutopool(address(this)).totalSupply();\n\n        emit Withdraw(msg.sender, receiver, owner, assets, shares);\n\n        emit Nav($.assetBreakdown.totalIdle, $.assetBreakdown.totalDebt, ts);\n\n        baseAsset.safeTransfer(receiver, assets);\n    }\n\n    /**\n     * @notice A helper function to get estimates of what would happen on a withdraw or redeem.\n     * @dev Reverts all changing state.\n     * @param $ Storage related to the calling Autopool.\n     * @param previewWithdraw Bool denoting whether to preview a redeem or withdrawal.\n     * @param assets Assets to be withdrawn or redeemed.\n     * @param applicableTotalAssets Operation dependent assets in the Autopool.\n     * @param functionCallEncoded Abi encoded function signature for recursive call.\n     * @return assetsAmount Preview of amount of assets to send to receiver.\n     * @return sharesAmount Preview of amount of assets to burn from owner.\n     */\n    function preview(\n        AutopoolState storage $,\n        bool previewWithdraw,\n        uint256 assets,\n        uint256 applicableTotalAssets,\n        bytes memory functionCallEncoded\n    ) external returns (uint256 assetsAmount, uint256 sharesAmount) {\n        if (msg.sender != address(this)) {\n            // Perform a recursive call the function in `funcCallEncoded`.  This will result in a call back to\n            // the Autopool, and then this function. The intention is to reach the \"else\" block in this function.\n            // solhint-disable avoid-low-level-calls\n            // slither-disable-next-line missing-zero-check,low-level-calls\n            (bool success, bytes memory returnData) = address(this).call(functionCallEncoded);\n            // solhint-enable avoid-low-level-calls\n\n            // If the recursive call is successful, it means an unintended code path was taken.\n            if (success) {\n                revert Errors.UnreachableError();\n            }\n\n            bytes4 sharesAmountSig = bytes4(keccak256(\"SharesAndAssetsReceived(uint256,uint256)\"));\n\n            // Extract the error signature (first 4 bytes) from the revert reason.\n            bytes4 errorSignature;\n            // solhint-disable no-inline-assembly\n            assembly {\n                errorSignature := mload(add(returnData, 0x20))\n            }\n\n            // If the error matches the expected signature, extract the amount from the revert reason and return.\n            if (errorSignature == sharesAmountSig) {\n                // Extract subsequent bytes for uint256.\n                assembly {\n                    assetsAmount := mload(add(returnData, 0x24))\n                    sharesAmount := mload(add(returnData, 0x44))\n                }\n            } else {\n                // If the error is not the expected one, forward the original revert reason.\n                assembly {\n                    revert(add(32, returnData), mload(returnData))\n                }\n            }\n            // solhint-enable no-inline-assembly\n        }\n        // This branch is taken during the recursive call.\n        else {\n            // Perform the actual withdrawal or redeem logic to compute the amount. This will be reverted to\n            // simulate the action.\n            uint256 previewAssets;\n            uint256 previewShares;\n            if (previewWithdraw) {\n                (previewAssets, previewShares,) = withdraw($, assets, applicableTotalAssets);\n            } else {\n                (previewAssets, previewShares,) = redeem($, assets, applicableTotalAssets);\n            }\n\n            // Revert with the computed amount as an error.\n            revert SharesAndAssetsReceived(previewAssets, previewShares);\n        }\n    }\n}\n","Filename":"src/vault/libs/AutopoolDebt.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        address item\n    ) external;\n\n    /// @notice Removes item registration\n    /// @param item Item address to be removed\n    function unregister(\n        address item\n    ) 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(\n        address item\n    ) 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(\n        address item\n    ) external view;\n}\n","Filename":"src/interfaces/liquidation/IAsyncSwapperRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { IDestinationVault } from \"src/interfaces/vault/IDestinationVault.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IDestinationVaultRegistry } from \"src/interfaces/vault/IDestinationVaultRegistry.sol\";\nimport { AutopoolState } from \"src/vault/libs/AutopoolState.sol\";\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\n\nlibrary AutopoolDestinations {\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using WithdrawalQueue for StructuredLinkedList.List;\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 TooManyDeployedDestinations();\n    error BaseAssetMismatch(address destinationVault);\n\n    /// @notice Maximum amount of destinations we can be deployed to a given time\n    uint256 public constant MAX_DEPLOYED_DESTINATIONS = 50;\n\n    /// @notice Remove, or queue to remove if necessary, destinations from the vault\n    /// @dev No need to handle withdrawal queue as it will be popped once it hits balance 0 in withdraw or rebalance.\n    /// Debt report queue is handled the same way\n    /// @param $ Storage data of the calling Autopool\n    /// @param _destinations Destinations to remove\n    function removeDestinations(AutopoolState storage $, address[] calldata _destinations) external {\n        for (uint256 i = 0; i < _destinations.length; ++i) {\n            address dAddress = _destinations[i];\n            IDestinationVault destination = IDestinationVault(dAddress);\n\n            // remove from main list (NOTE: done here so balance check below doesn't explode if address is invalid)\n            if (!$.destinations.remove(dAddress)) {\n                revert Errors.ItemNotFound();\n            }\n\n            if (destination.balanceOf(address(this)) > 0 && !$.removalQueue.contains(dAddress)) {\n                // we still have funds in it! move it to removalQueue for rebalancer to handle it later\n                // slither-disable-next-line unused-return\n                $.removalQueue.add(dAddress);\n\n                emit AddedToRemovalQueue(dAddress);\n            }\n\n            emit DestinationVaultRemoved(dAddress);\n        }\n    }\n\n    /// @notice Add a destination to the vault\n    /// @dev No need to add to debtReport and withdrawal queue from the vault as the rebalance will take care of that\n    /// @param $ Storage data of the calling Autopool\n    /// @param destinations New destinations to add\n    /// @param systemRegistry System registry reference for the vault\n    function addDestinations(\n        AutopoolState storage $,\n        address[] calldata destinations,\n        ISystemRegistry systemRegistry\n    ) external {\n        IDestinationVaultRegistry destinationRegistry = systemRegistry.destinationVaultRegistry();\n\n        uint256 numDestinations = destinations.length;\n        if (numDestinations == 0) {\n            revert Errors.InvalidParams();\n        }\n\n        address autopoolAsset = IAutopool(address(this)).asset();\n\n        address dAddress;\n        for (uint256 i = 0; i < numDestinations; ++i) {\n            dAddress = destinations[i];\n\n            // Address must be setup in our registry\n            if (dAddress == address(0) || !destinationRegistry.isRegistered(dAddress)) {\n                revert Errors.InvalidAddress(dAddress);\n            }\n\n            // Don't allow duplicates\n            if (!$.destinations.add(dAddress)) {\n                revert Errors.ItemExists();\n            }\n\n            address dvBaseAsset = IDestinationVault(dAddress).baseAsset();\n\n            if (dvBaseAsset != autopoolAsset) {\n                revert BaseAssetMismatch(dAddress);\n            }\n\n            // A destination could be queued for removal but we decided\n            // to keep it\n            // slither-disable-next-line unused-return\n            $.removalQueue.remove(dAddress);\n\n            emit DestinationVaultAdded(dAddress);\n        }\n    }\n\n    /// @notice Ensure a destination is in the queues it should be after a rebalance or debt report\n    /// @param destination The destination to manage\n    /// @param destinationIn Whether the destination was moved into, true, or out of, false.\n    function manageQueuesForDestination(AutopoolState storage $, address destination, bool destinationIn) external {\n        // The vault itself, when we are moving idle around, should never be\n        // in the queues.\n        if (destination != address(this)) {\n            // If we have a balance, we need to continue to report on it\n            if (IDestinationVault(destination).balanceOf(address(this)) > 0) {\n                // For debt reporting, we just updated the values so we can put\n                // it at the end of the queue.\n                $.debtReportQueue.addToTail(destination);\n\n                // Debt reporting queue is a proxy for destinations we are deployed to\n                // Easiest to check after doing the add as \"addToTail\" can move\n                // the destination when it already exists. Also, we run this fn for the \"out\"\n                // destination first so we're sure to free the spots we can\n                if ($.debtReportQueue.sizeOf() > MAX_DEPLOYED_DESTINATIONS) {\n                    revert TooManyDeployedDestinations();\n                }\n\n                // For withdraws, if we moved into the position then we want to put it\n                // at the end of the queue so we don't exit from our higher projected\n                // apr positions first. If we exited, that means its a lower apr\n                // and we want to continue to exit via user withdrawals so put it at the top\n                if (destinationIn) {\n                    $.withdrawalQueue.addToTail(destination);\n                } else {\n                    $.withdrawalQueue.addToHead(destination);\n                }\n            } else {\n                // If we no longer have a balance we don't need to continue to report\n                // on it and we also have nothing to withdraw from it\n\n                // We don't remove the destination from the debt queue here as it\n                // still might have unclaimed rewards.\n                $.withdrawalQueue.popAddress(destination);\n\n                if ($.removalQueue.remove(destination)) {\n                    emit RemovedFromRemovalQueue(destination);\n                }\n            }\n        }\n    }\n}\n","Filename":"src/vault/libs/AutopoolDestinations.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\ninterface ISystemSecurity {\n    /// @notice Get the number of NAV/share operations currently in progress\n    /// @return Number of operations\n    function navOpsInProgress() external view returns (uint256);\n\n    /// @notice Called at the start of any NAV/share changing operation\n    function enterNavOperation() external;\n\n    /// @notice Called at the end of any NAV/share changing operation\n    function exitNavOperation() external;\n\n    /// @notice Whether or not the system as a whole is paused\n    function isSystemPaused() external returns (bool);\n\n    /// @notice Sets an autopool in transient storage , this is used to guard against malicious user payloads\n    /// that could be used to reenter the system\n    /// @param autopool The address of the autopool to set\n    /// @dev This is used in the AutopilotRouter to guard against reentrancy via malicious payload in the\n    /// swap routes when redeeming\n    function setAllowedAutopool(\n        address autopool\n    ) external;\n\n    /// @notice Clears the autopool from transient storage\n    function clearAllowedAutopool() external;\n}\n","Filename":"src/interfaces/security/ISystemSecurity.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IBaseRewarder } from \"src/interfaces/rewarders/IBaseRewarder.sol\";\nimport { IExtraRewarder } from \"src/interfaces/rewarders/IExtraRewarder.sol\";\n\ninterface IMainRewarder is IBaseRewarder {\n    error ExtraRewardsNotAllowed();\n    error MaxExtraRewardsReached();\n\n    /// @notice Extra rewards can be added, but not removed, ref: https://github.com/Tokemak/v2-core/issues/659\n    event ExtraRewardAdded(address reward);\n\n    /**\n     * @notice Adds an ExtraRewarder contract address to the extraRewards array.\n     * @param reward The address of the ExtraRewarder contract.\n     */\n    function addExtraReward(\n        address reward\n    ) external;\n\n    /**\n     * @notice Withdraws the specified amount of tokens from the vault for the specified account, and transfers all\n     * rewards for the account from this contract and any linked extra reward contracts.\n     * @param account The address of the account to withdraw tokens and claim rewards for.\n     * @param amount The amount of tokens to withdraw.\n     * @param claim If true, claims all rewards for the account from this contract and any linked extra reward\n     * contracts.\n     */\n    function withdraw(address account, uint256 amount, bool claim) external;\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account from this contract and any linked extra reward\n     * contracts.\n     * @dev If claimExtras is true, also claims all rewards from linked extra reward contracts.\n     * @param account The address of the account to claim rewards for.\n     * @param recipient The address to send the rewards to.\n     * @param claimExtras If true, claims rewards from linked extra reward contracts.\n     */\n    function getReward(address account, address recipient, bool claimExtras) external;\n\n    /**\n     * @notice Number of extra rewards currently registered\n     */\n    function extraRewardsLength() external view returns (uint256);\n\n    /**\n     * @notice Get the extra rewards array values\n     */\n    function extraRewards() external view returns (address[] memory);\n\n    /**\n     * @notice Get the rewarder at the specified index\n     */\n    function getExtraRewarder(\n        uint256 index\n    ) external view returns (IExtraRewarder);\n}\n","Filename":"src/interfaces/rewarders/IMainRewarder.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nimport { ECDSA } from \"openzeppelin-contracts/utils/cryptography/ECDSA.sol\";\nimport { IERC20Permit } from \"openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\n\n/// @notice ERC20 token functionality converted into a library. Based on OZ's v5\n/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.1/contracts/token/ERC20/ERC20.sol\nlibrary AutopoolToken {\n    struct TokenData {\n        /// @notice Token balances\n        /// @dev account => balance\n        mapping(address => uint256) balances;\n        /// @notice Account spender allowances\n        /// @dev account => spender => allowance\n        mapping(address => mapping(address => uint256)) allowances;\n        /// @notice Total supply of the pool. Be careful when using this directly from the struct. The pool itself\n        /// modifies this number based on unlocked profited shares\n        uint256 totalSupply;\n        /// @notice ERC20 Permit nonces\n        /// @dev account -> nonce. Exposed via `nonces(owner)`\n        mapping(address => uint256) nonces;\n    }\n\n    /// @notice EIP2612 permit type hash\n    bytes32 public constant PERMIT_TYPEHASH =\n        keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n\n    /// @notice EIP712 domain type hash\n    bytes32 public constant TYPE_HASH =\n        keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n\n    /// @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    /// @param balance Current balance for the interacting account.\n    /// @param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n\n    /// @dev Indicates a failure with the token `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    error ERC20InvalidSender(address sender);\n\n    /// @dev Indicates a failure with the token `receiver`. Used in transfers.\n    /// @param receiver Address to which tokens are being transferred.\n    error ERC20InvalidReceiver(address receiver);\n\n    /// @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.\n    ///@param spender Address that may be allowed to operate on tokens without being their owner.\n    /// @param allowance Amount of tokens a `spender` is allowed to operate with.\n    ///@param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n    /// @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n    /// @param approver Address initiating an approval operation.\n    error ERC20InvalidApprover(address approver);\n\n    /// @dev Indicates a failure with the `spender` to be approved. Used in approvals.\n    /// @param spender Address that may be allowed to operate on tokens without being their owner.\n    error ERC20InvalidSpender(address spender);\n\n    /// @dev Permit deadline has expired.\n    error ERC2612ExpiredSignature(uint256 deadline);\n    /// @dev Mismatched signature.\n    error ERC2612InvalidSigner(address signer, address owner);\n    /// @dev The nonce used for an `account` is not the expected current nonce.\n    error InvalidAccountNonce(address account, uint256 currentNonce);\n\n    /// @dev Emitted when `value` tokens are moved from one account `from` to another `to`.\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /// @dev Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}.\n    /// `value` is the new allowance.\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /// @dev Sets a `value` amount of tokens as the allowance of `spender` over the caller's tokens.\n    function approve(TokenData storage data, address spender, uint256 value) external returns (bool) {\n        address owner = msg.sender;\n        approve(data, owner, spender, value);\n        return true;\n    }\n\n    /// @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.\n    function approve(TokenData storage data, address owner, address spender, uint256 value) public {\n        _approve(data, owner, spender, value, true);\n    }\n\n    function transfer(TokenData storage data, address to, uint256 value) external returns (bool) {\n        address owner = msg.sender;\n        _transfer(data, owner, to, value);\n        return true;\n    }\n\n    /// @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism.\n    /// value` is then deducted from the caller's allowance.\n    function transferFrom(TokenData storage data, address from, address to, uint256 value) external returns (bool) {\n        address spender = msg.sender;\n        _spendAllowance(data, from, spender, value);\n        _transfer(data, from, to, value);\n        return true;\n    }\n\n    /// @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\n    function mint(TokenData storage data, address account, uint256 value) external {\n        if (account == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(data, address(0), account, value);\n    }\n\n    /// @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\n    function burn(TokenData storage data, address account, uint256 value) external {\n        if (account == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        _update(data, account, address(0), value);\n    }\n\n    function permit(\n        TokenData storage data,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        if (block.timestamp > deadline) {\n            revert ERC2612ExpiredSignature(deadline);\n        }\n\n        uint256 nonce;\n        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be\n        // decremented or reset. This guarantees that the nonce never overflows.\n        unchecked {\n            // It is important to do x++ and not ++x here. Nonces starts at 0\n            nonce = data.nonces[owner]++;\n        }\n\n        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonce, deadline));\n\n        bytes32 hash = ECDSA.toTypedDataHash(IERC20Permit(address(this)).DOMAIN_SEPARATOR(), structHash);\n\n        address signer = ECDSA.recover(hash, v, r, s);\n        if (signer != owner) {\n            revert ERC2612InvalidSigner(signer, owner);\n        }\n\n        approve(data, owner, spender, value);\n    }\n\n    /// @dev Moves a `value` amount of tokens from `from` to `to`.\n    function _transfer(TokenData storage data, address from, address to, uint256 value) private {\n        if (from == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        if (to == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(data, from, to, value);\n    }\n\n    /// @dev Updates `owner` s allowance for `spender` based on spent `value`.\n    function _spendAllowance(TokenData storage data, address owner, address spender, uint256 value) private {\n        uint256 currentAllowance = data.allowances[owner][spender];\n        if (currentAllowance != type(uint256).max) {\n            if (currentAllowance < value) {\n                revert ERC20InsufficientAllowance(spender, currentAllowance, value);\n            }\n            unchecked {\n                _approve(data, owner, spender, currentAllowance - value, false);\n            }\n        }\n    }\n\n    /// @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\n    /// (or `to`) is the zero address.\n    function _update(TokenData storage data, address from, address to, uint256 value) private {\n        if (from == address(0)) {\n            // Overflow check required: The rest of the code assumes that totalSupply never overflows\n            data.totalSupply += value;\n        } else {\n            uint256 fromBalance = data.balances[from];\n            if (fromBalance < value) {\n                revert ERC20InsufficientBalance(from, fromBalance, value);\n            }\n            unchecked {\n                // Overflow not possible: value <= fromBalance <= totalSupply.\n                data.balances[from] = fromBalance - value;\n            }\n        }\n\n        if (to == address(0)) {\n            unchecked {\n                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\n                data.totalSupply -= value;\n            }\n        } else {\n            unchecked {\n                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\n                data.balances[to] += value;\n            }\n        }\n\n        emit Transfer(from, to, value);\n    }\n\n    /// @dev Variant of `_approve` with an optional flag to enable or disable the Approval event.\n    function _approve(TokenData storage data, address owner, address spender, uint256 value, bool emitEvent) private {\n        if (owner == address(0)) {\n            revert ERC20InvalidApprover(address(0));\n        }\n        if (spender == address(0)) {\n            revert ERC20InvalidSpender(address(0));\n        }\n        data.allowances[owner][spender] = value;\n        if (emitEvent) {\n            emit Approval(owner, spender, value);\n        }\n    }\n}\n","Filename":"src/vault/libs/AutopoolToken.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\n\ncontract SecurityBase {\n    IAccessController public immutable accessController;\n\n    error UndefinedAddress();\n\n    constructor(\n        address _accessController\n    ) {\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(\n        bytes32 role\n    ) {\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","Filename":"src/security/SecurityBase.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        address destinationVault\n    ) 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(\n        address newDestinationVault\n    ) 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(\n        address destinationVault\n    ) 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","Filename":"src/interfaces/vault/IDestinationVaultRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24; // 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(\n        StructuredLinkedList.List storage queue\n    ) public view returns (address) {\n        return _uintToAddress(StructuredLinkedList.getHead(queue));\n    }\n\n    /// @notice Returns the current tail.\n    function peekTail(\n        StructuredLinkedList.List storage queue\n    ) 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(\n        StructuredLinkedList.List storage queue\n    ) 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(\n        StructuredLinkedList.List storage self\n    ) 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(\n        StructuredLinkedList.List storage queue\n    ) 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(\n        StructuredLinkedList.List storage queue\n    ) 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(\n        address addr\n    ) private pure returns (uint256) {\n        return uint256(uint160(addr));\n    }\n\n    function _uintToAddress(\n        uint256 x\n    ) private pure returns (address) {\n        return address(uint160(x));\n    }\n}\n","Filename":"src/strategy/WithdrawalQueue.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\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(\n        uint256 amount\n    ) external;\n}\n","Filename":"src/interfaces/utils/IWETH9.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\ninterface IBaseRewarder {\n    error RecoverDurationPending();\n\n    event RewardAdded(\n        uint256 reward,\n        uint256 rewardRate,\n        uint256 lastUpdateBlock,\n        uint256 periodInBlockFinish,\n        uint256 historicalRewards\n    );\n    event UserRewardUpdated(\n        address indexed user, uint256 amount, uint256 rewardPerTokenStored, uint256 lastUpdateBlock\n    );\n    event Staked(address indexed user, uint256 amount);\n    event Withdrawn(address indexed user, uint256 amount);\n    event RewardPaid(address indexed user, address indexed recipient, uint256 reward);\n    event QueuedRewardsUpdated(uint256 startingQueuedRewards, uint256 startingNewRewards, uint256 queuedRewards);\n    event AddedToWhitelist(address indexed wallet);\n    event RemovedFromWhitelist(address indexed wallet);\n\n    event TokeLockDurationUpdated(uint256 newDuration);\n\n    event Recovered(address token, address recipient, uint256 amount);\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account\n     */\n    function getReward() external;\n\n    /**\n     * @notice Stakes the specified amount of tokens for the specified account.\n     * @param account The address of the account to stake tokens for.\n     * @param amount The amount of tokens to stake.\n     */\n    function stake(address account, uint256 amount) external;\n\n    /**\n     * @notice Calculate the earned rewards for an account.\n     * @param account Address of the account.\n     * @return The earned rewards for the given account.\n     */\n    function earned(\n        address account\n    ) 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(\n        address account\n    ) 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(\n        uint256 newRewards\n    ) 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(\n        address wallet\n    ) 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(\n        address wallet\n    ) external;\n\n    /**\n     * @notice Recovers tokens from the rewarder. However, a recovery duration of 1 year is applicable for reward token\n     * @param token Address of token\n     * @param recipient recipient Address of recipient\n     */\n    function recover(address token, address recipient) external;\n\n    /**\n     * @notice Check if an address is whitelisted.\n     * @param wallet The address to be checked.\n     * @return bool indicating if the address is whitelisted.\n     */\n    function isWhitelisted(\n        address wallet\n    ) external view returns (bool);\n}\n","Filename":"src/interfaces/rewarders/IBaseRewarder.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        bytes32 aprId\n    ) external view returns (IStatsCalculator calculator);\n\n    /// @notice List all calculator addresses registered\n    function listCalculators() external view returns (bytes32[] memory, address[] memory);\n\n    /// @notice Register a new stats calculator\n    /// @param calculator address of the calculator\n    function register(\n        address calculator\n    ) external;\n\n    /// @notice Remove a stats calculator\n    /// @param aprId key of the calculator to remove\n    function removeCalculator(\n        bytes32 aprId\n    ) external;\n\n    /// @notice Set the factory that can register calculators\n    /// @param factory address of the factory\n    function setCalculatorFactory(\n        address factory\n    ) external;\n}\n","Filename":"src/interfaces/stats/IStatsCalculatorRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IBaseRewarder } from \"src/interfaces/rewarders/IBaseRewarder.sol\";\n\ninterface IExtraRewarder is IBaseRewarder {\n    /**\n     * @notice Withdraws the specified amount of tokens from the vault for the specified account.\n     * @param account The address of the account to withdraw tokens for.\n     * @param amount The amount of tokens to withdraw.\n     */\n    function withdraw(address account, uint256 amount) external;\n\n    /**\n     * @notice Claims and transfers all rewards for the specified account from this contract.\n     * @param account The address of the account to claim rewards for.\n     * @param recipient The address to send the rewards to.\n     */\n    function getReward(address account, address recipient) external;\n}\n","Filename":"src/interfaces/rewarders/IExtraRewarder.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { ISyncSwapper } from \"src/interfaces/swapper/ISyncSwapper.sol\";\n\ninterface ISwapRouter {\n    struct SwapData {\n        address token;\n        address pool;\n        ISyncSwapper swapper;\n        bytes data;\n    }\n\n    error MaxSlippageExceeded();\n    error SwapRouteLookupFailed(address from, address to);\n    error SwapFailed();\n\n    event SwapRouteSet(address indexed token, SwapData[] routes);\n    event SwapForQuoteSuccessful(\n        address indexed assetToken,\n        uint256 sellAmount,\n        address indexed quoteToken,\n        uint256 minBuyAmount,\n        uint256 buyAmount\n    );\n\n    /**\n     * @notice Sets a new swap route for a given asset token.\n     * @param assetToken The asset token for which the swap route is being set.\n     * @param _swapRoute The new swap route as an array of SwapData. The last element represents the quoteToken.\n     * @dev Each 'hop' in the swap route is validated using the respective swapper's validate function. The validate\n     * function ensures that the encoded data contains the correct 'fromAddress' and 'toAddress' (swapData.token), and\n     * verifies that these tokens are in the pool.\n     */\n    function setSwapRoute(address assetToken, SwapData[] calldata _swapRoute) external;\n\n    /**\n     * @notice Swaps the asset token for the quote token.\n     * @dev We're adopting an \"exact in, variable out\" model for all our swaps. This ensures that the entire sellAmount\n     * is used, eliminating the need for additional balance checks and refunds. This model is expected to be followed by\n     * all swapper implementations to maintain consistency and to optimize for gas efficiency.\n     * @param assetToken The address of the asset token to swap.\n     * @param sellAmount The exact amount of the asset token to swap.\n     * @param quoteToken The address of the quote token.\n     * @param minBuyAmount The minimum amount of the quote token expected to be received from the swap.\n     * @return The amount received from the swap.\n     */\n    function swapForQuote(\n        address assetToken,\n        uint256 sellAmount,\n        address quoteToken,\n        uint256 minBuyAmount\n    ) external returns (uint256);\n}\n","Filename":"src/interfaces/swapper/ISwapRouter.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        address user\n    ) external view returns (Lockup[] memory);\n    function toke() external view returns (IERC20Metadata);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Errors\n    ///////////////////////////////////////////////////////////////////\n\n    error ZeroAddress();\n    error StakingDurationTooShort();\n    error StakingDurationTooLong();\n    error StakingPointsExceeded();\n    error IncorrectStakingAmount();\n    error InsufficientFunds();\n    error LockupDoesNotExist();\n    error NotUnlockableYet();\n    error AlreadyUnlocked();\n    error ExtendDurationTooShort();\n    error TransfersDisabled();\n    error TransferFailed();\n    error NoRewardsToClaim();\n    error InsufficientAmount();\n    error InvalidLockupIds();\n    error InvalidDurationLength();\n    error InvalidMinStakeDuration();\n    error AdminUnlockActive();\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, address indexed recipient, uint256 amount);\n    event AdminUnlockSet(bool newUnlockState);\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(\n        uint256[] memory lockupIds\n    ) 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     * @param user address of the user to unstake for\n     * @param to address to receive the unstaked TOKE\n     */\n    function unstake(uint256[] memory lockupIds, address user, address to) 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(\n        address user\n    ) external returns (uint256);\n\n    /**\n     * @notice Calculate points based on duration from the staking system's start epoch to the user's staking end date\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(\n        address user\n    ) external view returns (uint256);\n\n    /// @notice Claim rewards for the caller\n    function collectRewards() external returns (uint256);\n\n    /// @notice Claim rewards for the user and send to recipient\n    function collectRewards(address user, address recipient) external returns (uint256);\n\n    /// @notice Check if amount can be staked\n    function isStakeableAmount(\n        uint256 amount\n    ) external pure returns (bool);\n}\n","Filename":"src/interfaces/staking/IAccToke.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { AutopoolFees } from \"src/vault/libs/AutopoolFees.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { AutopoolState, AutopoolStorage } from \"src/vault/libs/AutopoolState.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\n\nlibrary Autopool4626 {\n    using Math for uint256;\n    using SafeERC20 for IERC20Metadata;\n    using WithdrawalQueue for StructuredLinkedList.List;\n    using AutopoolToken for AutopoolToken.TokenData;\n    using AutopoolFees for AutopoolState;\n    using AutopoolDebt for AutopoolState;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    /// =====================================================\n    /// Errors\n    /// =====================================================\n\n    error InvalidTotalAssetPurpose();\n    error InvalidShutdownStatus(IAutopool.VaultShutdownStatus status);\n    error RecoveryFailed();\n\n    /// =====================================================\n    /// Events\n    /// =====================================================\n\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n    event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    event TokensRecovered(address[] tokens, uint256[] amounts, address[] destinations);\n    event Shutdown(IAutopool.VaultShutdownStatus reason);\n    event RewarderSet(address newRewarder, address oldRewarder);\n    event SymbolAndDescSet(string symbol, string desc);\n\n    /// @notice Mints Vault shares to receiver by depositing exactly amount of underlying tokens\n    /// @dev No nav/share changing operations, debt reportings or rebalances,\n    /// can be happening throughout the entire system\n    function deposit(\n        AutopoolState storage $,\n        address baseAsset,\n        uint256 assets,\n        address receiver,\n        bool paused,\n        uint256 _baseAssetDecimals\n    ) external returns (uint256 shares) {\n        Errors.verifyNotZero(assets, \"assets\");\n\n        uint256 ta = $.totalAssetsTimeChecked(IAutopool.TotalAssetPurpose.Deposit);\n\n        // Handles the vault being paused, returns 0\n        uint256 maxDepositAmount = maxDeposit($, receiver, paused, _baseAssetDecimals);\n        if (assets > maxDepositAmount) {\n            revert IAutopool.ERC4626DepositExceedsMax(assets, maxDepositAmount);\n        }\n\n        // Factor in base Asset decimals\n        shares = convertToShares(assets, ta, totalSupply(), Math.Rounding.Down, _baseAssetDecimals);\n        Errors.verifyNotZero(shares, \"shares\");\n\n        transferAndMint($, IERC20Metadata(baseAsset), assets, shares, receiver);\n    }\n\n    /// @notice Returns the amount of tokens owned by account.\n    /// @dev Subtracts any unlocked profit shares that will be burned when account is the Vault itself\n    function balanceOf(AutopoolState storage $, address account) public view returns (uint256) {\n        if (account == address(this)) {\n            return $.token.balances[account] - $.unlockedShares();\n        }\n        return $.token.balances[account];\n    }\n\n    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.\n    /// @dev Utilizes the \"Global\" purpose internally\n    function totalAssets(\n        IAutopool.AssetBreakdown storage assetBreakdown\n    ) public view returns (uint256) {\n        return totalAssets(assetBreakdown, IAutopool.TotalAssetPurpose.Global);\n    }\n\n    /// @notice Returns the total amount of the underlying asset that is “managed” by the Vault with respect to its\n    /// usage\n    /// @dev Value changes based on purpose. Global is an avg. Deposit is valued higher. Withdraw is valued lower.\n    /// @param purpose The calculation the total assets will be used in\n    function totalAssets(\n        IAutopool.AssetBreakdown storage assetBreakdown,\n        IAutopool.TotalAssetPurpose purpose\n    ) public view returns (uint256) {\n        if (purpose == IAutopool.TotalAssetPurpose.Global) {\n            return assetBreakdown.totalIdle + assetBreakdown.totalDebt;\n        } else if (purpose == IAutopool.TotalAssetPurpose.Deposit) {\n            return assetBreakdown.totalIdle + assetBreakdown.totalDebtMax;\n        } else if (purpose == IAutopool.TotalAssetPurpose.Withdraw) {\n            return assetBreakdown.totalIdle + assetBreakdown.totalDebtMin;\n        } else {\n            revert InvalidTotalAssetPurpose();\n        }\n    }\n\n    // @notice Scale the amount from existing decimals to newDecimals\n    function changeDecimals(\n        uint256 amount,\n        uint256 existingDecimals,\n        uint256 newDecimals\n    ) internal pure returns (uint256) {\n        if (existingDecimals == newDecimals) {\n            return amount;\n        }\n        if (existingDecimals > newDecimals) {\n            return amount / (10 ** (existingDecimals - newDecimals));\n        } else {\n            return amount * (10 ** (newDecimals - existingDecimals));\n        }\n    }\n\n    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided,\n    /// in an ideal scenario where all the conditions are met\n    function convertToShares(\n        uint256 assets,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding,\n        uint256 baseAssetDecimals\n    ) internal pure returns (uint256 shares) {\n        // slither-disable-next-line incorrect-equality\n        shares = (assets == 0 || supply == 0)\n            ? changeDecimals(assets, baseAssetDecimals, 18)\n            : assets.mulDiv(supply, totalAssetsForPurpose, rounding);\n    }\n\n    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    function convertToAssets(\n        uint256 shares,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding,\n        uint256 baseAssetDecimals\n    ) internal pure returns (uint256 assets) {\n        // slither-disable-next-line incorrect-equality\n        assets = (supply == 0)\n            ? changeDecimals(shares, 18, baseAssetDecimals)\n            : shares.mulDiv(totalAssetsForPurpose, supply, rounding);\n    }\n\n    /// @notice Returns the maximum amount of the underlying asset that can be\n    /// deposited into the Vault for the receiver, through a deposit call\n    function maxDeposit(\n        AutopoolState storage $,\n        address wallet,\n        bool paused,\n        uint256 baseAssetDecimals\n    ) public returns (uint256 maxAssets) {\n        uint256 aptTotalAssets = $.totalAssetsTimeChecked(IAutopool.TotalAssetPurpose.Deposit);\n        uint256 maxMintShares = maxMint($, wallet, paused);\n        maxAssets = convertToAssets(maxMintShares, aptTotalAssets, totalSupply(), Math.Rounding.Up, baseAssetDecimals);\n    }\n\n    /// @notice Returns the maximum amount of the Vault shares that\n    /// can be minted for the receiver, through a mint call.\n    function maxMint(AutopoolState storage $, address, bool paused) public returns (uint256) {\n        // If we are temporarily paused, or in full shutdown mode,\n        // no new shares are able to be minted\n        if (paused || $.shutdown) {\n            return 0;\n        }\n\n        // First deposit\n        uint256 ts = totalSupply();\n        if (ts == 0) {\n            return type(uint112).max;\n        }\n\n        // We know totalSupply greater than zero now so if totalAssets is zero\n        // the vault is in an invalid state and users would be able to mint shares for free\n        uint256 ta = AutopoolDebt.totalAssetsTimeChecked($, IAutopool.TotalAssetPurpose.Deposit);\n        if (ta == 0) {\n            return 0;\n        }\n\n        if (ts > type(uint112).max) {\n            return 0;\n        }\n\n        return type(uint112).max - ts;\n    }\n\n    /// @notice Set the rewarder contract used by the vault.\n    /// @param _rewarder Address of new rewarder.\n    function setRewarder(AutopoolState storage $, address _rewarder) external {\n        Errors.verifyNotZero(_rewarder, \"rewarder\");\n\n        address toBeReplaced = address($.rewarder);\n        // Check that the new rewarder has not been a rewarder before, and that the current rewarder and\n        //      new rewarder addresses are not the same.\n        if ($.pastRewarders.contains(_rewarder) || toBeReplaced == _rewarder) {\n            revert Errors.ItemExists();\n        }\n\n        if (toBeReplaced != address(0)) {\n            // slither-disable-next-line unused-return\n            $.pastRewarders.add(toBeReplaced);\n        }\n\n        $.rewarder = IMainRewarder(_rewarder);\n        emit RewarderSet(_rewarder, toBeReplaced);\n    }\n\n    /// @notice Returns the amount of tokens in existence.\n    /// @dev Subtracts any unlocked profit shares that will be burned\n    function totalSupply() public view returns (uint256 shares) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        shares = $.token.totalSupply - $.unlockedShares();\n    }\n\n    function transferAndMint(\n        AutopoolState storage $,\n        IERC20Metadata baseAsset,\n        uint256 assets,\n        uint256 shares,\n        address receiver\n    ) public {\n        // From OZ documentation:\n        // ----------------------\n        // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the\n        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,\n        // calls the vault, which is assumed not malicious.\n        //\n        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the\n        // assets are transferred and before the shares are minted, which is a valid state.\n        // slither-disable-next-line reentrancy-no-eth\n\n        Errors.verifyNotZero(assets, \"assets\");\n\n        baseAsset.safeTransferFrom(msg.sender, address(this), assets);\n\n        uint256 newIdle = $.assetBreakdown.totalIdle + assets;\n        $.assetBreakdown.totalIdle = newIdle;\n\n        $.token.mint(receiver, shares);\n\n        emit Deposit(msg.sender, receiver, assets, shares);\n\n        emit Nav(newIdle, $.assetBreakdown.totalDebt, totalSupply());\n    }\n\n    /// @notice Initiate the shutdown procedures for this vault\n    function shutdownVault(AutopoolState storage $, IAutopool.VaultShutdownStatus reason) external {\n        if (reason == IAutopool.VaultShutdownStatus.Active) {\n            revert InvalidShutdownStatus(reason);\n        }\n\n        $.shutdown = true;\n        $.shutdownStatus = reason;\n\n        emit Shutdown(reason);\n    }\n\n    /// @notice Allow the updating of symbol/desc for the vault (only AFTER shutdown)\n    /// @param newSymbol Symbol the Autopool will use going forward\n    /// @param newName Name the Autopool will use going forward\n    function setSymbolAndDescAfterShutdown(\n        AutopoolState storage $,\n        string memory newSymbol,\n        string memory newName\n    ) external {\n        Errors.verifyNotEmpty(newSymbol, \"newSymbol\");\n        Errors.verifyNotEmpty(newName, \"newName\");\n\n        // make sure the vault is no longer active\n        if ($.shutdownStatus == IAutopool.VaultShutdownStatus.Active) {\n            revert InvalidShutdownStatus($.shutdownStatus);\n        }\n\n        emit SymbolAndDescSet(newSymbol, newName);\n\n        $.symbol = newSymbol;\n        $.name = newName;\n    }\n\n    /// @notice Transfer out non-tracked tokens\n    function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external {\n        // Makes sure our params are valid\n        uint256 len = tokens.length;\n\n        Errors.verifyNotZero(len, \"len\");\n        Errors.verifyArrayLengths(len, amounts.length, \"tokens+amounts\");\n        Errors.verifyArrayLengths(len, destinations.length, \"tokens+destinations\");\n\n        emit TokensRecovered(tokens, amounts, destinations);\n\n        IAutopool autoPool = IAutopool(address(this));\n\n        for (uint256 i = 0; i < len; ++i) {\n            (address tokenAddress, uint256 amount, address destination) = (tokens[i], amounts[i], destinations[i]);\n\n            // Ensure this isn't an asset we care about\n            if (\n                tokenAddress == address(this) || tokenAddress == autoPool.asset()\n                    || autoPool.isDestinationRegistered(tokenAddress)\n                    || autoPool.isDestinationQueuedForRemoval(tokenAddress)\n            ) {\n                revert Errors.AssetNotAllowed(tokenAddress);\n            }\n\n            if (tokenAddress != 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {\n                IERC20Metadata(tokenAddress).safeTransfer(destination, amount);\n            } else {\n                // solhint-disable-next-line avoid-low-level-calls\n                (bool result,) = payable(destination).call{ value: amount }(\"\");\n                if (!result) {\n                    revert RecoveryFailed();\n                }\n            }\n        }\n    }\n}\n","Filename":"src/vault/libs/Autopool4626.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\n\ninterface ISwapRouterV2 is ISwapRouter {\n    struct UserSwapData {\n        address fromToken;\n        address toToken;\n        address target;\n        bytes data;\n    }\n\n    function initTransientSwap(\n        UserSwapData[] memory customRoutes\n    ) external;\n\n    function exitTransientSwap() external;\n}\n","Filename":"src/interfaces/swapper/ISwapRouterV2.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\n// solhint-disable no-inline-assembly\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\n\nstruct AutopoolState {\n    /// @notice Balances, allowances, and supply for the pool\n    /// @dev Want to keep this var in this position\n    AutopoolToken.TokenData token;\n    /// @notice Full list of possible destinations that could be deployed to\n    /// @dev Exposed via `getDestinations()`\n    EnumerableSet.AddressSet destinations;\n    /// @notice Destinations that are queued for removal\n    /// @dev Exposed via `getRemovalQueue`\n    EnumerableSet.AddressSet removalQueue;\n    /// @notice Whether or not the vault has been shutdown\n    /// @dev Exposed via `isShutdown()`\n    bool shutdown;\n    /// @notice Reason for shutdown (or `Active` if not shutdown)\n    /// @dev Exposed via `shutdownStatus()`\n    IAutopool.VaultShutdownStatus shutdownStatus;\n    /// @notice Lookup of destinationVaultAddress -> Info .. Debt reporting snapshot info\n    /// @dev Exposed via `getDestinationInfo`\n    mapping(address => AutopoolDebt.DestinationInfo) destinationInfo;\n    /// @notice Ordered list of destinations to withdraw from\n    /// @dev Exposed via `getWithdrawalQueue()`\n    StructuredLinkedList.List withdrawalQueue;\n    /// @notice Ordered list of destinations to debt report on. Ordered from oldest to newest\n    /// @dev Exposed via `getDebtReportingQueue()`\n    StructuredLinkedList.List debtReportQueue;\n    /// @notice State and settings related to gradual profit unlock\n    /// @dev Exposed via `getProfitUnlockSettings()`\n    IAutopool.ProfitUnlockSettings profitUnlockSettings;\n    /// @notice State and settings related to periodic and streaming fees\n    /// @dev Exposed via `getFeeSettings()`\n    IAutopool.AutopoolFeeSettings feeSettings;\n    /// @notice Rewarders that have been replaced.\n    /// @dev Exposed via `isPastRewarder()`\n    EnumerableSet.AddressSet pastRewarders;\n    /// @notice Main rewarder for this contract\n    IMainRewarder rewarder;\n    /// @notice Pool/token name\n    string name;\n    /// @notice Pool/token symbol\n    string symbol;\n    /// @notice Storage address of hook configurations\n    address hookStore;\n    /// @notice Factory contract that created this vault\n    address factory;\n    /// @notice Asset tracking for idle and debt values\n    /// @dev Exposed via `getAssetBreakdown()`\n    IAutopool.AssetBreakdown assetBreakdown;\n}\n\nstruct ProcessRebalanceParams {\n    IERC20Metadata baseAsset;\n    IERC3156FlashBorrower receiver;\n    IStrategy.RebalanceParams rebalanceParams;\n}\n\nlibrary AutopoolStorage {\n    // keccak256(abi.encode(uint256(keccak256(\"autopilot.storage.AutopoolState\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant SLOT = 0x17264fbcd79a365fd3ccff89407ad487986f8b37b9035d6bc8b51cacd5832200;\n\n    function load() internal pure returns (AutopoolState storage $) {\n        assembly {\n            $.slot := SLOT\n        }\n    }\n}\n","Filename":"src/vault/libs/AutopoolState.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\n/// @title Return stats on base LSTs\ninterface ILSTStats {\n    struct LSTStatsData {\n        uint256 lastSnapshotTimestamp;\n        uint256 baseApr;\n        int256 discount; // positive number is a discount, negative is a premium\n        uint24[10] discountHistory; // 7 decimal precision\n        uint40 discountTimestampByPercent; // timestamp that the token reached 1pct discount\n    }\n\n    /// @notice Get the current stats for the LST\n    /// @dev Returned data is a combination of current data and filtered snapshots\n    /// @return lstStatsData current data on the LST\n    function current() external returns (LSTStatsData memory lstStatsData);\n\n    /// @notice Get the EthPerToken (or Share) for the LST\n    /// @return ethPerShare the backing eth for the LST\n    function calculateEthPerToken() external view returns (uint256 ethPerShare);\n\n    /// @notice Returns whether to use the market price when calculating discount\n    /// @dev Will be true for rebasing tokens and other non-standard tokens\n    function usePriceAsDiscount() external view returns (bool useAsDiscount);\n}\n","Filename":"src/interfaces/stats/ILSTStats.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IAutopilotRouterBase } from \"src/interfaces/vault/IAutopilotRouterBase.sol\";\nimport { IRewards } from \"src/interfaces/rewarders/IRewards.sol\";\nimport { SwapParams } from \"src/interfaces/liquidation/IAsyncSwapper.sol\";\nimport { ISwapRouterV2 } from \"src/interfaces/swapper/ISwapRouterV2.sol\";\n\n/**\n * @title IAutopilotRouter Interface\n * @notice Extends the IAutopilotRouterBase with specific flows to save gas\n */\ninterface IAutopilotRouter is IAutopilotRouterBase {\n    error PreviewRedeemWithRoutesResult(uint256);\n\n    /**\n     * ***************************   Deposit ********************************\n     */\n\n    /**\n     * @notice deposit available asset balance to a AutopoolETH.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function depositBalance(\n        IAutopool vault,\n        address to,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice deposit max assets to a AutopoolETH.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function depositMax(\n        IAutopool vault,\n        address to,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * *************************   Withdraw   **********************************\n     */\n\n    /**\n     * @notice withdraw `amount` to a AutopoolETH.\n     * @param fromVault The AutopoolETH to withdraw assets from.\n     * @param toVault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param amount The amount of assets to withdraw from fromVault.\n     * @param maxSharesIn The max amount of fromVault shares withdrawn by caller.\n     * @param minSharesOut The min amount of toVault shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MaxSharesError, MinSharesError\n     */\n    function withdrawToDeposit(\n        IAutopool fromVault,\n        IAutopool toVault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesIn,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * *************************   Redeem    ********************************\n     */\n\n    /**\n     * @notice redeem `shares` to a AutopoolETH.\n     * @param fromVault The AutopoolETH to redeem shares from.\n     * @param toVault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param shares The amount of shares to redeem from fromVault.\n     * @param minSharesOut The min amount of toVault shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinAmountError, MinSharesError\n     */\n    function redeemToDeposit(\n        IAutopool fromVault,\n        IAutopool toVault,\n        address to,\n        uint256 shares,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem max shares to a AutopoolETH.\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param to The destination of assets.\n     * @param minAmountOut The min amount of assets received by `to`.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeemMax(\n        IAutopool vault,\n        address to,\n        uint256 minAmountOut\n    ) external payable returns (uint256 amountOut);\n\n    /**\n     * @notice previewRedeem `shares` shares from a AutopoolETH with a custom route\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param shares The amount of shares to redeem from vault.\n     * @param customRoute The custom route to use for the swap.\n     * @dev throws PreviewRedeemWithRoutesResult on all executions\n     */\n    function previewRedeemWithRoutes(\n        IAutopool vault,\n        uint256 shares,\n        ISwapRouterV2.UserSwapData[] calldata customRoute\n    ) external payable;\n\n    /**\n     * @notice redeem `shares` shares from a AutopoolETH with a custom route\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param to The destination of assets.\n     * @param shares The amount of shares to redeem from vault.\n     * @param minAmountOut The min amount of assets received by `to`.\n     * @param customRoute The custom route to use for the swap.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeemWithRoutes(\n        IAutopool vault,\n        address to,\n        uint256 shares,\n        uint256 minAmountOut,\n        ISwapRouterV2.UserSwapData[] calldata customRoute\n    ) external payable returns (uint256 amountOut);\n\n    /**\n     * @notice swaps token\n     * @param swapper Address of the swapper to use\n     * @param swapParams  Parameters for the swap\n     * @return amountReceived Swap output amount\n     */\n    function swapToken(\n        address swapper,\n        SwapParams memory swapParams\n    ) external payable returns (uint256 amountReceived);\n\n    /**\n     * @notice claims vault token rewards\n     * @param rewarder Address of the rewarder to claim from\n     * @param recipient Struct containing recipient details\n     * @return amountReceived Swap output amount\n     */\n    function claimRewards(\n        IRewards rewarder,\n        IRewards.Recipient calldata recipient,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external payable returns (uint256);\n\n    /**\n     * @notice swaps Exact token balance in the contract\n     * @param swapper Address of the swapper to use\n     * @param swapParams  Parameters for the swap\n     * @return amountReceived Swap output amount\n     * @dev sets the sellAmount to the balance of the contract\n     */\n    function swapTokenBalance(\n        address swapper,\n        SwapParams memory swapParams\n    ) external payable returns (uint256 amountReceived);\n\n    /**\n     * @notice stake Acc token balance\n     * @param duration The duration of the stake\n     * @param accToke contract address of the AccToke\n     * @param to The destination of ownership shares.\n     */\n    function stakeAccBalance(address accToke, uint256 duration, address to) external payable;\n\n    /**\n     * @notice stake Acc token for specified amount\n     * @param amount Amount of TOKE to stake\n     * @param accToke contract address of the AccToke\n     * @param duration The duration of the stake\n     * @param to The destination of ownership shares.\n     */\n    function stakeAcc(address accToke, uint256 amount, uint256 duration, address to) external payable;\n\n    /**\n     * @notice unstake Acc token balance\n     * @param accToke contract address of the AccToke\n     * @param lockupIds The lockup ids to unstake\n     * @param to The destination of staked TOKE.\n     */\n    function unstakeAcc(address accToke, uint256[] memory lockupIds, address to) external payable;\n\n    /**\n     * @notice Collect staking rewards\n     * @dev rewards can only be sent to user or router\n     * @param accToke contract address of the AccToke\n     * @param recipient The recipient of the rewards\n     * @return amountReceived Swap output amount\n     */\n    function collectAccTokeRewards(address accToke, address recipient) external payable returns (uint256);\n\n    /**\n     * @notice AccTokeV1 function to lock TOKE for `numOfCycles` cycles\n     * @param amount Amount of TOKE to lock up\n     * @param duration Number of cycles to lock for\n     */\n    function lockTokeFor(uint256 amount, uint256 duration) external payable;\n}\n","Filename":"src/interfaces/vault/IAutopilotRouter.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { ILSTStats } from \"src/interfaces/stats/ILSTStats.sol\";\n\n/// @title Return stats DEXs with LSTs\ninterface IDexLSTStats {\n    event DexSnapshotTaken(uint256 snapshotTimestamp, uint256 priorFeeApr, uint256 newFeeApr, uint256 unfilteredFeeApr);\n\n    struct StakingIncentiveStats {\n        // time-weighted average total supply to prevent spikes/attacks from impacting rebalancing\n        uint256 safeTotalSupply;\n        // rewardTokens, annualizedRewardAmounts, and periodFinishForRewards will match indexes\n        // they are split to workaround an issue with forge having nested structs\n        // address of the reward tokens\n        address[] rewardTokens;\n        // the annualized reward rate for the reward token\n        uint256[] annualizedRewardAmounts;\n        // the timestamp for when the rewards are set to terminate\n        uint40[] periodFinishForRewards;\n        // incentive rewards score. max 48, min 0\n        uint8 incentiveCredits;\n    }\n\n    struct DexLSTStatsData {\n        uint256 lastSnapshotTimestamp;\n        uint256 feeApr;\n        uint256[] reservesInEth;\n        StakingIncentiveStats stakingIncentiveStats;\n        ILSTStats.LSTStatsData[] lstStatsData;\n    }\n\n    /// @notice Get the current stats for the DEX with underlying LST tokens\n    /// @dev Returned data is a combination of current data and filtered snapshots\n    /// @return dexLSTStatsData current data on the DEX\n    function current() external returns (DexLSTStatsData memory dexLSTStatsData);\n}\n","Filename":"src/interfaces/stats/IDexLSTStats.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n}\n","Filename":"lib/openzeppelin-contracts/contracts/utils/Strings.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nlibrary VaultTypes {\n    bytes32 public constant LST = keccak256(\"LST\");\n    bytes32 public constant GENERAL_V1 = keccak256(\"GENERAL_V1\");\n}\n","Filename":"src/vault/VaultTypes.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\ninterface ICurveResolver {\n    /// @notice Resolve details of a Curve pool regardless of type or version\n    /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools.\n    /// @param poolAddress pool address to lookup\n    /// @return tokens tokens that make up the pool\n    /// @return numTokens the number of tokens. tokens are not unwrapped.\n    /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap\n    function resolve(\n        address poolAddress\n    ) external view returns (address[8] memory tokens, uint256 numTokens, bool isStableSwap);\n\n    /// @notice Resolve details of a Curve pool regardless of type or version\n    /// @dev This resolves tokens without unwrapping to underlying in the case of meta pools.\n    /// @dev Use the isStableSwap value to differentiate between StableSwap (V1) and CryptoSwap (V2) pools.\n    /// @param poolAddress pool address to lookup\n    /// @return tokens tokens that make up the pool\n    /// @return numTokens the number of tokens. tokens are not unwrapped\n    /// @return lpToken lp token of the pool\n    /// @return isStableSwap is this a StableSwap pool. false = CryptoSwap\n    function resolveWithLpToken(\n        address poolAddress\n    ) external view 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(\n        address poolAddress\n    ) 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(\n        address poolAddress\n    ) external view returns (uint256[8] memory balances);\n}\n","Filename":"src/interfaces/utils/ICurveResolver.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\n// solhint-disable no-inline-assembly,avoid-low-level-calls\n\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { SSTORE2 } from \"src/external/solady/SSTORE2.sol\";\nimport { LibBytes } from \"src/external/solady/LibBytes.sol\";\nimport { AutopoolState } from \"src/vault/libs/AutopoolState.sol\";\nimport { IStrategyHook } from \"src/interfaces/strategy/IStrategyHook.sol\";\n\nlibrary AutopoolStrategyHooks {\n    /// =====================================================\n    /// Hook configurations are read and stored via SSTORE2.\n    /// The storage is formatted as fixed length packed byte20(address) arrays\n    /// that are themselves packed in the order of the function definitions\n    /// on the interface.\n    /// For example, lets say we have 3 hooks configured (with 8 functions and 10 hooks supported):\n    ///   - Hook 1, address(1), supports onRebalanceStart (1) and onRebalanceFeeProfitHandlingComplete (16)\n    ///   - Hook 2, address(2), supports onRebalanceStart (1) and onRebalanceInAssetsReturned (4)\n    ///   - Hook 3, address(3), supports onRebalanceComplete (32)\n    /// Storage would look like:\n    ///   0000000000000000000000000000000000000001 - Start of hooks to call onRebalanceStart() (flag 1) for\n    ///   0000000000000000000000000000000000000002\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000 - Hooks to call onRebalanceOutAssetsReady() (flag 2) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000002 - Hooks to call onRebalanceInAssetsReturned() (flag 4) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000 - Hooks to call onRebalanceDestinationVaultUpdated() (flag 8) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000001 - Hooks to call onRebalanceFeeProfitHandlingComplete() (flag 16) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000003 - Start of hooks to call onRebalanceComplete() (flag 32) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000 - Start of hooks to call onDestinationDebtReport() (flag 64) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000 - Start of hooks to call onNavUpdate() (flag 128) for\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n    ///   0000000000000000000000000000000000000000\n\n    /// Adding hooks should append to the lowest empty of the section\n    /// Removing hooks should shift remainder down to maintain order\n\n    /// =====================================================\n    /// Constants\n    /// =====================================================\n\n    /// @notice Returns maximum number of hooks supported\n    uint256 public constant NUM_HOOKS = 10;\n\n    /// @notice Returns the number of hook functions supported\n    uint256 public constant NUM_FUNCTIONS = 8;\n\n    /// @notice Max function flag given the number of functions\n    uint256 private constant MAX_FN_FLAG = 2 ** NUM_FUNCTIONS;\n\n    /// =====================================================\n    /// Errors\n    /// =====================================================\n\n    /// @notice Fires when are at the maximum number of configured hooks for a function\n    error MaxHooksSet();\n\n    /// @notice Fires when a hook is already registered for a function\n    error HookAlreadySet(address hook, uint256 fn);\n\n    /// @notice Fires on removal when a hook doesn't exist\n    error HookNotSet(address hook);\n\n    /// @notice Fires on removal when a function is supposed to be registered but isn't\n    error FunctionNotSet(address hook, uint256 fn);\n\n    /// @notice Fires when a hook execution fails\n    error HookExecutionFailed(address hook, bytes underlyingError);\n\n    /// =====================================================\n    /// Structs\n    /// =====================================================\n\n    /// @notice Used for view/display purposes\n    struct HookConfiguration {\n        address[NUM_HOOKS] onRebalanceStart;\n        address[NUM_HOOKS] onRebalanceOutAssetsReady;\n        address[NUM_HOOKS] onRebalanceInAssetsReturned;\n        address[NUM_HOOKS] onRebalanceDestinationVaultUpdated;\n        address[NUM_HOOKS] onRebalanceFeeProfitHandlingComplete;\n        address[NUM_HOOKS] onRebalanceComplete;\n        address[NUM_HOOKS] onDestinationDebtReport;\n        address[NUM_HOOKS] onNavUpdate;\n    }\n\n    /// =====================================================\n    /// Functions - External\n    /// =====================================================\n\n    /// @notice Execute all hooks, in order, based on the provided configuration\n    /// @dev Caller is responsible for ensuring fnIndex and call are correctly paired\n    /// @param hooks Packed hooks configuration data\n    /// @param fnIndex Index of the function to execute\n    /// @param call Call to make to the hook\n    function executeHooks(bytes memory hooks, uint256 fnIndex, bytes memory call) external {\n        uint256 hookIx = (fnIndex * NUM_HOOKS * 20) + 32; // 32 for the length of bytes\n        uint256 hookEndIx = hookIx + (20 * NUM_HOOKS);\n\n        while (hookIx < hookEndIx) {\n            // Pull the bytes out at the index and convert to address\n            address addrVal;\n            assembly {\n                addrVal := shr(96, mload(add(hooks, hookIx)))\n            }\n\n            // If its there an address, we execute, otherwise we're done\n            if (addrVal != address(0)) {\n                (bool result, bytes memory errorData) = addrVal.call(call);\n                if (!result) {\n                    revert HookExecutionFailed(addrVal, errorData);\n                }\n            } else {\n                break;\n            }\n\n            unchecked {\n                hookIx += 20;\n            }\n        }\n    }\n\n    /// @notice Add a set of hooks to the Autopools configuration\n    /// @param $ Storage data of the calling Autopool\n    /// @param newHooks Set of hooks to add to the Autopool\n    /// @param configDatas Set of datas to pass to the onRegistered function of the hook\n    function addHooks(AutopoolState storage $, IStrategyHook[] memory newHooks, bytes[] memory configDatas) external {\n        bytes memory flags = getHookBytes($);\n\n        uint256 len = newHooks.length;\n        Errors.verifyNotZero(len, \"len\");\n        Errors.verifyArrayLengths(len, configDatas.length, \"ars\");\n\n        for (uint256 i = 0; i < len;) {\n            flags = _addHook(flags, newHooks[i], configDatas[i]);\n\n            unchecked {\n                ++i;\n            }\n        }\n\n        $.hookStore = SSTORE2.write(flags);\n    }\n\n    /// @notice Remove a hook from the Autopools configuration\n    /// @param $ Storage data of the calling Autopool\n    /// @param hookToRemove Hook to remove from to the Autopool\n    /// @param cleanupData Data to pass to the onUnregistered function of the hook\n    function removeHook(AutopoolState storage $, IStrategyHook hookToRemove, bytes calldata cleanupData) external {\n        Errors.verifyNotZero(address(hookToRemove), \"hookToRemove\");\n\n        bytes memory flags;\n\n        address hookStorage = $.hookStore;\n        if (hookStorage != address(0)) {\n            flags = SSTORE2.read(hookStorage);\n        } else {\n            revert HookNotSet(address(hookToRemove));\n        }\n\n        // We are OK with assumption that supported flags can't change between\n        // register and unregister\n        uint8 supportedHookFunctions = hookToRemove.getFnFlags();\n        Errors.verifyNotZero(supportedHookFunctions, \"supportedHookFunctions\");\n        uint256 fnToCheck = 1;\n\n        bytes memory newFlagsData;\n        uint256 bytesIndex = 0;\n\n        // Loop over all the functions hook slots\n        // build our replacement storage data, newFlagsData\n        // Replace the address with empty where needed\n        bytes memory empty = hex\"0000000000000000000000000000000000000000\";\n\n        while (fnToCheck < MAX_FN_FLAG) {\n            uint256 hookIx = 0;\n            bool needToRemove = supportedHookFunctions & fnToCheck == fnToCheck;\n            bool removed = false;\n\n            while (hookIx < NUM_HOOKS) {\n                // Get our existing value\n                address addrVal;\n                {\n                    bytes memory val = LibBytes.slice(flags, bytesIndex, bytesIndex + 20);\n                    assembly {\n                        addrVal := shr(96, mload(add(val, 32)))\n                    }\n                }\n                if (addrVal == address(hookToRemove)) {\n                    // Don't put anything in its spot so that the remaining\n                    // values will shift down. We'll tack an empty onto the end\n                    removed = true;\n                } else {\n                    // Not the value we're looking for, forward it on\n                    newFlagsData = LibBytes.concat(newFlagsData, abi.encodePacked(addrVal));\n                }\n\n                unchecked {\n                    ++hookIx;\n                    bytesIndex += 20;\n                }\n            }\n\n            if (needToRemove && !removed) {\n                revert FunctionNotSet(address(hookToRemove), fnToCheck);\n            }\n\n            // We don't support duplicates so we know it was only in once\n            // We will revert if didn't remove, so we know we removed one item\n            // Add an empty onto the end in its place\n            if (removed) {\n                newFlagsData = LibBytes.concat(newFlagsData, empty);\n            }\n\n            unchecked {\n                fnToCheck *= 2;\n            }\n        }\n\n        // We removed it so run the cleanup\n        hookToRemove.onUnregistered(cleanupData);\n\n        $.hookStore = SSTORE2.write(newFlagsData);\n    }\n\n    /// @notice Get hooks in a proper format\n    /// @dev Do not use in any executing code\n    /// @param $ Storage data of the calling Autopool\n    function getHooks(\n        AutopoolState storage $\n    ) external view returns (HookConfiguration memory) {\n        bytes memory flags = getHookBytes($);\n\n        uint256 memSize = NUM_HOOKS * NUM_FUNCTIONS * 32;\n        assembly {\n            // Get our working space for building the struct\n            let structPtr := mload(0x40)\n\n            // Update pointer to new position\n            mstore(0x40, add(structPtr, memSize))\n\n            // Get the store of our addresses, first 32 is array size\n            let dataPtr := add(flags, 32)\n            let dataEnd := add(dataPtr, mload(flags))\n            let offset := 0\n\n            // Loop our data and set the addresses\n            for { } lt(dataPtr, dataEnd) { } {\n                let word := mload(dataPtr)\n\n                // Convert to address\n                word := shr(96, word)\n\n                // Add address to struct\n                mstore(add(structPtr, offset), word)\n\n                // Increment our counters\n                dataPtr := add(dataPtr, 20)\n                offset := add(offset, 32)\n            }\n\n            return(structPtr, memSize)\n        }\n    }\n\n    /// =====================================================\n    /// Functions - Public\n    /// =====================================================\n\n    /// @notice Get hooks in a proper format\n    /// @dev Do not use in any executing code\n    /// @param $ Storage data of the calling Autopool\n    function getHookBytes(\n        AutopoolState storage $\n    ) public view returns (bytes memory) {\n        bytes memory flags;\n\n        address hookStorage = $.hookStore;\n        if (hookStorage != address(0)) {\n            flags = SSTORE2.read(hookStorage);\n        } else {\n            // If we haven't set any data initialize the array\n            // to all empty addresses\n            flags = new bytes(NUM_HOOKS * NUM_FUNCTIONS * 20);\n        }\n\n        return flags;\n    }\n\n    /// =====================================================\n    /// Functions - Private\n    /// =====================================================\n\n    /// @notice Add a hook to the Autopools configuration\n    /// @param flags Existing hook data\n    /// @param newHook Hook to add to the Autopool\n    /// @param configData Data to pass to the onRegistered function of the hook\n    /// @return New flags configuration storage data\n    function _addHook(\n        bytes memory flags,\n        IStrategyHook newHook,\n        bytes memory configData\n    ) private returns (bytes memory) {\n        Errors.verifyNotZero(address(newHook), \"newHook\");\n\n        uint8 supportedHookFunctions = newHook.getFnFlags();\n        Errors.verifyNotZero(supportedHookFunctions, \"supportedHookFunctions\");\n        uint256 fnToCheck = 1;\n\n        bytes memory newFlagsData;\n        uint256 bytesIndex = 0;\n\n        // Loop over all the functions hook slots and\n        // build our replacement storage data, newFlagsData\n        // Splice in our new values where we need\n        while (fnToCheck < MAX_FN_FLAG) {\n            uint256 hookIx = 0;\n            bool needToSet = supportedHookFunctions & fnToCheck == fnToCheck;\n            bool set = false;\n            while (hookIx < NUM_HOOKS) {\n                // Get our existing value\n                bytes memory val = LibBytes.slice(flags, bytesIndex, bytesIndex + 20);\n\n                if (needToSet && !set) {\n                    address addrVal;\n                    assembly {\n                        addrVal := shr(96, mload(add(val, 32)))\n                    }\n\n                    if (addrVal == address(newHook)) {\n                        revert HookAlreadySet(addrVal, fnToCheck);\n                    } else if (addrVal == address(0)) {\n                        // If the space is empty we can use it\n                        set = true;\n                        newFlagsData = LibBytes.concat(newFlagsData, abi.encodePacked(newHook));\n                    } else {\n                        // Space isn't empty so forward the existing value\n                        newFlagsData = LibBytes.concat(newFlagsData, val);\n                    }\n                } else {\n                    // We don't need to set, or already set, so just forward the current value\n                    newFlagsData = LibBytes.concat(newFlagsData, val);\n                }\n\n                unchecked {\n                    ++hookIx;\n                    bytesIndex += 20;\n                }\n            }\n\n            if (needToSet && !set) {\n                // We needed to configure the hook but couldn't. Means we couldn't find\n                // a spot which implies we have the maximum number already configured\n                revert MaxHooksSet();\n            }\n\n            unchecked {\n                fnToCheck *= 2;\n            }\n        }\n\n        // We were able to add it to the config, so set it up\n        newHook.onRegistered(configData);\n\n        return newFlagsData;\n    }\n}\n","Filename":"src/vault/libs/AutopoolStrategyHooks.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { IERC4626 } from \"src/interfaces/vault/IERC4626.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { IAutopoolStrategy } from \"src/interfaces/strategy/IAutopoolStrategy.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20Permit } from \"openzeppelin-contracts/token/ERC20/extensions/draft-IERC20Permit.sol\";\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\n\ninterface IAutopool is IERC4626, IERC20Permit {\n    enum VaultShutdownStatus {\n        Active,\n        Deprecated,\n        Exploit\n    }\n\n    /// @param unlockPeriodInSeconds Time it takes for profit to unlock in seconds\n    /// @param fullProfitUnlockTime Time at which all profit will have been unlocked\n    /// @param lastProfitUnlockTime Last time profits were unlocked\n    /// @param profitUnlockRate Per second rate at which profit shares unlocks. Rate when calculated is denominated in\n    /// MAX_BPS_PROFIT. TODO: Get into uint112\n    struct ProfitUnlockSettings {\n        uint48 unlockPeriodInSeconds;\n        uint48 fullProfitUnlockTime;\n        uint48 lastProfitUnlockTime;\n        uint256 profitUnlockRate;\n    }\n\n    /// @param feeSink Where claimed fees are sent\n    /// @param totalAssetsHighMark The last totalAssets amount we took fees at\n    /// @param totalAssetsHighMarkTimestamp The last timestamp we updated the high water mark\n    /// @param lastPeriodicFeeTake Timestamp of when the last periodic fee was taken.\n    /// @param periodicFeeSink Address that receives periodic fee.\n    /// @param periodicFeeBps Current periodic fee.  100% == 10000.\n    /// @param streamingFeeBps Current streaming fee taken on profit. 100% == 10000\n    /// @param navPerShareLastFeeMark The last nav/share height we took fees at\n    /// @param navPerShareLastFeeMarkTimestamp The last timestamp we took fees at\n    /// @param rebalanceFeeHighWaterMarkEnabled Returns whether the nav/share high water mark is enabled for the\n    /// rebalance fee\n    struct AutopoolFeeSettings {\n        address feeSink;\n        uint256 totalAssetsHighMark;\n        uint256 totalAssetsHighMarkTimestamp;\n        uint256 lastPeriodicFeeTake;\n        address periodicFeeSink;\n        uint256 periodicFeeBps;\n        uint256 streamingFeeBps;\n        uint256 navPerShareLastFeeMark;\n        uint256 navPerShareLastFeeMarkTimestamp;\n        bool rebalanceFeeHighWaterMarkEnabled;\n    }\n\n    /// @param totalIdle The amount of baseAsset deposited into the contract pending deployment\n    /// @param totalDebt The current (though cached) value of assets we've deployed\n    /// @param totalDebtMin The current (though cached) value of assets we use for valuing during deposits\n    /// @param totalDebtMax The current (though cached) value of assets we use for valuing during withdrawals\n    struct AssetBreakdown {\n        uint256 totalIdle;\n        uint256 totalDebt;\n        uint256 totalDebtMin;\n        uint256 totalDebtMax;\n    }\n\n    enum TotalAssetPurpose {\n        Global,\n        Deposit,\n        Withdraw\n    }\n\n    /* ******************************** */\n    /*      Events                      */\n    /* ******************************** */\n\n    event RebalanceStarted(address receiver, IStrategy.RebalanceParams rebalanceParams);\n    event RebalanceCompleted(AutopoolDebt.AssetChanges updatedAssets);\n\n    // Autopool4626\n\n    // event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n    event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    event TokensRecovered(address[] tokens, uint256[] amounts, address[] destinations);\n    event Shutdown(IAutopool.VaultShutdownStatus reason);\n    event RewarderSet(address newRewarder, address oldRewarder);\n    event SymbolAndDescSet(string symbol, string desc);\n\n    // AutopoolDebt\n\n    event DestinationDebtReporting(\n        address destination, AutopoolDebt.IdleDebtUpdates debtInfo, uint256 claimed, uint256 claimGasUsed\n    );\n    event NewNavShareFeeMark(uint256 navPerShare, uint256 timestamp);\n    // event Nav(uint256 idle, uint256 debt, uint256 totalSupply);\n    // event Withdraw(\n    //     address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares\n    // );\n\n    // AutopoolDestinations\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    // AutopoolFees\n\n    event FeeCollected(uint256 fees, address feeSink, uint256 mintedShares, uint256 profit, uint256 totalAssets);\n    event PeriodicFeeCollected(uint256 fees, address feeSink, uint256 mintedShares);\n    // event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n    event PeriodicFeeSet(uint256 newFee);\n    event PeriodicFeeSinkSet(address newPeriodicFeeSink);\n    event LastPeriodicFeeTakeSet(uint256 lastPeriodicFeeTake);\n    event RebalanceFeeHighWaterMarkEnabledSet(bool enabled);\n    // event NewNavShareFeeMark(uint256 navPerShare, uint256 timestamp);\n    event NewTotalAssetsHighWatermark(uint256 assets, uint256 timestamp);\n    event StreamingFeeSet(uint256 newFee);\n    event FeeSinkSet(address newFeeSink);\n    event NewProfitUnlockTime(uint48 timeSeconds);\n\n    // AutopoolToken\n\n    /// @dev Emitted when `value` tokens are moved from one account `from` to another `to`.\n    //event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /// @dev Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}.\n    /// `value` is the new allowance.\n    // event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /* ******************************** */\n    /*      Errors                      */\n    /* ******************************** */\n\n    // Autopool\n\n    error InvalidDecimals();\n    error NavOpsInProgress();\n    error NavDecreased(uint256 oldNav, uint256 newNav);\n    error ValueSharesMismatch(uint256 value, uint256 shares);\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\n    // Autopool4626\n\n    error InvalidTotalAssetPurpose();\n    error InvalidShutdownStatus(IAutopool.VaultShutdownStatus status);\n    error RecoveryFailed();\n\n    // AutopoolDestination\n\n    error BaseAssetMismatch(address destinationVault);\n\n    // AutopoolDebt\n\n    error VaultShutdown();\n    error WithdrawShareCalcInvalid(uint256 currentShares, uint256 cachedShares);\n    //error RebalanceFailed(string message);\n    error InvalidPrices();\n    //error InvalidTotalAssetPurpose();\n    error InvalidDestination(address destination);\n    error TooFewAssets(uint256 requested, uint256 actual);\n    error SharesAndAssetsReceived(uint256 assets, uint256 shares);\n    error AmountExceedsAllowance(uint256 shares, uint256 allowed);\n    error PositivePriceRecoupNotCovered(uint256 remaining);\n    error InsufficientAssets(address asset);\n    error RebalanceDestinationUnderlyerMismatch(address destination, address trueUnderlyer, address providedUnderlyer);\n    error OnlyRebalanceToIdleAvailable();\n    error UnregisteredDestination(address dest);\n    error RebalanceDestinationsMatch();\n\n    // AutopoolFees\n\n    error InvalidFee(uint256 newFee);\n    error AlreadySet();\n    error DebtReportingStale();\n\n    // AutopoolStrategyHooks\n\n    /// @notice Fires when are at the maximum number of configured hooks for a function\n    error MaxHooksSet();\n\n    /// @notice Fires when a hook is already registered for a function\n    error HookAlreadySet(address hook, uint256 fn);\n\n    /// @notice Fires on removal when a hook doesn't exist\n    error HookNotSet(address hook);\n\n    /// @notice Fires on removal when a function is supposed to be registered but isn't\n    error FunctionNotSet(address hook, uint256 fn);\n\n    /// @notice Fires when a hook execution fails\n    error HookExecutionFailed(address hook, bytes underlyingError);\n\n    // AutopoolToken\n\n    /// @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    /// @param balance Current balance for the interacting account.\n    /// @param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n\n    /// @dev Indicates a failure with the token `sender`. Used in transfers.\n    /// @param sender Address whose tokens are being transferred.\n    error ERC20InvalidSender(address sender);\n\n    /// @dev Indicates a failure with the token `receiver`. Used in transfers.\n    /// @param receiver Address to which tokens are being transferred.\n    error ERC20InvalidReceiver(address receiver);\n\n    /// @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.\n    ///@param spender Address that may be allowed to operate on tokens without being their owner.\n    /// @param allowance Amount of tokens a `spender` is allowed to operate with.\n    ///@param needed Minimum amount required to perform a transfer.\n    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n    /// @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n    /// @param approver Address initiating an approval operation.\n    error ERC20InvalidApprover(address approver);\n\n    /// @dev Indicates a failure with the `spender` to be approved. Used in approvals.\n    /// @param spender Address that may be allowed to operate on tokens without being their owner.\n    error ERC20InvalidSpender(address spender);\n\n    /// @dev Permit deadline has expired.\n    error ERC2612ExpiredSignature(uint256 deadline);\n\n    /// @dev Mismatched signature.\n    error ERC2612InvalidSigner(address signer, address owner);\n\n    /// @dev The nonce used for an `account` is not the expected current nonce.\n    error InvalidAccountNonce(address account, uint256 currentNonce);\n\n    /// @notice A full unit of this pool\n    // solhint-disable-next-line func-name-mixedcase\n    function ONE() external view returns (uint256);\n\n    /// @notice Amount to pad scaling operations by\n    function decimalPad() external view returns (uint256);\n\n    /// @notice Query the type of vault\n    function vaultType() external view returns (bytes32);\n\n    /// @notice Strategy governing the pools rebalances\n    function autoPoolStrategy() external view returns (IAutopoolStrategy);\n\n    /// @notice Allow token recoverer to collect dust / unintended transfers (non-tracked assets only)\n    function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external;\n\n    /// @notice Set the order of destination vaults used for withdrawals\n    // NOTE: will be done going directly to strategy (IStrategy) vault points to.\n    //       How it'll delegate is still being decided\n    // function setWithdrawalQueue(address[] calldata destinations) external;\n\n    /// @notice Get a list of destination vaults with pending assets to clear out\n    function getRemovalQueue() external view returns (address[] memory);\n\n    function getFeeSettings() external view returns (AutopoolFeeSettings memory);\n\n    /// @notice Initiate the shutdown procedures for this vault\n    function shutdown(\n        VaultShutdownStatus reason\n    ) external;\n\n    /// @notice True if the vault has been shutdown\n    function isShutdown() external view returns (bool);\n\n    /// @notice Returns the reason for shutdown (or `Active` if not shutdown)\n    function shutdownStatus() external view returns (VaultShutdownStatus);\n\n    /// @notice gets the list of supported destination vaults for the Autopool/Strategy\n    /// @return _destinations List of supported destination vaults\n    function getDestinations() external view returns (address[] memory _destinations);\n\n    function convertToShares(\n        uint256 assets,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) external view returns (uint256 shares);\n\n    function convertToAssets(\n        uint256 shares,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) external view returns (uint256 assets);\n\n    function totalAssets(\n        TotalAssetPurpose purpose\n    ) 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(\n        address destVault\n    ) external view returns (AutopoolDebt.DestinationInfo memory);\n\n    /// @notice check if a destination is registered with the vault\n    function isDestinationRegistered(\n        address destination\n    ) external view returns (bool);\n\n    /// @notice get if a destinationVault is queued for removal by the AutopoolETH\n    function isDestinationQueuedForRemoval(\n        address destination\n    ) external view returns (bool);\n\n    /// @notice Returns instance of vault rewarder.\n    function rewarder() external view returns (IMainRewarder);\n\n    /// @notice Returns boolean telling whether address passed in is past rewarder.\n    function isPastRewarder(\n        address _pastRewarder\n    ) external view returns (bool);\n}\n","Filename":"src/interfaces/vault/IAutopool.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\n/// @title Keep track of Vaults created through the Vault Factory\ninterface IAutopoolRegistry {\n    ///////////////////////////////////////////////////////////////////\n    //                        Errors\n    ///////////////////////////////////////////////////////////////////\n\n    error VaultNotFound(address vaultAddress);\n    error VaultAlreadyExists(address vaultAddress);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Events\n    ///////////////////////////////////////////////////////////////////\n    event VaultAdded(address indexed asset, address indexed vault);\n    event VaultRemoved(address indexed asset, address indexed vault);\n\n    ///////////////////////////////////////////////////////////////////\n    //                        Functions\n    ///////////////////////////////////////////////////////////////////\n\n    /// @notice Checks if an address is a valid vault\n    /// @param vaultAddress Vault address to be added\n    function isVault(\n        address vaultAddress\n    ) external view returns (bool);\n\n    /// @notice Registers a vault\n    /// @param vaultAddress Vault address to be added\n    function addVault(\n        address vaultAddress\n    ) external;\n\n    /// @notice Removes vault registration\n    /// @param vaultAddress Vault address to be removed\n    function removeVault(\n        address vaultAddress\n    ) 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(\n        address asset\n    ) 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(\n        bytes32 _vaultType\n    ) external view returns (address[] memory);\n}\n","Filename":"src/interfaces/vault/IAutopoolRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\n\ninterface ISyncSwapper {\n    error DataMismatch(string element);\n    error InvalidIndex();\n\n    /**\n     * @notice Returns address of swap router that can access SyncSwapper contract\n     */\n    function router() external view returns (ISwapRouter);\n\n    /**\n     * @notice Swaps sellToken for buyToken\n     * @param pool The address of the pool for the swapper\n     * @param sellTokenAddress The address of the token to sell\n     * @param sellAmount The amount of sellToken to sell\n     * @param buyTokenAddress The address of the token to buy\n     * @param minBuyAmount The minimum amount of buyToken expected\n     * @param data Additional data used differently by the different swappers\n     * @return actualBuyAmount The actual amount received from the swap\n     */\n    function swap(\n        address pool,\n        address sellTokenAddress,\n        uint256 sellAmount,\n        address buyTokenAddress,\n        uint256 minBuyAmount,\n        bytes memory data\n    ) external returns (uint256 actualBuyAmount);\n\n    /**\n     * @notice Validates that the swapData contains the correct information, ensuring that the encoded data contains the\n     * correct 'fromAddress' and 'toAddress' (swapData.token), and verifies that these tokens are in the pool\n     * @dev This function should revert with a DataMismatch error if the swapData is invalid\n     * @param fromAddress The address from which the swap originates\n     * @param swapData The data associated with the swap that needs to be validated\n     */\n    function validate(address fromAddress, ISwapRouter.SwapData memory swapData) external view;\n}\n","Filename":"src/interfaces/swapper/ISyncSwapper.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n/// @dev Interface of the ERC4626 \"Tokenized Vault Standard\", as defined in https://eips.ethereum.org/EIPS/eip-4626\n/// @dev Due to the nature of obtaining estimates for previewing withdraws and redeems, a few functions are not\n///     view and therefore do not conform to eip 4626.  These functions use state changing operations\n///     to get accurate estimates, reverting after the preview amounts have been obtained.\ninterface IERC4626 is IERC20Metadata {\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares\n    );\n\n    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and\n    /// withdrawing.\n    /// @dev\n    /// - MUST be an ERC-20 token contract.\n    /// - MUST NOT revert.\n    function asset() external view returns (address assetTokenAddress);\n\n    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.\n    /// @dev\n    /// - SHOULD include any compounding that occurs from yield.\n    /// - MUST be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT revert.\n    function totalAssets() external view returns (uint256 totalManagedAssets);\n\n    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    /// @dev\n    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT show any variations depending on the caller.\n    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n    /// from.\n    function convertToShares(\n        uint256 assets\n    ) external view returns (uint256 shares);\n\n    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    /// @dev\n    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n    /// - MUST NOT show any variations depending on the caller.\n    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n    /// from.\n    function convertToAssets(\n        uint256 shares\n    ) external view returns (uint256 assets);\n\n    /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the\n    /// receiver,\n    /// through a deposit call.\n    /// @dev\n    /// - MUST return a limited value if receiver is subject to some deposit limit.\n    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n    /// - MUST NOT revert.\n    function maxDeposit(\n        address receiver\n    ) external returns (uint256 maxAssets);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block,\n    /// given\n    /// current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n    ///   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n    ///   in the same transaction.\n    /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n    ///   deposit would be accepted, regardless if the user has enough tokens approved, etc.\n    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.\n    function previewDeposit(\n        uint256 assets\n    ) external returns (uint256 shares);\n\n    /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n    /// @dev\n    /// - MUST emit the Deposit event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   deposit execution, and are accounted for during deposit.\n    /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n    ///   approving enough underlying tokens to the Vault contract, etc).\n    ///\n    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n    function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n    /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n    /// @dev\n    /// - MUST return a limited value if receiver is subject to some mint limit.\n    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n    /// - MUST NOT revert.\n    function maxMint(\n        address receiver\n    ) external returns (uint256 maxShares);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n    /// current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n    ///   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n    ///   same transaction.\n    /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n    ///   would be accepted, regardless if the user has enough tokens approved, etc.\n    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by minting.\n    function previewMint(\n        uint256 shares\n    ) external returns (uint256 assets);\n\n    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n    /// @dev\n    /// - MUST emit the Deposit event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n    ///   execution, and are accounted for during mint.\n    /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n    ///   approving enough underlying tokens to the Vault contract, etc).\n    ///\n    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n    function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n    /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n    /// Vault, through a withdraw call.\n    /// @dev\n    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n    /// - MUST NOT revert.\n    function maxWithdraw(\n        address owner\n    ) external returns (uint256 maxAssets);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n    /// given current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n    ///   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n    ///   called\n    ///   in the same transaction.\n    /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n    ///   the withdrawal would be accepted, regardless if the user has enough shares, etc.\n    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.\n    function previewWithdraw(\n        uint256 assets\n    ) external returns (uint256 shares);\n\n    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n    /// @dev\n    /// - MUST emit the Withdraw event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   withdraw execution, and are accounted for during withdraw.\n    /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n    ///   not having enough shares, etc).\n    ///\n    /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n    /// Those methods should be performed separately.\n    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);\n\n    /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n    /// through a redeem call.\n    /// @dev\n    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n    /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n    /// - MUST NOT revert.\n    function maxRedeem(\n        address owner\n    ) external returns (uint256 maxShares);\n\n    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n    /// given current on-chain conditions.\n    /// @dev\n    /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n    ///   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n    ///   same transaction.\n    /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n    ///   redemption would be accepted, regardless if the user has enough shares, etc.\n    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n    /// - MUST NOT revert.\n    ///\n    /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n    /// share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n    function previewRedeem(\n        uint256 shares\n    ) external returns (uint256 assets);\n\n    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n    /// @dev\n    /// - MUST emit the Withdraw event.\n    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n    ///   redeem execution, and are accounted for during redeem.\n    /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n    ///   not having enough shares, etc).\n    ///\n    /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n    /// Those methods should be performed separately.\n    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);\n}\n","Filename":"src/interfaces/vault/IERC4626.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\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","Filename":"src/interfaces/destinations/IDestinationAdapter.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        address token\n    ) external;\n\n    /// @notice remove a token from being snapshot\n    /// @param token the address of the token to remove\n    function removeRegisteredToken(\n        address token\n    ) 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(\n        address[] calldata tokensToSnapshot\n    ) 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","Filename":"src/interfaces/stats/IIncentivesPricingStats.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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","Filename":"src/interfaces/vault/IBaseAssetVault.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title AutopoolETH Router Base Interface\n * @notice A canonical router between AutopoolETHs\n *\n * The base router is a multicall style router inspired by Uniswap v3 with built-in features for permit,\n * WETH9 wrap/unwrap, and ERC20 token pulling/sweeping/approving. It includes methods for the four mutable\n * ERC4626 functions deposit/mint/withdraw/redeem as well.\n *\n * These can all be arbitrarily composed using the multicall functionality of the router.\n *\n * NOTE the router is capable of pulling any approved token from your wallet. This is only possible when\n * your address is msg.sender, but regardless be careful when interacting with the router or ERC4626 Vaults.\n * The router makes no special considerations for unique ERC20 implementations such as fee on transfer.\n * There are no built in protections for unexpected behavior beyond enforcing the minSharesOut is received.\n */\ninterface IAutopilotRouterBase {\n    /// @notice thrown when amount of assets received is below the min set by caller\n    error MinAmountError();\n\n    /// @notice thrown when amount of shares received is below the min set by caller\n    error MinSharesError();\n\n    /// @notice thrown when amount of assets received is above the max set by caller\n    error MaxAmountError();\n\n    /// @notice thrown when amount of shares received is above the max set by caller\n    error MaxSharesError();\n\n    /// @notice thrown when timestamp is too old\n    error TimestampTooOld();\n\n    /**\n     * @notice mint `shares` from an ERC4626 vault.\n     * @param vault The AutopoolETH to mint shares from.\n     * @param to The destination of ownership shares.\n     * @param shares The amount of shares to mint from `vault`.\n     * @param maxAmountIn The max amount of assets used to mint.\n     * @return amountIn the amount of assets used to mint by `to`.\n     * @dev throws MaxAmountError\n     */\n    function mint(\n        IAutopool vault,\n        address to,\n        uint256 shares,\n        uint256 maxAmountIn\n    ) external payable returns (uint256 amountIn);\n\n    /**\n     * @notice deposit `amount` to an ERC4626 vault.\n     * @param vault The AutopoolETH to deposit assets to.\n     * @param to The destination of ownership shares.\n     * @param amount The amount of assets to deposit to `vault`.\n     * @param minSharesOut The min amount of `vault` shares received by `to`.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MinSharesError\n     */\n    function deposit(\n        IAutopool vault,\n        address to,\n        uint256 amount,\n        uint256 minSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice withdraw `amount` from an ERC4626 vault.\n     * @param vault The AutopoolETH to withdraw assets from.\n     * @param to The destination of assets.\n     * @param amount The amount of assets to withdraw from vault.\n     * @param maxSharesOut The max amount of shares burned for assets requested.\n     * @return sharesOut the amount of shares received by `to`.\n     * @dev throws MaxSharesError\n     */\n    function withdraw(\n        IAutopool vault,\n        address to,\n        uint256 amount,\n        uint256 maxSharesOut\n    ) external payable returns (uint256 sharesOut);\n\n    /**\n     * @notice redeem `shares` shares from a AutopoolETH\n     * @param vault The AutopoolETH to redeem shares from.\n     * @param to The destination of assets.\n     * @param shares The amount of shares to redeem from vault.\n     * @param minAmountOut The min amount of assets received by `to`.\n     * @return amountOut the amount of assets received by `to`.\n     * @dev throws MinAmountError\n     */\n    function redeem(\n        IAutopool vault,\n        address to,\n        uint256 shares,\n        uint256 minAmountOut\n    ) external payable returns (uint256 amountOut);\n\n    /// @notice Stakes vault token to corresponding rewarder.\n    /// @param vault IERC20 instance of an Autopool to stake to.\n    /// @param maxAmount Maximum amount for user to stake.  Amount > balanceOf(user) will stake all present tokens.\n    /// @return staked Returns total amount staked.\n    function stakeVaultToken(IERC20 vault, uint256 maxAmount) external payable returns (uint256 staked);\n\n    /// @notice Unstakes vault token from corresponding rewarder.\n    /// @param vault IAutopool instance of the vault token to withdraw.\n    /// @param rewarder Rewarder to withdraw from.\n    /// @param maxAmount Amount of vault token to withdraw Amount > balanceOf(user) will withdraw all owned tokens.\n    /// @param claim Claiming rewards or not on unstaking.\n    /// @return withdrawn Amount of vault token withdrawn.\n    function withdrawVaultToken(\n        IAutopool vault,\n        IMainRewarder rewarder,\n        uint256 maxAmount,\n        bool claim\n    ) external payable returns (uint256 withdrawn);\n\n    /// @notice Claims rewards on user stake of vault token.\n    /// @param vault IAutopool instance of vault token to claim rewards for.\n    /// @param rewarder Rewarder to claim rewards from.\n    /// @param recipient Address to claim rewards for.\n    function claimAutopoolRewards(IAutopool vault, IMainRewarder rewarder, address recipient) external payable;\n\n    /// @notice Checks if timestamp is expired. Purpose is to check the execution deadline with the multicall.\n    /// @param timestamp Timestamp to check.\n    /// @dev throws TimestampTooOld. Payable to allow for multicall.\n    function expiration(\n        uint256 timestamp\n    ) external payable;\n}\n","Filename":"src/interfaces/vault/IAutopilotRouterBase.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nimport { IWETH9 } from \"src/interfaces/utils/IWETH9.sol\";\nimport { IAccToke } from \"src/interfaces/staking/IAccToke.sol\";\nimport { IAutopoolRegistry } from \"src/interfaces/vault/IAutopoolRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { ISwapRouter } from \"src/interfaces/swapper/ISwapRouter.sol\";\nimport { ICurveResolver } from \"src/interfaces/utils/ICurveResolver.sol\";\nimport { IAutopilotRouter } from \"src/interfaces/vault/IAutopilotRouter.sol\";\nimport { IAutopoolFactory } from \"src/interfaces/vault/IAutopoolFactory.sol\";\nimport { ISystemSecurity } from \"src/interfaces/security/ISystemSecurity.sol\";\nimport { IDestinationRegistry } from \"src/interfaces/destinations/IDestinationRegistry.sol\";\nimport { IRootPriceOracle } from \"src/interfaces/oracles/IRootPriceOracle.sol\";\nimport { IDestinationVaultRegistry } from \"src/interfaces/vault/IDestinationVaultRegistry.sol\";\nimport { IAccessController } from \"src/interfaces/security/IAccessController.sol\";\nimport { IStatsCalculatorRegistry } from \"src/interfaces/stats/IStatsCalculatorRegistry.sol\";\nimport { IAsyncSwapperRegistry } from \"src/interfaces/liquidation/IAsyncSwapperRegistry.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IIncentivesPricingStats } from \"src/interfaces/stats/IIncentivesPricingStats.sol\";\nimport { IMessageProxy } from \"src/interfaces/messageProxy/IMessageProxy.sol\";\n\n/// @notice Root most registry contract for the system\ninterface ISystemRegistry {\n    /// @notice Get the TOKE contract for the system\n    /// @return toke instance of TOKE used in the system\n    function toke() external view returns (IERC20Metadata);\n\n    /// @notice Get the referenced WETH contract for the system\n    /// @return weth contract pointer\n    function weth() external view returns (IWETH9);\n\n    /// @notice Get the AccToke staking contract\n    /// @return accToke instance of the accToke contract for the system\n    function accToke() external view returns (IAccToke);\n\n    /// @notice Get the AutopoolRegistry for this system\n    /// @return registry instance of the registry for this system\n    function autoPoolRegistry() external view returns (IAutopoolRegistry registry);\n\n    /// @notice Get the destination Vault registry for this system\n    /// @return registry instance of the registry for this system\n    function destinationVaultRegistry() external view returns (IDestinationVaultRegistry registry);\n\n    /// @notice Get the access Controller for this system\n    /// @return controller instance of the access controller for this system\n    function accessController() external view returns (IAccessController controller);\n\n    /// @notice Get the destination template registry for this system\n    /// @return registry instance of the registry for this system\n    function destinationTemplateRegistry() external view returns (IDestinationRegistry registry);\n\n    /// @notice Auto Pilot Router\n    /// @return router instance of the system\n    function autoPoolRouter() external view returns (IAutopilotRouter router);\n\n    /// @notice Vault factory lookup by type\n    /// @return vaultFactory instance of the vault factory for this vault type\n    function getAutopoolFactoryByType(\n        bytes32 vaultType\n    ) external view returns (IAutopoolFactory vaultFactory);\n\n    /// @notice Get the stats calculator registry for this system\n    /// @return registry instance of the registry for this system\n    function statsCalculatorRegistry() external view returns (IStatsCalculatorRegistry registry);\n\n    /// @notice Get the root price oracle for this system\n    /// @return oracle instance of the root price oracle for this system\n    function rootPriceOracle() external view returns (IRootPriceOracle oracle);\n\n    /// @notice Get the async swapper registry for this system\n    /// @return registry instance of the registry for this system\n    function asyncSwapperRegistry() external view returns (IAsyncSwapperRegistry registry);\n\n    /// @notice Get the swap router for this system\n    /// @return router instance of the swap router for this system\n    function swapRouter() external view returns (ISwapRouter router);\n\n    /// @notice Get the curve resolver for this system\n    /// @return resolver instance of the curve resolver for this system\n    function curveResolver() external view returns (ICurveResolver resolver);\n\n    /// @notice Verify if given address is registered as Reward Token\n    /// @param rewardToken token address to verify\n    /// @return bool that indicates true if token is registered and false if not\n    function isRewardToken(\n        address rewardToken\n    ) external view returns (bool);\n\n    /// @notice Get the system security instance for this system\n    /// @return security instance of system security for this system\n    function systemSecurity() external view returns (ISystemSecurity security);\n\n    /// @notice Get the Incentive Pricing Stats\n    /// @return incentivePricing the incentive pricing contract\n    function incentivePricing() external view returns (IIncentivesPricingStats);\n\n    /// @notice Get the Message Proxy\n    /// @return Message proxy contract\n    function messageProxy() external view returns (IMessageProxy);\n\n    /// @notice Get the receiving router contract.\n    /// @return Receiving router contract\n    function receivingRouter() external view returns (address);\n\n    /// @notice Check if an additional contract of type is valid in the system\n    /// @return True if the contract is a valid for the given type\n    function isValidContract(bytes32 contractType, address contractAddress) external view returns (bool);\n\n    /// @notice Returns the additional contract of the given type\n    /// @dev Revert if not set\n    function getUniqueContract(\n        bytes32 contractType\n    ) external view returns (address);\n\n    /// @notice Returns all unique contracts configured\n    function listUniqueContracts() external view returns (bytes32[] memory contractTypes, address[] memory addresses);\n\n    /// @notice Returns all additional contract types configured\n    function listAdditionalContractTypes() external view returns (bytes32[] memory);\n\n    /// @notice Returns configured additional contracts by type\n    /// @param contractType Type of contract to list\n    function listAdditionalContracts(\n        bytes32 contractType\n    ) external view returns (address[] memory);\n}\n","Filename":"src/interfaces/ISystemRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\n\ninterface IStrategy {\n    /* ******************************** */\n    /*      Events                      */\n    /* ******************************** */\n    // event DestinationVaultAdded(address destination);\n    // event DestinationVaultRemoved(address destination);\n    // event WithdrawalQueueSet(address[] destinations);\n    // event AddedToRemovalQueue(address destination);\n    // event RemovedFromRemovalQueue(address destination);\n\n    // error InvalidDestinationVault();\n\n    // error RebalanceFailed(string message);\n\n    /// @notice gets the list of supported destination vaults for the Autopool/Strategy\n    /// @return _destinations List of supported destination vaults\n    function getDestinations() external view returns (address[] memory _destinations);\n\n    /// @notice add supported destination vaults for the Autopool/Strategy\n    /// @param _destinations The list of destination vaults to add\n    function addDestinations(\n        address[] calldata _destinations\n    ) external;\n\n    /// @notice remove supported destination vaults for the Autopool/Strategy\n    /// @param _destinations The list of destination vaults to remove\n    function removeDestinations(\n        address[] calldata _destinations\n    ) external;\n\n    /// @param destinationIn The address / lp token of the destination vault that will increase\n    /// @param tokenIn The address of the underlyer token that will be provided by the swapper\n    /// @param amountIn The amount of the underlying LP tokens that will be received\n    /// @param destinationOut The address of the destination vault that will decrease\n    /// @param tokenOut The address of the underlyer token that will be received by the swapper\n    /// @param amountOut The amount of the tokenOut that will be received by the swapper\n    struct RebalanceParams {\n        address destinationIn;\n        address tokenIn;\n        uint256 amountIn;\n        address destinationOut;\n        address tokenOut;\n        uint256 amountOut;\n    }\n\n    /// @param destination The address / lp token of the destination vault\n    /// @param baseApr Base Apr is the yield generated by staking rewards\n    /// @param feeApr Yield for pool trading fees\n    /// @param incentiveApr Incentives for LP\n    /// @param safeTotalSupply Safe supply for LP tokens\n    /// @param priceReturn Return from price movement to & away from peg\n    /// @param maxDiscount Max discount to peg\n    /// @param maxPremium Max premium to peg\n    /// @param ownedShares Shares owned for this destination\n    /// @param compositeReturn Total return combined from the individual yield components\n    /// @param pricePerShare Price per share\n    struct SummaryStats {\n        address destination;\n        uint256 baseApr;\n        uint256 feeApr;\n        uint256 incentiveApr;\n        uint256 safeTotalSupply;\n        int256 priceReturn;\n        int256 maxDiscount;\n        int256 maxPremium;\n        uint256 ownedShares;\n        int256 compositeReturn;\n        uint256 pricePerShare;\n    }\n\n    /// @notice rebalance the Autopool from the tokenOut (decrease) to the tokenIn (increase)\n    /// This uses a flash loan to receive the tokenOut to reduce the working capital requirements of the swapper\n    /// @param receiver The contract receiving the tokens, needs to implement the\n    /// `onFlashLoan(address user, address token, uint256 amount, uint256 fee, bytes calldata)` interface\n    /// @param params Parameters by which to perform the rebalance\n    /// @param data A data parameter to be passed on to the `receiver` for any custom use\n    function flashRebalance(\n        IERC3156FlashBorrower receiver,\n        RebalanceParams calldata params,\n        bytes calldata data\n    ) external;\n}\n","Filename":"src/interfaces/strategy/IStrategy.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nstruct SwapParams {\n    /// @dev The address of the token to be sold.\n    address sellTokenAddress;\n    /// @dev The amount of tokens to be sold.\n    uint256 sellAmount;\n    /// @dev The address of the token to be bought.\n    address buyTokenAddress;\n    /// @dev The expected minimum amount of tokens to be bought.\n    uint256 buyAmount;\n    /// @dev Data payload to be used for complex swap operations.\n    bytes data;\n    /// @dev Extra data payload reserved for future development. This field allows for additional information\n    /// or functionality to be added without changing the struct and interface.\n    bytes extraData;\n    /// @dev Execution deadline in timestamp format\n    uint256 deadline;\n}\n\ninterface IAsyncSwapper {\n    error TokenAddressZero();\n    error SwapFailed();\n    error InsufficientBuyAmountReceived(uint256 buyTokenAmountReceived, uint256 buyAmount);\n    error InsufficientSellAmount();\n    error InsufficientBuyAmount();\n    error InsufficientBalance(uint256 balanceNeeded, uint256 balanceAvailable);\n\n    event Swapped(\n        address indexed sellTokenAddress,\n        address indexed buyTokenAddress,\n        uint256 sellAmount,\n        uint256 buyAmount,\n        uint256 buyTokenAmountReceived\n    );\n\n    /**\n     * @notice Swaps sellToken for buyToken\n     * @param swapParams Encoded swap data\n     * @return buyTokenAmountReceived The amount of buyToken received from the swap\n     */\n    function swap(\n        SwapParams memory swapParams\n    ) external payable returns (uint256 buyTokenAmountReceived);\n}\n","Filename":"src/interfaces/liquidation/IAsyncSwapper.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { AutopoolState } from \"src/vault/libs/AutopoolState.sol\";\n\nlibrary AutopoolFees {\n    using Math for uint256;\n    using AutopoolToken for AutopoolToken.TokenData;\n\n    /// @notice Profit denomination\n    uint256 public constant MAX_BPS_PROFIT = 1_000_000_000;\n\n    /// @notice 100% == 10000\n    uint256 public constant FEE_DIVISOR = 10_000;\n\n    /// @notice Max periodic fee, 10%.  100% = 10_000.\n    uint256 public constant MAX_PERIODIC_FEE_BPS = 1000;\n\n    uint256 public constant SECONDS_IN_YEAR = 365 days;\n\n    event FeeCollected(uint256 fees, address feeSink, uint256 mintedShares, uint256 profit, uint256 totalAssets);\n    event PeriodicFeeCollected(uint256 fees, address feeSink, uint256 mintedShares);\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n    event PeriodicFeeSet(uint256 newFee);\n    event PeriodicFeeSinkSet(address newPeriodicFeeSink);\n    event LastPeriodicFeeTakeSet(uint256 lastPeriodicFeeTake);\n    event RebalanceFeeHighWaterMarkEnabledSet(bool enabled);\n    event NewNavShareFeeMark(uint256 navPerShare, uint256 timestamp);\n    event NewTotalAssetsHighWatermark(uint256 assets, uint256 timestamp);\n    event StreamingFeeSet(uint256 newFee);\n    event FeeSinkSet(address newFeeSink);\n    event NewProfitUnlockTime(uint48 timeSeconds);\n\n    error InvalidFee(uint256 newFee);\n    error AlreadySet();\n    error DebtReportingStale();\n\n    /// @notice Returns the amount of unlocked profit shares that will be burned\n    function unlockedShares(\n        AutopoolState storage $\n    ) public view returns (uint256 shares) {\n        uint256 fullTime = $.profitUnlockSettings.fullProfitUnlockTime;\n        if (fullTime > block.timestamp) {\n            shares = $.profitUnlockSettings.profitUnlockRate\n                * (block.timestamp - $.profitUnlockSettings.lastProfitUnlockTime) / MAX_BPS_PROFIT;\n        } else if (fullTime != 0) {\n            shares = $.token.balances[address(this)];\n        }\n    }\n\n    function initializeFeeSettings(\n        AutopoolState storage $\n    ) external {\n        uint256 timestamp = block.timestamp;\n\n        // Stops fees from being able to be claimed before init timestamp.\n        $.feeSettings.lastPeriodicFeeTake = timestamp;\n        $.feeSettings.navPerShareLastFeeMark = FEE_DIVISOR;\n        $.feeSettings.navPerShareLastFeeMarkTimestamp = timestamp;\n\n        emit LastPeriodicFeeTakeSet(timestamp);\n    }\n\n    function burnUnlockedShares(\n        AutopoolState storage $\n    ) external {\n        uint256 shares = unlockedShares($);\n        if (shares == 0) {\n            return;\n        }\n        if ($.profitUnlockSettings.fullProfitUnlockTime > block.timestamp) {\n            $.profitUnlockSettings.lastProfitUnlockTime = uint48(block.timestamp);\n        }\n        $.token.burn(address(this), shares);\n    }\n\n    function _calculateEffectiveNavPerShareLastFeeMark(\n        IAutopool.AutopoolFeeSettings storage settings,\n        uint256 currentBlock,\n        uint256 currentNavPerShare,\n        uint256 aumCurrent\n    ) private view returns (uint256) {\n        uint256 workingHigh = settings.navPerShareLastFeeMark;\n\n        if (workingHigh == 0) {\n            // If we got 0, we shouldn't increase it\n            return 0;\n        }\n\n        if (!settings.rebalanceFeeHighWaterMarkEnabled) {\n            // No calculations or checks to do in this case\n            return workingHigh;\n        }\n\n        uint256 daysSinceLastFeeEarned = (currentBlock - settings.navPerShareLastFeeMarkTimestamp) / 60 / 60 / 24;\n\n        if (daysSinceLastFeeEarned > 600) {\n            return currentNavPerShare;\n        }\n        if (daysSinceLastFeeEarned > 60 && daysSinceLastFeeEarned <= 600) {\n            uint8 decimals = IAutopool(address(this)).decimals();\n\n            uint256 one = 10 ** decimals;\n            uint256 aumHighMark = settings.totalAssetsHighMark;\n\n            // AUM_min = min(AUM_high, AUM_current)\n            uint256 minAssets = aumCurrent < aumHighMark ? aumCurrent : aumHighMark;\n\n            // AUM_max = max(AUM_high, AUM_current);\n            uint256 maxAssets = aumCurrent > aumHighMark ? aumCurrent : aumHighMark;\n\n            /// 0.999 * (AUM_min / AUM_max)\n            // dividing by `one` because we need end up with a number in the 100's wei range\n            uint256 g1 = ((999 * minAssets * one) / (maxAssets * one));\n\n            /// 0.99 * (1 - AUM_min / AUM_max)\n            // dividing by `10 ** (decimals() - 1)` because we need to divide 100 out for our % and then\n            // we want to end up with a number in the 10's wei range\n            uint256 g2 = (99 * (one - (minAssets * one / maxAssets))) / 10 ** (decimals - 1);\n\n            uint256 gamma = g1 + g2;\n\n            uint256 daysDiff = daysSinceLastFeeEarned - 60;\n            for (uint256 i = 0; i < daysDiff / 25; ++i) {\n                // slither-disable-next-line divide-before-multiply\n                workingHigh = workingHigh * (gamma ** 25 / 1e72) / 1000;\n            }\n            // slither-disable-next-line weak-prng\n            for (uint256 i = 0; i < daysDiff % 25; ++i) {\n                // slither-disable-next-line divide-before-multiply\n                workingHigh = workingHigh * gamma / 1000;\n            }\n        }\n        return workingHigh;\n    }\n\n    function collectFees(\n        AutopoolState storage $,\n        uint256 totalAssets,\n        uint256 currentTotalSupply,\n        bool collectPeriodicFees\n    ) external returns (uint256) {\n        // If there's no supply then there should be no assets and so nothing\n        // to actually take fees on\n        // slither-disable-next-line incorrect-equality\n        if (currentTotalSupply == 0) {\n            return 0;\n        }\n\n        uint256 decimalPad = IAutopool(address(this)).decimalPad();\n\n        // slither-disable-start timestamp\n        if (collectPeriodicFees) {\n            address periodicFeeSink = $.feeSettings.periodicFeeSink;\n            uint256 periodicFeeBps = $.feeSettings.periodicFeeBps;\n            // If there is a periodic fee and fee sink set, take the fee.\n            if (periodicFeeBps > 0 && periodicFeeSink != address(0)) {\n                uint256 durationSinceLastPeriodicFeeTake = block.timestamp - $.feeSettings.lastPeriodicFeeTake;\n                uint256 timeAdjustedBps = durationSinceLastPeriodicFeeTake.mulDiv(\n                    periodicFeeBps * FEE_DIVISOR, SECONDS_IN_YEAR, Math.Rounding.Up\n                );\n\n                uint256 periodicShares =\n                    _collectPeriodicFees(periodicFeeSink, timeAdjustedBps, currentTotalSupply, totalAssets);\n\n                currentTotalSupply += periodicShares;\n                $.token.mint(periodicFeeSink, periodicShares);\n            }\n\n            // Needs to be kept up to date so if a fee is suddenly turned on a large part of assets do not get\n            // claimed as fees.\n            $.feeSettings.lastPeriodicFeeTake = block.timestamp;\n            emit LastPeriodicFeeTakeSet(block.timestamp);\n        }\n        // slither-disable-end timestamp\n        uint256 currentNavPerShare = (totalAssets * decimalPad * FEE_DIVISOR) / currentTotalSupply;\n\n        // If the high mark is disabled then this just returns the `navPerShareLastFeeMark`\n        // Otherwise, it'll check if it needs to decay\n        uint256 effectiveNavPerShareLastFeeMark =\n            _calculateEffectiveNavPerShareLastFeeMark($.feeSettings, block.timestamp, currentNavPerShare, totalAssets);\n\n        if (currentNavPerShare > effectiveNavPerShareLastFeeMark) {\n            // Even if we aren't going to take the fee (haven't set a sink)\n            // We still want to calculate so we can emit for off-chain analysis\n            uint256 profit = (currentNavPerShare - effectiveNavPerShareLastFeeMark).mulDiv(\n                currentTotalSupply, decimalPad, Math.Rounding.Up\n            );\n            uint256 fees = profit.mulDiv($.feeSettings.streamingFeeBps, (FEE_DIVISOR ** 2), Math.Rounding.Up);\n\n            if (fees > 0) {\n                currentTotalSupply = _mintStreamingFee($, fees, profit, currentTotalSupply, totalAssets);\n                currentNavPerShare = (totalAssets * decimalPad * FEE_DIVISOR) / currentTotalSupply;\n            }\n        }\n\n        // Two situations we're covering here\n        //   1. If the high mark is disabled then we just always need to know the last\n        //      time we evaluated fees so we can catch any run up. i.e. the `navPerShareLastFeeMark`\n        //      can go down\n        //   2. When the high mark is enabled, then we only want to set `navPerShareLastFeeMark`\n        //      when it is greater than the last time we captured fees (or would have)\n        if (currentNavPerShare >= effectiveNavPerShareLastFeeMark || !$.feeSettings.rebalanceFeeHighWaterMarkEnabled) {\n            $.feeSettings.navPerShareLastFeeMark = currentNavPerShare;\n            $.feeSettings.navPerShareLastFeeMarkTimestamp = block.timestamp;\n            emit NewNavShareFeeMark(currentNavPerShare, block.timestamp);\n        }\n\n        // Set our new high water mark for totalAssets, regardless if we took fees\n        if ($.feeSettings.totalAssetsHighMark < totalAssets) {\n            $.feeSettings.totalAssetsHighMark = totalAssets;\n            $.feeSettings.totalAssetsHighMarkTimestamp = block.timestamp;\n            emit NewTotalAssetsHighWatermark(\n                $.feeSettings.totalAssetsHighMark, $.feeSettings.totalAssetsHighMarkTimestamp\n            );\n        }\n\n        return currentTotalSupply;\n    }\n\n    function _mintStreamingFee(\n        AutopoolState storage $,\n        uint256 fees,\n        uint256 profit,\n        uint256 currentTotalSupply,\n        uint256 totalAssets\n    ) private returns (uint256) {\n        address sink = $.feeSettings.feeSink;\n        if (sink == address(0)) {\n            return currentTotalSupply;\n        }\n\n        uint256 streamingFeeShares =\n            _calculateSharesToMintFeeCollection($.feeSettings.streamingFeeBps, profit, totalAssets, currentTotalSupply);\n        $.token.mint(sink, streamingFeeShares);\n        currentTotalSupply += streamingFeeShares;\n\n        emit Deposit(address(this), sink, 0, streamingFeeShares);\n        emit FeeCollected(fees, sink, streamingFeeShares, profit, totalAssets);\n\n        return currentTotalSupply;\n    }\n\n    /// @dev Collects periodic fees.\n    function _collectPeriodicFees(\n        address periodicSink,\n        uint256 timeAdjustedFeeBps,\n        uint256 currentTotalSupply,\n        uint256 totalAssets\n    ) private returns (uint256 newShares) {\n        newShares =\n            _calculateSharesToMintFeeCollection(timeAdjustedFeeBps, totalAssets, totalAssets, currentTotalSupply);\n\n        // Fee in assets that we are taking.\n        uint256 fees = (timeAdjustedFeeBps * totalAssets / FEE_DIVISOR).ceilDiv(FEE_DIVISOR);\n        emit Deposit(address(this), periodicSink, 0, newShares);\n        emit PeriodicFeeCollected(fees, periodicSink, newShares);\n\n        return newShares;\n    }\n\n    function _calculateSharesToMintFeeCollection(\n        uint256 feeBps,\n        uint256 amountForFee,\n        uint256 totalAssets,\n        uint256 totalSupply\n    ) private pure returns (uint256 toMint) {\n        // Gas savings, this is used twice.\n        uint256 feeTotalAssets = feeBps * amountForFee / FEE_DIVISOR;\n\n        // Separate from other mints as normal share mint is round down\n        // Mints shares taking into account the dilution so we end up with the expected amount\n        // `feeBps` is padded by FEE_DIVISOR when taking periodic fee\n        // `amountForFee` is padded by FEE_DIVISOR when taking streaming fee\n        toMint =\n            Math.mulDiv(feeTotalAssets, totalSupply, (totalAssets * FEE_DIVISOR) - (feeTotalAssets), Math.Rounding.Up);\n    }\n\n    /// @dev If set to 0, existing shares will unlock immediately and increase nav/share. This is intentional\n    function setProfitUnlockPeriod(AutopoolState storage $, uint48 newUnlockPeriodInSeconds) external {\n        $.profitUnlockSettings.unlockPeriodInSeconds = newUnlockPeriodInSeconds;\n\n        // If we are turning off the unlock, setting it to 0, then\n        // unlock all existing shares\n        if (newUnlockPeriodInSeconds == 0) {\n            uint256 currentShares = $.token.balances[address(this)];\n            if (currentShares > 0) {\n                $.profitUnlockSettings.lastProfitUnlockTime = uint48(block.timestamp);\n                $.token.burn(address(this), currentShares);\n            }\n\n            // Reset vars so old values aren't used during a subsequent lockup\n            $.profitUnlockSettings.fullProfitUnlockTime = 0;\n            $.profitUnlockSettings.profitUnlockRate = 0;\n        }\n\n        emit NewProfitUnlockTime(newUnlockPeriodInSeconds);\n    }\n\n    function calculateProfitLocking(\n        IAutopool.ProfitUnlockSettings storage settings,\n        AutopoolToken.TokenData storage token,\n        uint256 feeShares,\n        uint256 newTotalAssets,\n        uint256 startTotalAssets,\n        uint256 startTotalSupply,\n        uint256 previousLockShares\n    ) external returns (uint256) {\n        uint256 unlockPeriod = settings.unlockPeriodInSeconds;\n\n        // If there were existing shares and we set the unlock period to 0 they are immediately unlocked\n        // so we don't have to worry about existing shares here. And if the period is 0 then we\n        // won't be locking any new shares\n        if (unlockPeriod == 0 || startTotalAssets == 0) {\n            return startTotalSupply;\n        }\n\n        uint256 newLockShares = 0;\n        uint256 previousLockToBurn = 0;\n        uint256 effectiveTs = startTotalSupply;\n\n        // The total supply we would need to not see a change in nav/share\n        uint256 targetTotalSupply = newTotalAssets * (effectiveTs - feeShares) / startTotalAssets;\n\n        if (effectiveTs > targetTotalSupply) {\n            // Our actual total supply is greater than our target.\n            // This means we would see a decrease in nav/share\n            // See if we can burn any profit shares to offset that\n            if (previousLockShares > 0) {\n                uint256 diff = effectiveTs - targetTotalSupply;\n                if (previousLockShares >= diff) {\n                    previousLockToBurn = diff;\n                    effectiveTs -= diff;\n                } else {\n                    previousLockToBurn = previousLockShares;\n                    effectiveTs -= previousLockShares;\n                }\n            }\n        }\n\n        if (targetTotalSupply > effectiveTs) {\n            // Our actual total supply is less than our target.\n            // This means we would see an increase in nav/share (due to gains) which we can't allow\n            // We need to mint shares to the vault to offset\n            newLockShares = targetTotalSupply - effectiveTs;\n            effectiveTs += newLockShares;\n        }\n\n        // We know how many shares should be locked at this point\n        // Mint or burn what we need to match if necessary\n        uint256 totalLockShares = previousLockShares - previousLockToBurn + newLockShares;\n        if (totalLockShares > previousLockShares) {\n            uint256 mintAmount = totalLockShares - previousLockShares;\n            token.mint(address(this), mintAmount);\n            startTotalSupply += mintAmount;\n        } else if (totalLockShares < previousLockShares) {\n            uint256 burnAmount = previousLockShares - totalLockShares;\n            token.burn(address(this), burnAmount);\n            startTotalSupply -= burnAmount;\n        }\n\n        // If we're going to end up with no profit shares, zero the rate\n        // We don't need to 0 the other timing vars if we just zero the rate\n        if (totalLockShares == 0) {\n            settings.profitUnlockRate = 0;\n        }\n\n        // We have shares and they are going to unlocked later\n        if (totalLockShares > 0 && unlockPeriod > 0) {\n            _updateProfitUnlockTimings(\n                settings, unlockPeriod, previousLockToBurn, previousLockShares, newLockShares, totalLockShares\n            );\n        }\n\n        return startTotalSupply;\n    }\n\n    function _updateProfitUnlockTimings(\n        IAutopool.ProfitUnlockSettings storage settings,\n        uint256 unlockPeriod,\n        uint256 previousLockToBurn,\n        uint256 previousLockShares,\n        uint256 newLockShares,\n        uint256 totalLockShares\n    ) private {\n        uint256 previousLockTime;\n        uint256 fullUnlockTime = settings.fullProfitUnlockTime;\n\n        // Determine how much time is left for the remaining previous profit shares\n        if (fullUnlockTime > block.timestamp) {\n            previousLockTime = (previousLockShares - previousLockToBurn) * (fullUnlockTime - block.timestamp);\n        }\n\n        // Amount of time it will take to unlock all shares, weighted avg over current and new shares\n        uint256 newUnlockPeriod = (previousLockTime + newLockShares * unlockPeriod) / totalLockShares;\n\n        if (newUnlockPeriod == 0) {\n            settings.profitUnlockRate = 0;\n        } else {\n            // Rate at which totalLockShares will unlock\n            settings.profitUnlockRate = totalLockShares * MAX_BPS_PROFIT / newUnlockPeriod;\n        }\n\n        // Time the full of amount of totalLockShares will be unlocked\n        settings.fullProfitUnlockTime = uint48(block.timestamp + newUnlockPeriod);\n        settings.lastProfitUnlockTime = uint48(block.timestamp);\n    }\n\n    /// @notice Enable or disable the high water mark on the rebalance fee\n    /// @dev Will revert if set to the same value\n    function setRebalanceFeeHighWaterMarkEnabled(\n        IAutopool.AutopoolFeeSettings storage feeSettings,\n        bool enabled\n    ) external {\n        if (feeSettings.rebalanceFeeHighWaterMarkEnabled == enabled) {\n            revert AlreadySet();\n        }\n\n        feeSettings.rebalanceFeeHighWaterMarkEnabled = enabled;\n\n        emit RebalanceFeeHighWaterMarkEnabledSet(enabled);\n    }\n\n    /// @notice Set the fee that will be taken when profit is realized\n    /// @dev Resets the high water to current value\n    /// @param $ Storage related to the calling Autopool.\n    /// @param fee Percent. 100% == 10000\n    /// @param oldestDebtReporting Debt reporting timestamp to be checked\n    function setStreamingFeeBps(AutopoolState storage $, uint256 fee, uint256 oldestDebtReporting) external {\n        if (fee >= FEE_DIVISOR) {\n            revert InvalidFee(fee);\n        }\n\n        _checkLastDebtReportingTime(oldestDebtReporting, $.debtReportQueue.size);\n\n        IAutopool vault = IAutopool(address(this));\n\n        $.feeSettings.streamingFeeBps = fee;\n\n        // Set the high mark when we change the fee so we aren't able to go farther back in\n        // time than one debt reporting and claim fee's against past profits\n        uint256 ts = vault.totalSupply();\n        if (ts > 0) {\n            uint256 ta = vault.totalAssets();\n            if (ta > 0) {\n                $.feeSettings.navPerShareLastFeeMark = (ta * vault.decimalPad() * FEE_DIVISOR) / ts;\n            } else {\n                $.feeSettings.navPerShareLastFeeMark = FEE_DIVISOR;\n            }\n        }\n        emit StreamingFeeSet(fee);\n    }\n\n    /// @notice Set the periodic fee taken.\n    /// @dev Zero is allowed, no fee taken.\n    /// @param $ Storage related to the calling Autopool.\n    /// @param fee Fee to update periodic fee to.\n    /// @param oldestDebtReporting Debt reporting timestamp to be checked\n    function setPeriodicFeeBps(AutopoolState storage $, uint256 fee, uint256 oldestDebtReporting) external {\n        if (fee > MAX_PERIODIC_FEE_BPS) {\n            revert InvalidFee(fee);\n        }\n\n        _checkLastDebtReportingTime(oldestDebtReporting, $.debtReportQueue.size);\n\n        // Fee checked to fit into uint16 above, able to be wrapped without safe cast here.\n        emit PeriodicFeeSet(fee);\n        $.feeSettings.periodicFeeBps = uint16(fee);\n    }\n\n    /// @notice Set the address that will receive fees\n    /// @param newFeeSink Address that will receive fees\n    function setFeeSink(IAutopool.AutopoolFeeSettings storage feeSettings, address newFeeSink) external {\n        emit FeeSinkSet(newFeeSink);\n\n        // Zero is valid. One way to disable taking fees\n        // slither-disable-next-line missing-zero-check\n        feeSettings.feeSink = newFeeSink;\n    }\n\n    /// @notice Sets the address that will receive periodic fees.\n    /// @dev Zero address allowable.  Disables fees.\n    /// @param newPeriodicFeeSink New periodic fee address.\n    function setPeriodicFeeSink(\n        IAutopool.AutopoolFeeSettings storage feeSettings,\n        address newPeriodicFeeSink\n    ) external {\n        emit PeriodicFeeSinkSet(newPeriodicFeeSink);\n\n        // slither-disable-next-line missing-zero-check\n        feeSettings.periodicFeeSink = newPeriodicFeeSink;\n    }\n\n    function _checkLastDebtReportingTime(uint256 oldestDebtReporting, uint256 debtReportQueueLength) private view {\n        if (debtReportQueueLength > 0 && oldestDebtReporting < block.timestamp - 10 minutes) {\n            revert DebtReportingStale();\n        }\n    }\n}\n","Filename":"src/vault/libs/AutopoolFees.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\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(\n        address token\n    ) external returns (uint256 price);\n\n    /// @notice Returns a spot price for the provided token in ETH, utilizing specified liquidity pool\n    /// @param token token to get the spot price of\n    /// @param pool liquidity pool to be used for price determination\n    /// @return price the spot price of the token in ETH based on the provided pool\n    function getSpotPriceInEth(address token, address pool) external returns (uint256);\n\n    /// @notice Returns a price for base token in quote token.\n    /// @dev Requires both tokens to be registered.\n    /// @param base Address of base token.\n    /// @param quote Address of quote token.\n    /// @return price Price of the base token in quote token.\n    function getPriceInQuote(address base, address quote) external returns (uint256 price);\n\n    /// @notice Retrieve the price of LP token based on the reserves\n    /// @param lpToken LP token to get the price of\n    /// @param pool liquidity pool to be used for price determination\n    /// @param quoteToken token to quote the price in\n    function getRangePricesLP(\n        address lpToken,\n        address pool,\n        address quoteToken\n    ) external returns (uint256 spotPriceInQuote, uint256 safePriceInQuote, bool isSpotSafe);\n\n    /// @notice Returns floor or ceiling price of the supplied lp token in terms of requested quote.\n    /// @dev  Floor price: the minimum price among all the spot prices and safe prices of the tokens in the pool.\n    ///       Ceiling price: the maximum price among all the spot prices and safe prices of the tokens in the pool.\n    /// @param pool Address of pool to get spot pricing from.\n    /// @param lpToken Address of the lp token to price.\n    /// @param inQuote Address of desired quote token.\n    /// @param ceiling Bool indicating whether to get floor or ceiling price.\n    /// @return floorOrCeilingPerLpToken Floor or ceiling price of the lp token.\n    function getFloorCeilingPrice(\n        address pool,\n        address lpToken,\n        address inQuote,\n        bool ceiling\n    ) external returns (uint256 floorOrCeilingPerLpToken);\n\n    function getFloorPrice(address, address, address) external returns (uint256 price);\n\n    function getCeilingPrice(address, address, address) external returns (uint256 price);\n}\n","Filename":"src/interfaces/oracles/IRootPriceOracle.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2024 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\n\n/**\n *  @title Validates and distributes Vault token rewards based on the\n *  the signed and submitted payloads\n */\ninterface IRewards {\n    struct Recipient {\n        uint256 chainId;\n        uint256 cycle;\n        address wallet;\n        uint256 amount;\n    }\n\n    event SignerSet(address newSigner);\n    event Claimed(uint256 cycle, address recipient, uint256 amount);\n\n    /// @notice Get the underlying token rewards are paid in\n    /// @return Token address\n    function rewardToken() external view returns (IERC20);\n\n    /// @notice Get the current payload signer;\n    /// @return Signer address\n    function rewardsSigner() external view returns (address);\n\n    /// @notice Check the amount an account has already claimed\n    /// @param account Account to check\n    /// @return Amount already claimed\n    function claimedAmounts(\n        address account\n    ) external view returns (uint256);\n\n    /// @notice Get the amount that is claimable based on the provided payload\n    /// @param recipient Published rewards payload\n    /// @return Amount claimable if the payload is signed\n    function getClaimableAmount(\n        Recipient calldata recipient\n    ) external view returns (uint256);\n\n    /// @notice Change the signer used to validate payloads\n    /// @param newSigner The new address that will be signing rewards payloads\n    function setSigner(\n        address newSigner\n    ) external;\n\n    /// @notice Claim your rewards\n    /// @param recipient Published rewards payload\n    /// @param v v component of the payload signature\n    /// @param r r component of the payload signature\n    /// @param s s component of the payload signature\n    function claim(Recipient calldata recipient, uint8 v, bytes32 r, bytes32 s) external returns (uint256);\n\n    /// @notice Claim rewards on behalf of another account , invoked primarily by the router\n    /// @param recipient Published rewards payload\n    /// @param v v component of the payload signature\n    /// @param r r component of the payload signature\n    /// @param s s component of the payload signature\n    function claimFor(Recipient calldata recipient, uint8 v, bytes32 r, bytes32 s) external returns (uint256);\n\n    /// @notice Generate the hash of the payload\n    /// @param recipient Published rewards payload\n    /// @return Hash of the payload\n    function genHash(\n        Recipient memory recipient\n    ) external view returns (bytes32);\n}\n","Filename":"src/interfaces/rewarders/IRewards.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\n/// @title Send messages to our systems on other chains\ninterface IMessageProxy {\n    function sendMessage(bytes32 messageType, bytes memory message) external;\n}\n","Filename":"src/interfaces/messageProxy/IMessageProxy.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2025 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { Initializable } from \"openzeppelin-contracts/proxy/utils/Initializable.sol\";\n\n/// @title Copy of OZ's ReentrancyGuard with a read only variant added\nabstract contract NonReentrantUpgradeable is Initializable {\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    uint256 private _status;\n\n    error ReentrancyGuardReentrantCall();\n\n    function initialize() internal onlyInitializing {\n        _status = NOT_ENTERED;\n    }\n\n    modifier nonReentrantReadOnly() {\n        if (_status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n        _;\n    }\n\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        if (_status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        _status = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = NOT_ENTERED;\n    }\n}\n","Filename":"src/utils/NonReentrantUpgradeable.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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(\n        bytes32[] calldata destinationTypes\n    ) 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(\n        bytes32 destination\n    ) external returns (IDestinationAdapter);\n\n    /**\n     * @notice Adds given destination types to the whitelist\n     * @param destinationTypes Types to whitelist\n     */\n    function addToWhitelist(\n        bytes32[] calldata destinationTypes\n    ) external;\n\n    /**\n     * @notice Removes given pre-whitelisted destination types\n     * @param destinationTypes Ones from the destination type whitelist\n     */\n    function removeFromWhitelist(\n        bytes32[] calldata destinationTypes\n    ) external;\n\n    /**\n     * @notice Checks if the given destination type is whitelisted\n     * @param destinationType Type to verify\n     */\n    function isWhitelistedDestination(\n        bytes32 destinationType\n    ) external view returns (bool);\n}\n","Filename":"src/interfaces/destinations/IDestinationRegistry.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\n\ninterface IAutopoolStrategy {\n    enum RebalanceDirection {\n        In,\n        Out\n    }\n\n    /// @notice verify that a rebalance (swap between destinations) meets all the strategy constraints\n    /// @dev Signature identical to IStrategy.verifyRebalance\n    function verifyRebalance(\n        IStrategy.RebalanceParams memory,\n        IStrategy.SummaryStats memory\n    ) external returns (bool, string memory message);\n\n    /// @notice called by the Autopool when NAV is updated\n    /// @dev can only be called by the strategy's registered Autopool\n    /// @param navPerShare The navPerShare to record\n    function navUpdate(\n        uint256 navPerShare\n    ) external;\n\n    /// @notice called by the Autopool when a rebalance is completed\n    /// @dev can only be called by the strategy's registered Autopool\n    /// @param rebalanceParams The parameters for the rebalance that was executed\n    function rebalanceSuccessfullyExecuted(\n        IStrategy.RebalanceParams memory rebalanceParams\n    ) external;\n\n    /// @notice called by the Autopool during rebalance process\n    /// @param rebalanceParams The parameters for the rebalance that was executed\n    function getRebalanceOutSummaryStats(\n        IStrategy.RebalanceParams memory rebalanceParams\n    ) external returns (IStrategy.SummaryStats memory outSummary);\n\n    /// @notice Returns stats for a given destination\n    /// @dev Used to evaluate the current state of the destinations and decide best action\n    /// @param destAddress Destination address. Can be a DestinationVault or the AutoPool\n    /// @param direction Direction to evaluate the stats at\n    /// @param amount Amount to evaluate the stats at\n    function getDestinationSummaryStats(\n        address destAddress,\n        IAutopoolStrategy.RebalanceDirection direction,\n        uint256 amount\n    ) external returns (IStrategy.SummaryStats memory);\n\n    /// @notice Returns all hooks registered on strategy\n    /// @dev Will return zero addresses for unregistered hooks\n    /// @return hooks Array of hook addresses\n    function getHooks() external view returns (address[] memory hooks);\n\n    /// @notice the number of days to pause rebalancing due to NAV decay\n    function pauseRebalancePeriodInDays() external view returns (uint16);\n\n    /// @notice the number of seconds gap between consecutive rebalances\n    function rebalanceTimeGapInSeconds() external view returns (uint256);\n\n    /// @notice destinations trading a premium above maxPremium will be blocked from new capital deployments\n    function maxPremium() external view returns (int256); // 100% = 1e18\n\n    /// @notice destinations trading a discount above maxDiscount will be blocked from new capital deployments\n    function maxDiscount() external view returns (int256); // 100% = 1e18\n\n    /// @notice the allowed staleness of stats data before a revert occurs\n    function staleDataToleranceInSeconds() external view returns (uint40);\n\n    /// @notice the swap cost offset period to initialize the strategy with\n    function swapCostOffsetInitInDays() external view returns (uint16);\n\n    /// @notice the number of violations required to trigger a tightening of the swap cost offset period (1 to 10)\n    function swapCostOffsetTightenThresholdInViolations() external view returns (uint16);\n\n    /// @notice the number of days to decrease the swap offset period for each tightening step\n    function swapCostOffsetTightenStepInDays() external view returns (uint16);\n\n    /// @notice the number of days since a rebalance required to trigger a relaxing of the swap cost offset period\n    function swapCostOffsetRelaxThresholdInDays() external view returns (uint16);\n\n    /// @notice the number of days to increase the swap offset period for each relaxing step\n    function swapCostOffsetRelaxStepInDays() external view returns (uint16);\n\n    // slither-disable-start similar-names\n    /// @notice the maximum the swap cost offset period can reach. This is the loosest the strategy will be\n    function swapCostOffsetMaxInDays() external view returns (uint16);\n\n    /// @notice the minimum the swap cost offset period can reach. This is the most conservative the strategy will be\n    function swapCostOffsetMinInDays() external view returns (uint16);\n\n    /// @notice the number of days for the first NAV decay comparison (e.g., 30 days)\n    function navLookback1InDays() external view returns (uint8);\n\n    /// @notice the number of days for the second NAV decay comparison (e.g., 60 days)\n    function navLookback2InDays() external view returns (uint8);\n\n    /// @notice the number of days for the third NAV decay comparison (e.g., 90 days)\n    function navLookback3InDays() external view returns (uint8);\n    // slither-disable-end similar-names\n\n    /// @notice the maximum slippage that is allowed for a normal rebalance\n    function maxNormalOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when a destination is trimmed due to constraint violations\n    /// recommend setting this higher than maxNormalOperationSlippage\n    function maxTrimOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when a destinationVault has been shutdown\n    /// shutdown for a vault is abnormal and means there is an issue at that destination\n    /// recommend setting this higher than maxNormalOperationSlippage\n    function maxEmergencyOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum amount of slippage to allow when the Autopool has been shutdown\n    function maxShutdownOperationSlippage() external view returns (uint256); // 100% = 1e18\n\n    /// @notice the maximum discount used for price return\n    function maxAllowedDiscount() external view returns (int256); // 18 precision\n\n    /// @notice model weight used for LSTs base yield, 1e6 is the highest\n    function weightBase() external view returns (uint256);\n\n    /// @notice model weight used for DEX fee yield, 1e6 is the highest\n    function weightFee() external view returns (uint256);\n\n    /// @notice model weight used for incentive yield\n    function weightIncentive() external view returns (uint256);\n\n    /// @notice model weight applied to an LST discount when exiting the position\n    function weightPriceDiscountExit() external view returns (int256);\n\n    /// @notice model weight applied to an LST discount when entering the position\n    function weightPriceDiscountEnter() external view returns (int256);\n\n    /// @notice model weight applied to an LST premium when entering or exiting the position\n    function weightPricePremium() external view returns (int256);\n\n    /// @notice initial value of the swap cost offset to use\n    function swapCostOffsetInit() external view returns (uint16);\n\n    /// @notice initial lst price gap tolerance\n    function defaultLstPriceGapTolerance() external view returns (uint256);\n}\n","Filename":"src/interfaces/strategy/IAutopoolStrategy.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\ninterface IAutopoolFactory {\n    ///////////////////////////////////////////////////////////////////\n    //                        Vault Creation\n    ///////////////////////////////////////////////////////////////////\n\n    /**\n     * @notice Spin up a new AutopoolETH\n     * @param strategy Strategy template address\n     * @param symbolSuffix Symbol suffix of the new token\n     * @param descPrefix Description prefix of the new token\n     * @param salt Vault creation salt\n     * @param extraParams Any extra data needed for the vault\n     */\n    function createVault(\n        address strategy,\n        string memory symbolSuffix,\n        string memory descPrefix,\n        bytes32 salt,\n        bytes calldata extraParams\n    ) external payable returns (address newVaultAddress);\n\n    function addStrategyTemplate(\n        address strategyTemplate\n    ) external;\n\n    function removeStrategyTemplate(\n        address strategyTemplate\n    ) external;\n\n    /// @notice Returns the template used to create Autopools\n    function template() external returns (address);\n}\n","Filename":"src/interfaces/vault/IAutopoolFactory.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\n// solhint-disable no-inline-assembly\n\n/// @notice Read and write to persistent storage at a fraction of the cost.\n/// @notice Copied at\n/// https://github.com/Vectorized/solady/blob/ccaed15a964891aa729c9d22670304e88584a480/src/utils/SSTORE2.sol\n/// @notice Pragma updated from 8.4 to 8.24\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)\n/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)\n/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)\n/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)\nlibrary SSTORE2 {\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         CONSTANTS                          */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev The proxy initialization code.\n    uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;\n\n    /// @dev Hash of the `_CREATE3_PROXY_INITCODE`.\n    /// Equivalent to `keccak256(abi.encodePacked(hex\"67363d3d37363d34f03d5260086018f3\"))`.\n    bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =\n        0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                        CUSTOM ERRORS                       */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Unable to deploy the storage contract.\n    error DeploymentFailed();\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         WRITE LOGIC                        */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.\n    function write(\n        bytes memory data\n    ) internal returns (address pointer) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.\n            /**\n             * ---------------------------------------------------+\n             * Opcode | Mnemonic       | Stack     | Memory       |\n             * ---------------------------------------------------|\n             * 61 l   | PUSH2 l        | l         |              |\n             * 80     | DUP1           | l l       |              |\n             * 60 0xa | PUSH1 0xa      | 0xa l l   |              |\n             * 3D     | RETURNDATASIZE | 0 0xa l l |              |\n             * 39     | CODECOPY       | l         | [0..l): code |\n             * 3D     | RETURNDATASIZE | 0 l       | [0..l): code |\n             * F3     | RETURN         |           | [0..l): code |\n             * 00     | STOP           |           |              |\n             * ---------------------------------------------------+\n             * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.\n             * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.\n             */\n            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.\n            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))\n            // Deploy a new contract with the generated creation code.\n            pointer := create(0, add(data, 0x15), add(n, 0xb))\n            if iszero(pointer) {\n                mstore(0x00, 0x30116425) // `DeploymentFailed()`.\n                revert(0x1c, 0x04)\n            }\n            mstore(data, n) // Restore the length of `data`.\n        }\n    }\n\n    /// @dev Writes `data` into the bytecode of a storage contract with `salt`\n    /// and returns its normal CREATE2 deterministic address.\n    function writeCounterfactual(bytes memory data, bytes32 salt) internal returns (address pointer) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(data)\n            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.\n            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))\n            // Deploy a new contract with the generated creation code.\n            pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)\n            if iszero(pointer) {\n                mstore(0x00, 0x30116425) // `DeploymentFailed()`.\n                revert(0x1c, 0x04)\n            }\n            mstore(data, n) // Restore the length of `data`.\n        }\n    }\n\n    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.\n    /// This uses the so-called \"CREATE3\" workflow,\n    /// which means that `pointer` is agnostic to `data, and only depends on `salt`.\n    function writeDeterministic(bytes memory data, bytes32 salt) internal returns (address pointer) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(data)\n            mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.\n            let proxy := create2(0, 0x10, 0x10, salt)\n            if iszero(proxy) {\n                mstore(0x00, 0x30116425) // `DeploymentFailed()`.\n                revert(0x1c, 0x04)\n            }\n            mstore(0x14, proxy) // Store the proxy's address.\n            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).\n            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).\n            mstore(0x00, 0xd694)\n            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).\n            pointer := keccak256(0x1e, 0x17)\n\n            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.\n            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))\n            if iszero(\n                mul( // The arguments of `mul` are evaluated last to first.\n                extcodesize(pointer), call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00))\n            ) {\n                mstore(0x00, 0x30116425) // `DeploymentFailed()`.\n                revert(0x1c, 0x04)\n            }\n            mstore(data, n) // Restore the length of `data`.\n        }\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                    ADDRESS CALCULATIONS                    */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Returns the initialization code hash of the storage contract for `data`.\n    /// Used for mining vanity addresses with create2crunch.\n    function initCodeHash(\n        bytes memory data\n    ) internal pure returns (bytes32 hash) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let n := mload(data)\n            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.\n            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))\n            mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))\n            hash := keccak256(add(data, 0x15), add(n, 0xb))\n            mstore(data, n) // Restore the length of `data`.\n        }\n    }\n\n    /// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`\n    function predictCounterfactualAddress(bytes memory data, bytes32 salt) internal view returns (address pointer) {\n        pointer = predictCounterfactualAddress(data, salt, address(this));\n    }\n\n    /// @dev Returns the CREATE2 address of the storage contract for `data`\n    /// deployed with `salt` by `deployer`.\n    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.\n    function predictCounterfactualAddress(\n        bytes memory data,\n        bytes32 salt,\n        address deployer\n    ) internal pure returns (address predicted) {\n        bytes32 hash = initCodeHash(data);\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Compute and store the bytecode hash.\n            mstore8(0x00, 0xff) // Write the prefix.\n            mstore(0x35, hash)\n            mstore(0x01, shl(96, deployer))\n            mstore(0x15, salt)\n            predicted := keccak256(0x00, 0x55)\n            // Restore the part of the free memory pointer that has been overwritten.\n            mstore(0x35, 0)\n        }\n    }\n\n    /// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.\n    function predictDeterministicAddress(\n        bytes32 salt\n    ) internal view returns (address pointer) {\n        pointer = predictDeterministicAddress(salt, address(this));\n    }\n\n    /// @dev Returns the \"CREATE3\" deterministic address for `salt` with `deployer`.\n    function predictDeterministicAddress(bytes32 salt, address deployer) internal pure returns (address pointer) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let m := mload(0x40) // Cache the free memory pointer.\n            mstore(0x00, deployer) // Store `deployer`.\n            mstore8(0x0b, 0xff) // Store the prefix.\n            mstore(0x20, salt) // Store the salt.\n            mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.\n\n            mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.\n            mstore(0x40, m) // Restore the free memory pointer.\n            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).\n            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).\n            mstore(0x00, 0xd694)\n            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).\n            pointer := keccak256(0x1e, 0x17)\n        }\n    }\n\n    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n    /*                         READ LOGIC                         */\n    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n    /// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.\n    function read(\n        address pointer\n    ) internal view returns (bytes memory data) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            data := mload(0x40)\n            let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))\n            extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))\n            mstore(data, n) // Store the length.\n            mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.\n        }\n    }\n\n    /// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.\n    function read(address pointer, uint256 start) internal view returns (bytes memory data) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            data := mload(0x40)\n            let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))\n            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))\n            extcodecopy(pointer, add(data, 0x1f), start, add(l, 0x21))\n            mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.\n            mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.\n        }\n    }\n\n    /// @dev Returns a slice of the data on `pointer` from `start` to `end`.\n    /// `start` and `end` will be clamped to the range `[0, args.length]`.\n    /// The `pointer` MUST be deployed via the SSTORE2 write functions.\n    /// Otherwise, the behavior is undefined.\n    /// Out-of-gas reverts if `pointer` does not have any code.\n    function read(address pointer, uint256 start, uint256 end) internal view returns (bytes memory data) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            data := mload(0x40)\n            if iszero(lt(end, 0xffff)) { end := 0xffff }\n            let d := mul(sub(end, start), lt(start, end))\n            extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))\n            if iszero(and(0xff, mload(add(data, d)))) {\n                let n := sub(extcodesize(pointer), 0x01)\n                returndatacopy(returndatasize(), returndatasize(), shr(40, n))\n                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))\n            }\n            mstore(data, d) // Store the length.\n            mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.\n            mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.\n        }\n    }\n}\n","Filename":"src/external/solady/SSTORE2.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\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","Filename":"src/libs/LibAdapter.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\n/// @notice Creates and registers Destination Vaults for the system\ninterface IDestinationVaultFactory is ISystemComponent {\n    /// @notice Creates a vault of the specified type\n    /// @dev vaultType will be bytes32 encoded and checked that a template is registered\n    /// @param vaultType human readable key of the vault template\n    /// @param baseAsset Base asset of the system. WETH/USDC/etc\n    /// @param underlyer Underlying asset the vault will wrap\n    /// @param incentiveCalculator Incentive calculator of the vault\n    /// @param additionalTrackedTokens Any tokens in addition to base and underlyer that should be tracked\n    /// @param salt Contracts are created via CREATE2 with this value\n    /// @param params params to be passed to vaults initialize function\n    /// @return vault address of the newly created destination vault\n    function create(\n        string memory vaultType,\n        address baseAsset,\n        address underlyer,\n        address incentiveCalculator,\n        address[] memory additionalTrackedTokens,\n        bytes32 salt,\n        bytes memory params\n    ) external returns (address vault);\n\n    /// @notice Sets the default reward ratio\n    /// @param rewardRatio new default reward ratio\n    function setDefaultRewardRatio(\n        uint256 rewardRatio\n    ) external;\n\n    /// @notice Sets the default reward block duration\n    /// @param blockDuration new default reward block duration\n    function setDefaultRewardBlockDuration(\n        uint256 blockDuration\n    ) external;\n}\n","Filename":"src/interfaces/vault/IDestinationVaultFactory.sol"},{"SourceCode":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.24;\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(\n        List storage self\n    ) 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(\n        List storage self\n    ) 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(\n        List storage self\n    ) 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(\n        List storage self\n    ) 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(\n        List storage self\n    ) 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(\n        List storage self\n    ) 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","Filename":"src/strategy/StructuredLinkedList.sol"},{"SourceCode":"// 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","Filename":"lib/openzeppelin-contracts/contracts/utils/Address.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2023 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { IERC20Metadata as IERC20 } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\nimport { IBaseAssetVault } from \"src/interfaces/vault/IBaseAssetVault.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { IDexLSTStats } from \"src/interfaces/stats/IDexLSTStats.sol\";\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\n\ninterface IDestinationVault is ISystemComponent, IBaseAssetVault, IERC20 {\n    enum VaultShutdownStatus {\n        Active,\n        Deprecated,\n        Exploit\n    }\n\n    error LogicDefect();\n    error BaseAmountReceived(uint256 amount);\n\n    /* ******************************** */\n    /* View                             */\n    /* ******************************** */\n\n    /// @notice A full unit of this vault\n    // solhint-disable-next-line func-name-mixedcase\n    function ONE() external view returns (uint256);\n\n    /// @notice The asset that is deposited into the vault\n    function underlying() external view returns (address);\n\n    /// @notice The total supply of the underlying asset\n    function underlyingTotalSupply() external view returns (uint256);\n\n    /// @notice The asset that rewards and withdrawals to the Autopool are denominated in\n    /// @inheritdoc IBaseAssetVault\n    function baseAsset() external view override returns (address);\n\n    /// @notice Debt balance of underlying asset that is in contract.  This\n    ///     value includes only assets that are known as debt by the rest of the\n    ///     system (i.e. transferred in on rebalance), and does not include\n    ///     extraneous amounts of underlyer that may have ended up in this contract.\n    function internalDebtBalance() external view returns (uint256);\n\n    /// @notice Debt balance of underlying asset staked externally.  This value only\n    ///     includes assets known as debt to the rest of the system, and does not include\n    ///     any assets staked on behalf of the DV in external contracts.\n    function externalDebtBalance() external view returns (uint256);\n\n    /// @notice Returns true value of _underlyer in DV.  Debt + tokens that may have\n    ///     been transferred into the contract outside of rebalance.\n    function internalQueriedBalance() external view returns (uint256);\n\n    /// @notice Returns true value of staked _underlyer in external contract.  This\n    ///     will include any _underlyer that has been staked on behalf of the DV.\n    function externalQueriedBalance() external view returns (uint256);\n\n    /// @notice Balance of underlying debt, sum of `externalDebtBalance()` and `internalDebtBalance()`.\n    function balanceOfUnderlyingDebt() external view returns (uint256);\n\n    /// @notice Rewarder for this vault\n    function rewarder() external view returns (address);\n\n    /// @notice Exchange this destination vault points to\n    function exchangeName() external view returns (string memory);\n\n    /// @notice The type of pool associated with this vault\n    function poolType() external view returns (string memory);\n\n    /// @notice The type of pool plus any staking information\n    function destType() external view returns (string memory);\n\n    /// @notice If the pool only deals in ETH when adding or removing liquidity\n    function poolDealInEth() external view returns (bool);\n\n    /// @notice Tokens that base asset can be swapped into\n    function underlyingTokens() external view returns (address[] memory);\n\n    /// @notice Gets the reserves of the underlying tokens\n    function underlyingReserves() external view returns (address[] memory tokens, uint256[] memory amounts);\n\n    /* ******************************** */\n    /* Events                           */\n    /* ******************************** */\n\n    event Donated(address sender, uint256 amount);\n    event Withdraw(\n        uint256 target, uint256 actual, uint256 debtLoss, uint256 claimLoss, uint256 fromIdle, uint256 fromDebt\n    );\n    event UpdateSignedMessage(bytes32 hash, bool flag);\n\n    /* ******************************** */\n    /* Errors                           */\n    /* ******************************** */\n\n    error ZeroAddress(string paramName);\n    error InvalidShutdownStatus(VaultShutdownStatus status);\n\n    /* ******************************** */\n    /* Functions                        */\n    /* ******************************** */\n\n    /// @notice Setup the contract. These will be cloned so no constructor\n    /// @param baseAsset_ Base asset of the system. WETH/USDC/etc\n    /// @param underlyer_ Underlying asset the vault will wrap\n    /// @param rewarder_ Reward tracker for this vault\n    /// @param incentiveCalculator_ Incentive calculator for this vault\n    /// @param additionalTrackedTokens_ Additional tokens that should be considered 'tracked'\n    /// @param params_ Any extra parameters needed to setup the contract\n    function initialize(\n        IERC20 baseAsset_,\n        IERC20 underlyer_,\n        IMainRewarder rewarder_,\n        address incentiveCalculator_,\n        address[] memory additionalTrackedTokens_,\n        bytes memory params_\n    ) external;\n\n    function getRangePricesLP() external returns (uint256 spotPrice, uint256 safePrice, bool isSpotSafe);\n\n    /// @notice Calculates the current value of a portion of the debt based on shares\n    /// @dev Queries the current value of all tokens we have deployed, whether its a single place, multiple, staked, etc\n    /// @param shares The number of shares to value\n    /// @return value The current value of our debt in terms of the baseAsset\n    function debtValue(\n        uint256 shares\n    ) external returns (uint256 value);\n\n    /// @notice Collects any earned rewards from staking, incentives, etc. Transfers to sender\n    /// @dev Should be limited to LIQUIDATOR_MANAGER. Rewards must be collected before claimed\n    /// @return amounts amount of rewards claimed for each token\n    /// @return tokens tokens claimed\n    function collectRewards() external returns (uint256[] memory amounts, address[] memory tokens);\n\n    /// @notice Pull any non-tracked token to the specified destination\n    /// @dev Should be limited to TOKEN_RECOVERY_MANAGER\n    function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external;\n\n    /// @notice Recovers any extra underlying both in DV and staked externally not tracked as debt.\n    /// @dev Should be limited to TOKEN_SAVER_ROLE.\n    /// @param destination The address to send excess underlyer to.\n    function recoverUnderlying(\n        address destination\n    ) external;\n\n    /// @notice Deposit underlying to receive destination vault shares\n    /// @param amount amount of base lp asset to deposit\n    function depositUnderlying(\n        uint256 amount\n    ) external returns (uint256 shares);\n\n    /// @notice Withdraw underlying by burning destination vault shares\n    /// @param shares amount of destination vault shares to burn\n    /// @param to destination of the underlying asset\n    /// @return amount underlyer amount 'to' received\n    function withdrawUnderlying(uint256 shares, address to) external returns (uint256 amount);\n\n    /// @notice Burn specified shares for underlyer swapped to base asset\n    /// @param shares amount of vault shares to burn\n    /// @param to destination of the base asset\n    /// @return amount base asset amount 'to' received\n    /// @return tokens the tokens burned to get the base asset\n    /// @return tokenAmounts the amount of the tokens burned to get the base asset\n    function withdrawBaseAsset(\n        uint256 shares,\n        address to\n    ) external returns (uint256 amount, address[] memory tokens, uint256[] memory tokenAmounts);\n\n    /// @notice Mark this vault as shutdown so that autoPools can react\n    function shutdown(\n        VaultShutdownStatus reason\n    ) external;\n\n    /// @notice True if the vault has been shutdown\n    function isShutdown() external view returns (bool);\n\n    /// @notice Returns the reason for shutdown (or `Active` if not shutdown)\n    function shutdownStatus() external view returns (VaultShutdownStatus);\n\n    /// @notice Stats contract for this vault\n    function getStats() external view returns (IDexLSTStats);\n\n    /// @notice get the marketplace rewards\n    /// @return rewardTokens list of reward token addresses\n    /// @return rewardRates list of reward rates\n    function getMarketplaceRewards() external returns (uint256[] memory rewardTokens, uint256[] memory rewardRates);\n\n    /// @notice Get the address of the underlying pool the vault points to\n    /// @return poolAddress address of the underlying pool\n    function getPool() external view returns (address poolAddress);\n\n    /// @notice Gets the spot price of the underlying LP token\n    /// @dev Price validated to be inside our tolerance against safe price. Will revert if outside.\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getValidatedSpotPrice() external returns (uint256 price);\n\n    /// @notice Gets the safe price of the underlying LP token\n    /// @dev Price validated to be inside our tolerance against spot price. Will revert if outside.\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getValidatedSafePrice() external returns (uint256 price);\n\n    /// @notice Get the lowest price we can get for the LP token\n    /// @dev This price can be attacked is not validate to be in any range\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getUnderlyerFloorPrice() external returns (uint256 price);\n\n    /// @notice Get the highest price we can get for the LP token\n    /// @dev This price can be attacked is not validate to be in any range\n    /// @return price Value of 1 unit of the underlying LP token in terms of the base asset\n    function getUnderlyerCeilingPrice() external returns (uint256 price);\n\n    /// @notice Set or unset  a hash as a signed message\n    /// @dev Should be limited to DESTINATION_VAULTS_UPDATER. The set hash is used to validate a signature.\n    /// This signature can be potentially used to claim offchain rewards earned by Destination Vaults.\n    /// @param hash bytes32 hash of a payload\n    /// @param flag boolean flag to indicate a validity of hash\n    function setMessage(bytes32 hash, bool flag) external;\n\n    /// @notice Allows to change the incentive calculator of destination vault\n    /// @dev Only works when vault is shutdown, also validates the calculator before updating\n    /// @param incentiveCalculator address of the new incentive calculator\n    function setIncentiveCalculator(\n        address incentiveCalculator\n    ) external;\n\n    /// @notice Allows to change the extension contract\n    /// @dev Should be limited to DESTINATION_VAULT_MANAGER\n    /// @param extension contract address\n    function setExtension(\n        address extension\n    ) external;\n\n    /// @notice Calls the execute function of the extension contract\n    /// @dev Should be limited to DESTINATION_VAULT_MANAGER\n    /// @dev Special care should be taken to ensure that balances hasn't been manipulated\n    /// @param data any data that the extension contract needs\n    function executeExtension(\n        bytes calldata data\n    ) external;\n\n    /// @notice Returns the max recoup credit given during the withdraw of an undervalued destination\n    function recoupMaxCredit() external view returns (uint256);\n}\n","Filename":"src/interfaces/vault/IDestinationVault.sol"},{"SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2025 Tokemak Foundation. All rights reserved.\npragma solidity ^0.8.24;\n\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { ProcessRebalanceParams } from \"src/vault/libs/AutopoolState.sol\";\n\n// @dev Do not change the order of these, we rely on the index\nenum HookFunctionIndex {\n    onRebalanceStart,\n    onRebalanceOutAssetsReady,\n    onRebalanceInAssetsReturned,\n    onRebalanceDestinationVaultUpdated,\n    onRebalanceFeeProfitHandlingComplete,\n    onRebalanceComplete,\n    onDestinationDebtReport,\n    onNavUpdate\n}\n\ninterface IStrategyHook {\n    /// @notice Returns the flags that represent the functions to call on this hook\n    function getFnFlags() external view returns (uint8);\n\n    /// @notice Fires when the hook has been registered with an Autopool\n    /// @param registrationData Any data needed during registration such as initial configuration\n    function onRegistered(\n        bytes memory registrationData\n    ) external;\n\n    /// @notice Fires when the hook as been unregistered with an Autopool\n    /// @param cleanupData Any information needed to run cleanup operations\n    function onUnregistered(\n        bytes memory cleanupData\n    ) external;\n\n    /// =====================================================\n    /// Rebalance Flow\n    /// - Functions are defined in the order that they fire\n    /// - Any function may revert to stop a rebalance\n    /// - OnNavUpdate interjects at the end\n    /// =====================================================\n\n    /// Flag 1\n    /// @notice Fires at the start of a rebalance before any assets are moved\n    /// @dev No LP/Idle changes have occurred yet\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    function onRebalanceStart(ProcessRebalanceParams calldata params, address solverCaller) external;\n\n    /// Flag 2\n    /// @notice Fires when LP has been removed from a DestinationVault but before solver has control\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    /// @dev When this is an idle-out, the state of assets between here and Start() is the same\n    function onRebalanceOutAssetsReady(ProcessRebalanceParams calldata params, address solverCaller) external;\n\n    /// Flag 4\n    /// @notice Fires when LP/Idle has been returned from the Solver\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    /// @dev Solver has performed all market operations at this point\n    function onRebalanceInAssetsReturned(ProcessRebalanceParams calldata params, address solverCaller) external;\n\n    /// Flag 8\n    /// @notice Fires after assets have been deposited into DestinationVault\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    /// @dev When this is an idle-in, the state of assets doesn't change between InAssetsReturned() and here\n    function onRebalanceDestinationVaultUpdated(\n        ProcessRebalanceParams calldata params,\n        address solverCaller\n    ) external;\n\n    /// Flag 16\n    /// @notice Fires after any fee and profit handling has occurred\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    /// @dev Autopool totalSupply() should be steady at this point\n    function onRebalanceFeeProfitHandlingComplete(\n        ProcessRebalanceParams calldata params,\n        address solverCaller\n    ) external;\n\n    /// Flag 32\n    /// @notice Fires at the end of the rebalance\n    /// @param params Rebalance parameters\n    /// @param solverCaller Solver who initiated the rebalance\n    function onRebalanceComplete(ProcessRebalanceParams calldata params, address solverCaller) external;\n\n    /// =====================================================\n    /// Debt Reporting\n    /// - Functions should not revert\n    /// =====================================================\n\n    /// Flag 64\n    /// @notice Fires for any custom tracking of debt valuations\n    /// @dev Autopool has possession of auto-compounded rewards\n    /// @param destination Target DestinationVault address\n    /// @param debtResult Change in values due to debt reporting\n    function onDestinationDebtReport(address destination, AutopoolDebt.IdleDebtUpdates memory debtResult) external;\n\n    /// =====================================================\n    /// Nav Updates\n    /// - Functions should not revert\n    /// =====================================================\n\n    /// Flag 128\n    /// @notice Fires after any nav update\n    /// @param assetChanges New and old assets and totalSupply\n    /// @dev Also fires immediately before onRebalanceFeeProfitHandlingComplete()\n    function onNavUpdate(\n        AutopoolDebt.AssetChanges memory assetChanges\n    ) external;\n}\n","Filename":"src/interfaces/strategy/IStrategyHook.sol"}],"CompilerSettings":{"evmVersion":"cancun","libraries":{"src/strategy/StructuredLinkedList.sol:StructuredLinkedList":"0x89ee0134df602fede54f273c34bbfc876cfaf093","src/strategy/WithdrawalQueue.sol:WithdrawalQueue":"0x1f11f08aa558f6313e3885a9a1ddb5365148b179","src/vault/libs/Autopool4626.sol:Autopool4626":"0xf85b8eb4e7b2d26b7a72c172d0b7718f9ad4a8c8","src/vault/libs/AutopoolDebt.sol:AutopoolDebt":"0xd7bd5bcfaf5f3f9c92c430225e81978ef41b59a8","src/vault/libs/AutopoolDestinations.sol:AutopoolDestinations":"0xebc767937fdd6119268f565ef98b7fe8a67088ad","src/vault/libs/AutopoolFees.sol:AutopoolFees":"0xff10c647b55688fe5473d9f6c456c4d7550e4fc8","src/vault/libs/AutopoolStrategyHooks.sol:AutopoolStrategyHooks":"0x319535b89fa9eb30554045245e6bfdbd486d96d7","src/vault/libs/AutopoolToken.sol:AutopoolToken":"0xae9077cd5e0824b24a66c4d240bcfa67a8973b38"},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":true,"runs":200},"remappings":[":ERC4626/=lib/properties/lib/ERC4626/contracts/",":crytic/properties/=lib/properties/",":ds-test/=lib/forge-std/lib/ds-test/src/",":erc4626-tests/=lib/erc4626-tests/",":forge-std/=lib/forge-std/src/",":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",":prb-math/=lib/prb-math/",":properties/=lib/properties/contracts/",":redstone-evm-connector/=lib/redstone-evm-connector/",":redstone-finance/=lib/redstone-evm-connector/packages/evm-connector/contracts/",":solmate/=lib/properties/lib/solmate/src/",":src/=src/",":test/=test/",":usingtellor/=lib/usingtellor/contracts/"]},"ConstructorArguments":"0x0000000000000000000000002218f90a98b0c070676f249ef44834686daa4285000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","ExternalLibraries":[{"address_hash":"0x89ee0134df602fede54f273c34bbfc876cfaf093","name":"src/strategy/StructuredLinkedList.sol:StructuredLinkedList"},{"address_hash":"0x1f11f08aa558f6313e3885a9a1ddb5365148b179","name":"src/strategy/WithdrawalQueue.sol:WithdrawalQueue"},{"address_hash":"0xf85b8eb4e7b2d26b7a72c172d0b7718f9ad4a8c8","name":"src/vault/libs/Autopool4626.sol:Autopool4626"},{"address_hash":"0xd7bd5bcfaf5f3f9c92c430225e81978ef41b59a8","name":"src/vault/libs/AutopoolDebt.sol:AutopoolDebt"},{"address_hash":"0xebc767937fdd6119268f565ef98b7fe8a67088ad","name":"src/vault/libs/AutopoolDestinations.sol:AutopoolDestinations"},{"address_hash":"0xff10c647b55688fe5473d9f6c456c4d7550e4fc8","name":"src/vault/libs/AutopoolFees.sol:AutopoolFees"},{"address_hash":"0x319535b89fa9eb30554045245e6bfdbd486d96d7","name":"src/vault/libs/AutopoolStrategyHooks.sol:AutopoolStrategyHooks"},{"address_hash":"0xae9077cd5e0824b24a66c4d240bcfa67a8973b38","name":"src/vault/libs/AutopoolToken.sol:AutopoolToken"}],"OptimizationRuns":200,"IsProxy":"false","SourceCode":"// SPDX-License-Identifier: UNLICENSED\n// Copyright (c) 2025 Tokemak Foundation. All rights reserved.\n\npragma solidity ^0.8.24;\n\n//                   ██\n//                   ██\n//                   ██\n//                   ██\n//                   ██\n//      █████████████████████████████████████████\n//                                 ██\n//                                 ██\n//                                 ██\n//                                 ██\n//                                 ██\n\nimport { Roles } from \"src/libs/Roles.sol\";\nimport { Errors } from \"src/utils/Errors.sol\";\nimport { AutopoolDebt } from \"src/vault/libs/AutopoolDebt.sol\";\nimport { Pausable } from \"src/security/Pausable.sol\";\nimport { VaultTypes } from \"src/vault/VaultTypes.sol\";\nimport { NonReentrantUpgradeable } from \"src/utils/NonReentrantUpgradeable.sol\";\nimport { SecurityBase } from \"src/security/SecurityBase.sol\";\nimport { IAutopool } from \"src/interfaces/vault/IAutopool.sol\";\nimport { AutopoolFees } from \"src/vault/libs/AutopoolFees.sol\";\nimport { AutopoolToken } from \"src/vault/libs/AutopoolToken.sol\";\nimport { Autopool4626 } from \"src/vault/libs/Autopool4626.sol\";\nimport { IStrategy } from \"src/interfaces/strategy/IStrategy.sol\";\nimport { ISystemSecurity } from \"src/interfaces/security/ISystemSecurity.sol\";\nimport { Math } from \"openzeppelin-contracts/utils/math/Math.sol\";\nimport { WithdrawalQueue } from \"src/strategy/WithdrawalQueue.sol\";\nimport { AutopoolDestinations } from \"src/vault/libs/AutopoolDestinations.sol\";\nimport { ISystemRegistry } from \"src/interfaces/ISystemRegistry.sol\";\nimport { IERC20 } from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport { ISystemComponent } from \"src/interfaces/ISystemComponent.sol\";\nimport { IAutopoolStrategy } from \"src/interfaces/strategy/IAutopoolStrategy.sol\";\nimport { IMainRewarder } from \"src/interfaces/rewarders/IMainRewarder.sol\";\nimport { StructuredLinkedList } from \"src/strategy/StructuredLinkedList.sol\";\nimport { Initializable } from \"openzeppelin-contracts/proxy/utils/Initializable.sol\";\nimport { EnumerableSet } from \"openzeppelin-contracts/utils/structs/EnumerableSet.sol\";\nimport { IERC20Metadata } from \"openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC3156FlashBorrower } from \"openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol\";\nimport { AutopoolState, ProcessRebalanceParams, AutopoolStorage } from \"src/vault/libs/AutopoolState.sol\";\nimport { AutopoolStrategyHooks } from \"src/vault/libs/AutopoolStrategyHooks.sol\";\nimport { IStrategyHook, HookFunctionIndex } from \"src/interfaces/strategy/IStrategyHook.sol\";\n\n// solhint-disable max-states-count,const-name-snakecase\n\ncontract AutopoolETH is\n    ISystemComponent,\n    Initializable,\n    IAutopool,\n    IStrategy,\n    SecurityBase,\n    Pausable,\n    NonReentrantUpgradeable\n{\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using Math for uint256;\n    using WithdrawalQueue for StructuredLinkedList.List;\n    using AutopoolToken for AutopoolToken.TokenData;\n    using AutopoolDestinations for AutopoolState;\n    using AutopoolFees for AutopoolState;\n    using Autopool4626 for AutopoolState;\n    using AutopoolDebt for AutopoolState;\n    using AutopoolStrategyHooks for AutopoolState;\n\n    /// Be careful around the use of totalSupply and balanceOf. If you go directly to the _token struct you may miss\n    /// out on the profit share unlock logic or the checking the balance of the pool itself\n\n    /// =====================================================\n    /// Constant Vars\n    /// =====================================================\n\n    /// @notice Amount of base asset to be sent to vault on initialization.\n    uint256 public constant BASE_ASSET_INIT_DEPOSIT = 100_000;\n\n    // @notice Decimals of the Autopool\n    uint8 internal constant AUTOPOOL_DECIMALS = 18;\n\n    /// @notice 100% == 10000\n    uint256 internal constant FEE_DIVISOR = 10_000;\n\n    /// @notice Dead address for init share burn.\n    address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n    // solhint-disable-next-line var-name-mixedcase\n    uint256 public constant ONE = 10 ** AUTOPOOL_DECIMALS;\n\n    /// @notice The strategy logic for the Autopool\n    /// @dev Intentionally set to dead\n    IAutopoolStrategy public constant autoPoolStrategy = IAutopoolStrategy(DEAD_ADDRESS);\n\n    /// =====================================================\n    /// Immutable Vars\n    /// =====================================================\n\n    /// @notice Overarching baseAsset type\n    bytes32 public immutable vaultType = VaultTypes.GENERAL_V1;\n\n    /// @notice Amount to pad scaling operations by\n    uint256 public immutable decimalPad;\n\n    /// @notice Instance of this system this vault is tied to\n    /// @dev Exposed via `getSystemRegistry()`\n    ISystemRegistry internal immutable _systemRegistry;\n\n    /// @notice The asset that is deposited into the vault\n    /// @dev Exposed via `asset()`\n    IERC20Metadata internal immutable _baseAsset;\n\n    /// @notice Decimals of the base asset\n    uint8 internal immutable _baseAssetDecimals;\n\n    /// =====================================================\n    /// Modifiers\n    /// =====================================================\n\n    /// @notice Reverts if nav/share decreases during a deposit/mint/withdraw/redeem\n    /// @dev Increases are allowed. Ignored when supply is 0\n    modifier noNavPerShareDecrease(\n        TotalAssetPurpose purpose\n    ) {\n        (uint256 oldNav, uint256 startingTotalSupply) = _snapStartNav(purpose);\n        _;\n        _ensureNoNavPerShareDecrease(oldNav, startingTotalSupply, purpose);\n    }\n\n    /// @notice Reverts if any nav/share changing operations are in progress across the system\n    /// @dev Any rebalance or debtReporting on any pool\n    modifier ensureNoNavOps() {\n        _checkNoNavOps();\n        _;\n    }\n\n    /// @notice Globally track operations that change nav/share in a vault\n    /// @dev Doesn't revert, only meant to track so that `ensureNoNavOps()` can revert when appropriate\n    modifier trackNavOps() {\n        ISystemSecurity systemSecurity = _systemRegistry.systemSecurity();\n        systemSecurity.enterNavOperation();\n        _;\n        // slither-disable-next-line reentrancy-no-eth\n        systemSecurity.exitNavOperation();\n    }\n\n    /// =====================================================\n    /// Functions - Construction\n    /// =====================================================\n\n    constructor(\n        ISystemRegistry systemRegistry,\n        address _vaultAsset\n    ) SecurityBase(address(systemRegistry.accessController())) Pausable(systemRegistry) {\n        Errors.verifyNotZero(address(systemRegistry), \"systemRegistry\");\n\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        _systemRegistry = systemRegistry;\n\n        _baseAssetDecimals = IERC20Metadata(_vaultAsset).decimals();\n        _baseAsset = IERC20Metadata(_vaultAsset);\n        $.symbol = string(abi.encodePacked(\"autopool\", IERC20Metadata(_vaultAsset).symbol(), \"Template\"));\n        $.name = string(abi.encodePacked($.symbol, \" Token\"));\n\n        _disableInitializers();\n\n        if (_baseAssetDecimals > AUTOPOOL_DECIMALS) {\n            revert InvalidDecimals();\n        }\n        decimalPad = 10 ** (AUTOPOOL_DECIMALS - _baseAssetDecimals);\n    }\n\n    function initialize(\n        string memory symbolSuffix,\n        string memory descPrefix,\n        bytes memory\n    ) external virtual initializer {\n        NonReentrantUpgradeable.initialize();\n\n        Errors.verifyNotEmpty(symbolSuffix, \"symbolSuffix\");\n        Errors.verifyNotEmpty(descPrefix, \"descPrefix\");\n\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        $.symbol = symbolSuffix;\n        $.name = descPrefix;\n        $.factory = msg.sender;\n\n        $.initializeFeeSettings();\n\n        // slither-disable-start reentrancy-no-eth\n\n        // Send 100_000 shares to dead address to prevent nav / share inflation attack that can happen\n        // with very small shares and totalAssets amount.\n        uint256 sharesMinted = deposit(BASE_ASSET_INIT_DEPOSIT, DEAD_ADDRESS);\n\n        if (sharesMinted != Autopool4626.changeDecimals(BASE_ASSET_INIT_DEPOSIT, _baseAssetDecimals, AUTOPOOL_DECIMALS))\n        {\n            revert ValueSharesMismatch(BASE_ASSET_INIT_DEPOSIT, sharesMinted);\n        }\n        // slither-disable-end reentrancy-no-eth\n\n        AutopoolFees.setProfitUnlockPeriod($, 86_400);\n    }\n\n    /// =====================================================\n    /// Functions - External\n    /// =====================================================\n\n    /// @notice Enable or disable the high water mark on the rebalance fee\n    /// @dev Will revert if set to the same value\n    function setRebalanceFeeHighWaterMarkEnabled(\n        bool enabled\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_FEE_UPDATER);\n        AutopoolState storage $ = AutopoolStorage.load();\n        AutopoolFees.setRebalanceFeeHighWaterMarkEnabled($.feeSettings, enabled);\n    }\n\n    /// @notice Set the fee that will be taken when profit is realized\n    /// @dev Resets the high water to current value\n    /// @param fee Percent. 100% == 10000\n    function setStreamingFeeBps(\n        uint256 fee\n    ) external nonReentrant {\n        _ensureCallerHasRole(Roles.AUTO_POOL_FEE_UPDATER);\n        AutopoolState storage $ = AutopoolStorage.load();\n        $.setStreamingFeeBps(fee, $.oldestDebtReporting());\n    }\n\n    /// @notice Set the periodic fee taken.\n    /// @dev Depending on time until next fee take, may update periodicFeeBps directly or queue fee.\n    /// @param fee Fee to update periodic fee to.\n    function setPeriodicFeeBps(\n        uint256 fee\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_PERIODIC_FEE_UPDATER);\n        AutopoolState storage $ = AutopoolStorage.load();\n        $.setPeriodicFeeBps(fee, $.oldestDebtReporting());\n    }\n\n    /// @notice Set the address that will receive fees\n    /// @param newFeeSink Address that will receive fees\n    function setFeeSink(\n        address newFeeSink\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_FEE_UPDATER);\n        AutopoolFees.setFeeSink(AutopoolStorage.load().feeSettings, newFeeSink);\n    }\n\n    /// @notice Sets the address that will receive periodic fees.\n    /// @dev Zero address allowable.  Disables fees.\n    /// @param newPeriodicFeeSink New periodic fee address.\n    function setPeriodicFeeSink(\n        address newPeriodicFeeSink\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_PERIODIC_FEE_UPDATER);\n        AutopoolFees.setPeriodicFeeSink(AutopoolStorage.load().feeSettings, newPeriodicFeeSink);\n    }\n\n    /// @notice Change the length of time it takes for profits to unlock\n    /// @dev If set to 0, existing shares will unlock immediately and increase nav/share.\n    function setProfitUnlockPeriod(\n        uint48 newUnlockPeriodSeconds\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_MANAGER);\n        AutopoolFees.setProfitUnlockPeriod(AutopoolStorage.load(), newUnlockPeriodSeconds);\n    }\n\n    /// @notice Set the rewarder contract used by the Autopool.\n    /// @param newRewarder Address of new rewarder.\n    function setRewarder(\n        address newRewarder\n    ) external {\n        AutopoolState storage $ = AutopoolStorage.load();\n        // Factory needs to be able to call for vault creation.\n        if (msg.sender != $.factory && !_hasRole(Roles.AUTO_POOL_REWARD_MANAGER, msg.sender)) {\n            revert Errors.AccessDenied();\n        }\n\n        $.setRewarder(newRewarder);\n    }\n\n    /// @notice Allow the updating of symbol/desc for the vault (only AFTER shutdown)\n    /// @param newSymbol Symbol the Autopool will use going forward\n    /// @param newName Name the Autopool will use going forward\n    function setSymbolAndDescAfterShutdown(string memory newSymbol, string memory newName) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_MANAGER);\n        AutopoolStorage.load().setSymbolAndDescAfterShutdown(newSymbol, newName);\n    }\n\n    /// @notice Transfer out eligible tokens\n    /// @param tokens List of tokens to transfer\n    /// @param amounts Amount of those tokens to transfer\n    /// @param destinations Recipient wallets of the transferred tokens\n    function recover(\n        address[] calldata tokens,\n        uint256[] calldata amounts,\n        address[] calldata destinations\n    ) external virtual override {\n        _ensureCallerHasRole(Roles.TOKEN_RECOVERY_MANAGER);\n        Autopool4626.recover(tokens, amounts, destinations);\n    }\n\n    /// @inheritdoc IAutopool\n    function shutdown(\n        VaultShutdownStatus reason\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_MANAGER);\n        Autopool4626.shutdownVault(AutopoolStorage.load(), reason);\n    }\n\n    /// @notice Add Destinations to the Autopool\n    function addDestinations(\n        address[] calldata destinations\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_DESTINATION_UPDATER);\n        AutopoolStorage.load().addDestinations(destinations, _systemRegistry);\n    }\n\n    /// @notice Remove Destinations from the Autopool\n    /// @dev If Destination currently has deployments the Destination will be removed when its empty\n    function removeDestinations(\n        address[] calldata destinations\n    ) external {\n        _ensureCallerHasRole(Roles.AUTO_POOL_DESTINATION_UPDATER);\n        AutopoolStorage.load().removeDestinations(destinations);\n    }\n\n    /// @notice Update our cached values of the deployed Destinations\n    /// @param numToProcess The number of Destinations in the list to process\n    function updateDebtReporting(\n        uint256 numToProcess\n    ) external nonReentrant trackNavOps {\n        _ensureCallerHasRole(Roles.AUTO_POOL_REPORTING_EXECUTOR);\n\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        bytes memory hooks = $.getHookBytes();\n\n        // slither-disable-next-line reentrancy-no-eth\n        AutopoolDebt.AssetChanges memory result = AutopoolDebt.updateDebtReporting($, numToProcess, hooks);\n\n        _feeAndProfitHandling(result, hooks, true);\n    }\n\n    /// @notice Add a set of hooks to the Autopool configuration\n    /// @param newHooks Set of hooks to add to the Autopool\n    /// @param data Set of onRegister data to pass to the onRegistered function of the hook\n    function addHooks(IStrategyHook[] memory newHooks, bytes[] memory data) external {\n        _ensureCallerHasRole(Roles.STRATEGY_HOOK_CONFIGURATION);\n        AutopoolStorage.load().addHooks(newHooks, data);\n    }\n\n    /// @notice Remove a hook from the Autopools configuration\n    /// @param hookToRemove Hook to remove from to the Autopool\n    /// @param cleanupData Data to pass to the onUnregistered function of the hook\n    function removeHook(IStrategyHook hookToRemove, bytes calldata cleanupData) external {\n        _ensureCallerHasRole(Roles.STRATEGY_HOOK_CONFIGURATION);\n        AutopoolStorage.load().removeHook(hookToRemove, cleanupData);\n    }\n\n    /// @notice Get hooks configured on the Autopool\n    /// @dev Do not use in any executing code\n    function getHooks() external view returns (AutopoolStrategyHooks.HookConfiguration memory) {\n        return AutopoolStorage.load().getHooks();\n    }\n\n    /// @notice Returns the main rewarder for this contract\n    function rewarder() external view returns (IMainRewarder) {\n        return AutopoolStorage.load().rewarder;\n    }\n\n    /// @inheritdoc IAutopool\n    function isPastRewarder(\n        address _pastRewarder\n    ) external view returns (bool) {\n        return AutopoolStorage.load().pastRewarders.contains(_pastRewarder);\n    }\n\n    /// @inheritdoc IAutopool\n    function isShutdown() external view returns (bool) {\n        return AutopoolStorage.load().shutdown;\n    }\n\n    /// @inheritdoc IAutopool\n    function shutdownStatus() external view returns (VaultShutdownStatus) {\n        return AutopoolStorage.load().shutdownStatus;\n    }\n\n    /// @notice Returns state and settings related to gradual profit unlock\n    function getProfitUnlockSettings() external view returns (IAutopool.ProfitUnlockSettings memory) {\n        return AutopoolStorage.load().profitUnlockSettings;\n    }\n\n    /// @notice Returns state and settings related to periodic and streaming fees\n    function getFeeSettings() external view returns (IAutopool.AutopoolFeeSettings memory) {\n        return AutopoolStorage.load().feeSettings;\n    }\n\n    /// @notice Returns amount of assets for shares provided in an ideal scenario where all the conditions are met.\n    function convertToAssets(\n        uint256 shares\n    ) external view virtual returns (uint256 assets) {\n        assets = convertToAssets(shares, totalAssets(TotalAssetPurpose.Global), totalSupply(), Math.Rounding.Down);\n    }\n\n    /// @notice Returns the system instance this contract is tied to\n    function getSystemRegistry() external view override returns (address) {\n        return address(_systemRegistry);\n    }\n\n    /// @notice Returns the full list of Destinations configured on the Autopool\n    function getDestinations() external view override(IAutopool, IStrategy) returns (address[] memory) {\n        return AutopoolStorage.load().destinations.values();\n    }\n\n    /// @notice Returns the ordered list of Destinations that users will withdraw from when required\n    function getWithdrawalQueue() external view returns (address[] memory) {\n        return AutopoolStorage.load().withdrawalQueue.getList();\n    }\n\n    /// @notice Returns the list of Destinations that should be debt reported on\n    function getDebtReportingQueue() external view returns (address[] memory) {\n        return AutopoolStorage.load().debtReportQueue.getList();\n    }\n\n    /// @inheritdoc IAutopool\n    function isDestinationRegistered(\n        address destination\n    ) external view returns (bool) {\n        return AutopoolStorage.load().destinations.contains(destination);\n    }\n\n    /// @notice Returns all destination currently queued for removal\n    function getRemovalQueue() external view override returns (address[] memory) {\n        return AutopoolStorage.load().removalQueue.values();\n    }\n\n    /// @notice Factory contract that created this vault\n    function factory() external view returns (address) {\n        return AutopoolStorage.load().factory;\n    }\n\n    /// @inheritdoc IAutopool\n    function getDestinationInfo(\n        address destVault\n    ) external view returns (AutopoolDebt.DestinationInfo memory) {\n        return AutopoolStorage.load().destinationInfo[destVault];\n    }\n\n    /// @notice Return the timestamp of the oldest debt reporting\n    function oldestDebtReporting() external view returns (uint256) {\n        return AutopoolStorage.load().oldestDebtReporting();\n    }\n\n    /// @inheritdoc IAutopool\n    function isDestinationQueuedForRemoval(\n        address dest\n    ) external view returns (bool) {\n        return AutopoolStorage.load().removalQueue.contains(dest);\n    }\n\n    /// =====================================================\n    /// Functions - Public\n    /// =====================================================\n\n    /// @notice Mints Vault shares to receiver by depositing exactly amount of underlying tokens\n    /// @dev No nav/share changing operations, debt reportings or rebalances,\n    /// can be happening throughout the entire system\n    function deposit(\n        uint256 assets,\n        address receiver\n    )\n        public\n        virtual\n        override\n        nonReentrant\n        noNavPerShareDecrease(TotalAssetPurpose.Deposit)\n        ensureNoNavOps\n        returns (uint256 shares)\n    {\n        AutopoolState storage $ = AutopoolStorage.load();\n        shares = Autopool4626.deposit($, address(_baseAsset), assets, receiver, paused(), _baseAssetDecimals);\n        onDeposit(assets, shares, receiver);\n    }\n\n    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n    function mint(\n        uint256 shares,\n        address receiver\n    )\n        public\n        virtual\n        override\n        nonReentrant\n        noNavPerShareDecrease(TotalAssetPurpose.Deposit)\n        ensureNoNavOps\n        returns (uint256 assets)\n    {\n        AutopoolState storage $ = AutopoolStorage.load();\n        // Handles the vault being paused, returns 0\n        if (shares > maxMint(receiver)) {\n            revert ERC4626MintExceedsMax(shares, maxMint(receiver));\n        }\n\n        uint256 ta = $.totalAssetsTimeChecked(TotalAssetPurpose.Deposit);\n        assets = convertToAssets(shares, ta, totalSupply(), Math.Rounding.Up);\n\n        $.transferAndMint(_baseAsset, assets, shares, receiver);\n\n        onDeposit(assets, shares, receiver);\n    }\n\n    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    )\n        external\n        virtual\n        override\n        nonReentrant\n        whenNotPaused\n        noNavPerShareDecrease(TotalAssetPurpose.Withdraw)\n        ensureNoNavOps\n        returns (uint256 shares)\n    {\n        Errors.verifyNotZero(assets, \"assets\");\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        //slither-disable-next-line unused-return\n        (uint256 actualAssets, uint256 actualShares,) =\n            AutopoolDebt.withdraw($, assets, $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw));\n\n        shares = actualShares;\n\n        _completeWithdrawal($, actualAssets, shares, owner, receiver);\n    }\n\n    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    )\n        external\n        virtual\n        override\n        nonReentrant\n        whenNotPaused\n        noNavPerShareDecrease(TotalAssetPurpose.Withdraw)\n        ensureNoNavOps\n        returns (uint256 assets)\n    {\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        uint256 ta = $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw);\n\n        {\n            uint256 maxShares = _maxRedeem(owner, ta);\n            if (shares > maxShares) {\n                revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);\n            }\n        }\n\n        uint256 possibleAssets = convertToAssets(shares, ta, totalSupply(), Math.Rounding.Down);\n        Errors.verifyNotZero(possibleAssets, \"possibleAssets\");\n\n        //slither-disable-next-line unused-return\n        (uint256 actualAssets, uint256 actualShares,) = AutopoolDebt.redeem($, possibleAssets, ta);\n\n        assets = actualAssets;\n\n        assert(actualShares <= shares);\n\n        _completeWithdrawal($, actualAssets, shares, owner, receiver);\n    }\n\n    /// @notice Sets a `value` amount of tokens as the allowance of `spender` over the caller's tokens.\n    function approve(address spender, uint256 value) public virtual returns (bool) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        return $.token.approve(spender, value);\n    }\n\n    /// @notice Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism.\n    /// `value` is then deducted from the caller's allowance.\n    function transferFrom(address from, address to, uint256 value) public virtual whenNotPaused returns (bool) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        return $.token.transferFrom(from, to, value);\n    }\n\n    /// @notice Moves a `value` amount of tokens from the caller's account to `to`\n    function transfer(address to, uint256 value) public virtual whenNotPaused returns (bool) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        return $.token.transfer(to, value);\n    }\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public virtual {\n        AutopoolState storage $ = AutopoolStorage.load();\n        $.token.permit(owner, spender, value, deadline, v, r, s);\n    }\n\n    /// @notice Returns the maximum amount of the underlying asset that can be\n    /// deposited into the Vault for the receiver, through a deposit call\n    function maxDeposit(\n        address wallet\n    ) public virtual override returns (uint256 maxAssets) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        maxAssets = Autopool4626.maxDeposit($, wallet, paused(), _baseAssetDecimals);\n    }\n\n    /// @notice Simulate the effects of the deposit at the current block, given current on-chain conditions.\n    function previewDeposit(\n        uint256 assets\n    ) public virtual returns (uint256 shares) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        shares = convertToShares(\n            assets, $.totalAssetsTimeChecked(TotalAssetPurpose.Deposit), totalSupply(), Math.Rounding.Down\n        );\n    }\n\n    /// @notice Returns the maximum amount of the Vault shares that\n    /// can be minted for the receiver, through a mint call.\n    function maxMint(\n        address wallet\n    ) public virtual override returns (uint256 maxShares) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        maxShares = Autopool4626.maxMint($, wallet, paused());\n    }\n\n    /// @notice Returns the maximum amount of the underlying asset that can\n    /// be withdrawn from the owner balance in the Vault, through a withdraw call\n    function maxWithdraw(\n        address owner\n    ) public virtual returns (uint256 maxAssets) {\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        uint256 ownerShareBalance = balanceOf(owner);\n        uint256 taChecked = $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw);\n\n        if (paused() || ownerShareBalance == 0 || taChecked == 0) {\n            return 0;\n        }\n\n        uint256 convertedAssets = convertToAssets(ownerShareBalance, taChecked, totalSupply(), Math.Rounding.Down);\n\n        // slither-disable-next-line unused-return\n        (maxAssets,) = AutopoolDebt.preview(\n            $, true, convertedAssets, taChecked, abi.encodeCall(this.previewWithdraw, (convertedAssets))\n        );\n    }\n\n    /// @notice Returns the maximum amount of Vault shares that can be redeemed\n    /// from the owner balance in the Vault, through a redeem call\n    function maxRedeem(\n        address owner\n    ) public virtual returns (uint256 maxShares) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        maxShares = _maxRedeem(owner, $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw));\n    }\n\n    /// @notice Simulate the effects of a mint at the current block, given current on-chain conditions\n    function previewMint(\n        uint256 shares\n    ) public virtual returns (uint256 assets) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        uint256 ta = $.totalAssetsTimeChecked(TotalAssetPurpose.Deposit);\n        assets = convertToAssets(shares, ta, totalSupply(), Math.Rounding.Up);\n        Errors.verifyNotZero(assets, \"assets\");\n    }\n\n    /// @notice Simulate the effects of their withdrawal at the current block, given current on-chain conditions.\n    function previewWithdraw(\n        uint256 assets\n    ) public virtual returns (uint256 shares) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        // slither-disable-next-line unused-return\n        (, shares) = AutopoolDebt.preview(\n            $,\n            true,\n            assets,\n            $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw),\n            abi.encodeCall(this.previewWithdraw, (assets))\n        );\n    }\n\n    /// @notice Simulate the effects of their redemption at the current block, given current on-chain conditions.\n    function previewRedeem(\n        uint256 shares\n    ) public virtual override returns (uint256 assets) {\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        // These values are not needed until the recursive call, gas savings.\n        uint256 applicableTotalAssets = 0;\n        uint256 convertedAssets = 0;\n        if (msg.sender == address(this)) {\n            applicableTotalAssets = $.totalAssetsTimeChecked(TotalAssetPurpose.Withdraw);\n            convertedAssets = convertToAssets(shares, applicableTotalAssets, totalSupply(), Math.Rounding.Down);\n        }\n\n        // slither-disable-next-line unused-return\n        (assets,) = AutopoolDebt.preview(\n            $, false, convertedAssets, applicableTotalAssets, abi.encodeCall(this.previewRedeem, (shares))\n        );\n    }\n\n    /// @inheritdoc IStrategy\n    function flashRebalance(\n        IERC3156FlashBorrower receiver,\n        RebalanceParams memory rebalanceParams,\n        bytes calldata data\n    ) public nonReentrant whenNotPaused trackNavOps {\n        _ensureCallerHasRole(Roles.SOLVER);\n\n        emit RebalanceStarted(address(receiver), rebalanceParams);\n\n        AutopoolState storage $ = AutopoolStorage.load();\n        bytes memory hooks = $.getHookBytes();\n\n        ProcessRebalanceParams memory params =\n            ProcessRebalanceParams({ baseAsset: _baseAsset, receiver: receiver, rebalanceParams: rebalanceParams });\n        AutopoolDebt.AssetChanges memory updatedAssets = AutopoolDebt.processRebalance($, params, data, hooks);\n\n        _feeAndProfitHandling(updatedAssets, hooks, false);\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceFeeProfitHandlingComplete),\n            abi.encodeCall(IStrategyHook.onRebalanceFeeProfitHandlingComplete, (params, msg.sender))\n        );\n\n        // Ensure the destinations are in the queues they should be\n        $.manageQueuesForDestination(rebalanceParams.destinationOut, false);\n        $.manageQueuesForDestination(rebalanceParams.destinationIn, true);\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks,\n            uint256(HookFunctionIndex.onRebalanceComplete),\n            abi.encodeCall(IStrategyHook.onRebalanceComplete, (params, msg.sender))\n        );\n\n        emit RebalanceCompleted(updatedAssets);\n    }\n\n    /// @notice Returns the name of the token\n    function name() public view virtual override returns (string memory) {\n        return AutopoolStorage.load().name;\n    }\n\n    /// @notice Returns the symbol of the token\n    function symbol() public view virtual override returns (string memory) {\n        return AutopoolStorage.load().symbol;\n    }\n\n    /// @notice Returns the decimals of the autoPool token, always denominated in 18 decimals\n    function decimals() public view virtual override returns (uint8) {\n        return AUTOPOOL_DECIMALS;\n    }\n\n    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and\n    /// withdrawing.\n    function asset() public view virtual override returns (address) {\n        return address(_baseAsset);\n    }\n\n    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.\n    /// @dev Utilizes the \"Global\" purpose internally\n    function totalAssets() public view override returns (uint256) {\n        return Autopool4626.totalAssets(AutopoolStorage.load().assetBreakdown, TotalAssetPurpose.Global);\n    }\n\n    /// @notice Returns total amount of the asset() that is “managed” by the Autopool\n    /// @dev Value changes based on purpose. Global is an avg. Deposit is valued higher. Withdraw is valued lower.\n    /// @param purpose The calculation the total assets will be used in\n    function totalAssets(\n        TotalAssetPurpose purpose\n    ) public view returns (uint256) {\n        return Autopool4626.totalAssets(AutopoolStorage.load().assetBreakdown, purpose);\n    }\n\n    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided,\n    /// in an ideal scenario where all the conditions are met\n    function convertToShares(\n        uint256 assets\n    ) public view virtual returns (uint256 shares) {\n        shares = convertToShares(assets, totalAssets(TotalAssetPurpose.Global), totalSupply(), Math.Rounding.Down);\n    }\n\n    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided,\n    /// in an ideal scenario where all the conditions are met\n    function convertToShares(\n        uint256 assets,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) public view virtual returns (uint256 shares) {\n        shares = Autopool4626.convertToShares(assets, totalAssetsForPurpose, supply, rounding, _baseAssetDecimals);\n    }\n\n    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an\n    /// ideal\n    /// scenario where all the conditions are met.\n    function convertToAssets(\n        uint256 shares,\n        uint256 totalAssetsForPurpose,\n        uint256 supply,\n        Math.Rounding rounding\n    ) public view virtual returns (uint256 assets) {\n        assets = Autopool4626.convertToAssets(shares, totalAssetsForPurpose, supply, rounding, _baseAssetDecimals);\n    }\n\n    /// @notice Returns the amount of tokens in existence.\n    /// @dev Subtracts any unlocked profit shares that will be burned\n    function totalSupply() public view virtual override(IERC20) returns (uint256 shares) {\n        shares = Autopool4626.totalSupply();\n    }\n\n    /// @notice Returns the amount of tokens owned by account.\n    /// @dev Subtracts any unlocked profit shares that will be burned when account is the Vault itself\n    function balanceOf(\n        address account\n    ) public view override(IERC20) returns (uint256) {\n        return AutopoolStorage.load().balanceOf(account);\n    }\n\n    /// @notice Returns the amount of tokens owned by wallet.\n    /// @dev Does not subtract any unlocked profit shares that should be burned when wallet is the Vault itself\n    function balanceOfActual(\n        address account\n    ) public view returns (uint256) {\n        return AutopoolStorage.load().token.balances[account];\n    }\n\n    /// @notice Returns the remaining number of tokens that `spender` will be allowed to spend on\n    /// behalf of `owner` through {transferFrom}. This is zero by default\n    /// @dev This value changes when `approve` or `transferFrom` are called\n    function allowance(address owner, address spender) public view virtual returns (uint256) {\n        return AutopoolStorage.load().token.allowances[owner][spender];\n    }\n\n    function getAssetBreakdown() public view override returns (IAutopool.AssetBreakdown memory) {\n        return AutopoolStorage.load().assetBreakdown;\n    }\n\n    /// @notice Returns the next unused nonce for an address.\n    function nonces(\n        address owner\n    ) public view virtual returns (uint256) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        return $.token.nonces[owner];\n    }\n\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return keccak256(\n            abi.encode(\n                AutopoolToken.TYPE_HASH,\n                keccak256(bytes(\"Tokemak\")),\n                keccak256(bytes(\"1\")),\n                block.chainid,\n                address(this)\n            )\n        );\n    }\n\n    /// =====================================================\n    /// Functions - Internal\n    /// =====================================================\n\n    function onDeposit(uint256 assets, uint256 shares, address receiver) internal virtual { }\n\n    function _completeWithdrawal(\n        AutopoolState storage $,\n        uint256 assets,\n        uint256 shares,\n        address owner,\n        address receiver\n    ) internal virtual {\n        AutopoolDebt.completeWithdrawal($, assets, shares, owner, receiver, _baseAsset);\n    }\n\n    function _feeAndProfitHandling(\n        AutopoolDebt.AssetChanges memory assetChanges,\n        bytes memory hooks,\n        bool collectPeriodicFees\n    ) internal {\n        AutopoolState storage $ = AutopoolStorage.load();\n\n        uint256 startingTotalAssets = assetChanges.startingIdle + assetChanges.startingDebt;\n        uint256 newTotalAssets = assetChanges.newIdle + assetChanges.newDebt;\n\n        // Collect any fees and lock any profit if appropriate\n        $.burnUnlockedShares();\n\n        assetChanges.startingTotalSupply = totalSupply();\n\n        assetChanges.endingTotalSupply =\n            _collectFees(newTotalAssets, assetChanges.startingTotalSupply, collectPeriodicFees);\n\n        assetChanges.endingTotalSupply = AutopoolFees.calculateProfitLocking(\n            $.profitUnlockSettings,\n            $.token,\n            assetChanges.endingTotalSupply - assetChanges.startingTotalSupply, // new feeShares\n            newTotalAssets,\n            startingTotalAssets,\n            assetChanges.endingTotalSupply,\n            balanceOfActual(address(this))\n        );\n\n        AutopoolStrategyHooks.executeHooks(\n            hooks, uint256(HookFunctionIndex.onNavUpdate), abi.encodeCall(IStrategyHook.onNavUpdate, (assetChanges))\n        );\n\n        emit Nav(assetChanges.newIdle, assetChanges.newDebt, assetChanges.endingTotalSupply);\n    }\n\n    /// @dev This has been broken it out for testing purposes\n    function _collectFees(\n        uint256 currentTotalAssets,\n        uint256 currentTotalSupply,\n        bool collectPeriodicFees\n    ) internal virtual returns (uint256) {\n        AutopoolState storage $ = AutopoolStorage.load();\n        return AutopoolFees.collectFees($, currentTotalAssets, currentTotalSupply, collectPeriodicFees);\n    }\n\n    /// @dev Revert if a nav-changing operation is in progress in the system\n    function _checkNoNavOps() internal view {\n        if (_systemRegistry.systemSecurity().navOpsInProgress() > 0) {\n            revert NavOpsInProgress();\n        }\n    }\n\n    /// @dev Revert if nav/share decreases on withdraw/redeem. No-op when totalSupply is zero.\n    function _ensureNoNavPerShareDecrease(\n        uint256 oldNav,\n        uint256 startingTotalSupply,\n        TotalAssetPurpose purpose\n    ) internal view virtual {\n        uint256 ts = totalSupply();\n        // slither-disable-next-line incorrect-equality\n        if (ts == 0 || startingTotalSupply == 0) {\n            return;\n        }\n        uint256 newNav = (totalAssets(purpose) * decimalPad * FEE_DIVISOR) / ts;\n        if (newNav < oldNav) {\n            revert NavDecreased(oldNav, newNav);\n        }\n    }\n\n    /// =====================================================\n    /// Functions - Private\n    /// =====================================================\n\n    function _ensureCallerHasRole(\n        bytes32 role\n    ) private view {\n        if (!accessController.hasRole(role, msg.sender)) revert Errors.AccessDenied();\n    }\n\n    function _snapStartNav(\n        TotalAssetPurpose purpose\n    ) private view returns (uint256 oldNav, uint256 startingTotalSupply) {\n        startingTotalSupply = totalSupply();\n        // slither-disable-next-line incorrect-equality\n        if (startingTotalSupply == 0) {\n            return (0, 0);\n        }\n        oldNav = (totalAssets(purpose) * decimalPad * FEE_DIVISOR) / startingTotalSupply;\n    }\n\n    /// @dev Local gas-saving function to pass pre-calculated total assets time checked value\n    function _maxRedeem(address owner, uint256 _totalAssets) private returns (uint256 maxShares) {\n        // If total assets are zero then we are considered uncollateralized and all redeem's will fail\n        if (_totalAssets > 0) {\n            maxShares = paused() ? 0 : balanceOf(owner);\n        }\n    }\n}\n","ABI":"[{\"inputs\":[{\"internalType\":\"contract ISystemRegistry\",\"name\":\"systemRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_vaultAsset\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessDenied\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AlreadySet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"allowed\",\"type\":\"uint256\"}],\"name\":\"AmountExceedsAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destinationVault\",\"type\":\"address\"}],\"name\":\"BaseAssetMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DebtReportingStale\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"}],\"name\":\"ERC2612ExpiredSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC2612InvalidSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxDeposit\",\"type\":\"uint256\"}],\"name\":\"ERC4626DepositExceedsMax\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"}],\"name\":\"ERC4626ExceededMaxRedeem\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"}],\"name\":\"ERC4626ExceededMaxWithdraw\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxMint\",\"type\":\"uint256\"}],\"name\":\"ERC4626MintExceedsMax\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"hook\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fn\",\"type\":\"uint256\"}],\"name\":\"FunctionNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"hook\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fn\",\"type\":\"uint256\"}],\"name\":\"HookAlreadySet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"hook\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"underlyingError\",\"type\":\"bytes\"}],\"name\":\"HookExecutionFailed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"hook\",\"type\":\"address\"}],\"name\":\"HookNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"InsufficientAssets\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"currentNonce\",\"type\":\"uint256\"}],\"name\":\"InvalidAccountNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDecimals\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"InvalidDestination\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"InvalidFee\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"paramName\",\"type\":\"string\"}],\"name\":\"InvalidParam\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPrices\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enum IAutopool.VaultShutdownStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"name\":\"InvalidShutdownStatus\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTotalAssetPurpose\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IsNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IsPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxHooksSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"oldNav\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newNav\",\"type\":\"uint256\"}],\"name\":\"NavDecreased\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NavOpsInProgress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyRebalanceToIdleAvailable\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"remaining\",\"type\":\"uint256\"}],\"name\":\"PositivePriceRecoupNotCovered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"trueUnderlyer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"providedUnderlyer\",\"type\":\"address\"}],\"name\":\"RebalanceDestinationUnderlyerMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RebalanceDestinationsMatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecoveryFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrancyGuardReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"item\",\"type\":\"string\"}],\"name\":\"RegistryItemMissing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"SharesAndAssetsReceived\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"TooFewAssets\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UndefinedAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"dest\",\"type\":\"address\"}],\"name\":\"UnregisteredDestination\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"ValueSharesMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VaultShutdown\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"currentShares\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cachedShares\",\"type\":\"uint256\"}],\"name\":\"WithdrawShareCalcInvalid\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"paramName\",\"type\":\"string\"}],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"AddedToRemovalQueue\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"pricesWereSafe\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"totalIdleDecrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalIdleIncrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtIncrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtDecrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalMinDebtIncrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalMinDebtDecrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalMaxDebtIncrease\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalMaxDebtDecrease\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct AutopoolDebt.IdleDebtUpdates\",\"name\":\"debtInfo\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"claimGasUsed\",\"type\":\"uint256\"}],\"name\":\"DestinationDebtReporting\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"DestinationVaultAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"DestinationVaultRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeSink\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mintedShares\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"profit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalAssets\",\"type\":\"uint256\"}],\"name\":\"FeeCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeSink\",\"type\":\"address\"}],\"name\":\"FeeSinkSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastPeriodicFeeTake\",\"type\":\"uint256\"}],\"name\":\"LastPeriodicFeeTakeSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"idle\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debt\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"}],\"name\":\"Nav\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"navPerShare\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"NewNavShareFeeMark\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint48\",\"name\":\"timeSeconds\",\"type\":\"uint48\"}],\"name\":\"NewProfitUnlockTime\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"NewTotalAssetsHighWatermark\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fees\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeSink\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mintedShares\",\"type\":\"uint256\"}],\"name\":\"PeriodicFeeCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"PeriodicFeeSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPeriodicFeeSink\",\"type\":\"address\"}],\"name\":\"PeriodicFeeSinkSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"startingIdle\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startingDebt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startingTotalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newIdle\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newDebt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endingTotalSupply\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct AutopoolDebt.AssetChanges\",\"name\":\"updatedAssets\",\"type\":\"tuple\"}],\"name\":\"RebalanceCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"RebalanceFeeHighWaterMarkEnabledSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"destinationIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"destinationOut\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct IStrategy.RebalanceParams\",\"name\":\"rebalanceParams\",\"type\":\"tuple\"}],\"name\":\"RebalanceStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"RemovedFromRemovalQueue\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRewarder\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRewarder\",\"type\":\"address\"}],\"name\":\"RewarderSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum IAutopool.VaultShutdownStatus\",\"name\":\"reason\",\"type\":\"uint8\"}],\"name\":\"Shutdown\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"StreamingFeeSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"}],\"name\":\"SymbolAndDescSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"destinations\",\"type\":\"address[]\"}],\"name\":\"TokensRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"destinations\",\"type\":\"address[]\"}],\"name\":\"WithdrawalQueueSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BASE_ASSET_INIT_DEPOSIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ONE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accessController\",\"outputs\":[{\"internalType\":\"contract IAccessController\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"destinations\",\"type\":\"address[]\"}],\"name\":\"addDestinations\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategyHook[]\",\"name\":\"newHooks\",\"type\":\"address[]\"},{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"}],\"name\":\"addHooks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"asset\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"autoPoolStrategy\",\"outputs\":[{\"internalType\":\"contract IAutopoolStrategy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOfActual\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"convertToAssets\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalAssetsForPurpose\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supply\",\"type\":\"uint256\"},{\"internalType\":\"enum Math.Rounding\",\"name\":\"rounding\",\"type\":\"uint8\"}],\"name\":\"convertToAssets\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalAssetsForPurpose\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supply\",\"type\":\"uint256\"},{\"internalType\":\"enum Math.Rounding\",\"name\":\"rounding\",\"type\":\"uint8\"}],\"name\":\"convertToShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"name\":\"convertToShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimalPad\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"deposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"factory\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC3156FlashBorrower\",\"name\":\"receiver\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"destinationIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"destinationOut\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"internalType\":\"struct IStrategy.RebalanceParams\",\"name\":\"rebalanceParams\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"flashRebalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAssetBreakdown\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"totalIdle\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtMin\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtMax\",\"type\":\"uint256\"}],\"internalType\":\"struct IAutopool.AssetBreakdown\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDebtReportingQueue\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destVault\",\"type\":\"address\"}],\"name\":\"getDestinationInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"cachedDebtValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cachedMinDebtValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"cachedMaxDebtValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastReport\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownedShares\",\"type\":\"uint256\"}],\"internalType\":\"struct AutopoolDebt.DestinationInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDestinations\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFeeSettings\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeSink\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"totalAssetsHighMark\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalAssetsHighMarkTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastPeriodicFeeTake\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"periodicFeeSink\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"periodicFeeBps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"streamingFeeBps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"navPerShareLastFeeMark\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"navPerShareLastFeeMarkTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"rebalanceFeeHighWaterMarkEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct IAutopool.AutopoolFeeSettings\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getHooks\",\"outputs\":[{\"components\":[{\"internalType\":\"address[10]\",\"name\":\"onRebalanceStart\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onRebalanceOutAssetsReady\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onRebalanceInAssetsReturned\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onRebalanceDestinationVaultUpdated\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onRebalanceFeeProfitHandlingComplete\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onRebalanceComplete\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onDestinationDebtReport\",\"type\":\"address[10]\"},{\"internalType\":\"address[10]\",\"name\":\"onNavUpdate\",\"type\":\"address[10]\"}],\"internalType\":\"struct AutopoolStrategyHooks.HookConfiguration\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getProfitUnlockSettings\",\"outputs\":[{\"components\":[{\"internalType\":\"uint48\",\"name\":\"unlockPeriodInSeconds\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"fullProfitUnlockTime\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"lastProfitUnlockTime\",\"type\":\"uint48\"},{\"internalType\":\"uint256\",\"name\":\"profitUnlockRate\",\"type\":\"uint256\"}],\"internalType\":\"struct IAutopool.ProfitUnlockSettings\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRemovalQueue\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSystemRegistry\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWithdrawalQueue\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"symbolSuffix\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"descPrefix\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"dest\",\"type\":\"address\"}],\"name\":\"isDestinationQueuedForRemoval\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"isDestinationRegistered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_pastRewarder\",\"type\":\"address\"}],\"name\":\"isPastRewarder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isShutdown\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"wallet\",\"type\":\"address\"}],\"name\":\"maxDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"maxAssets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"wallet\",\"type\":\"address\"}],\"name\":\"maxMint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"maxShares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"maxRedeem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"maxShares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"maxWithdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"maxAssets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oldestDebtReporting\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"name\":\"previewDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"previewMint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"name\":\"previewRedeem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"name\":\"previewWithdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"address[]\",\"name\":\"destinations\",\"type\":\"address[]\"}],\"name\":\"recover\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"redeem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"destinations\",\"type\":\"address[]\"}],\"name\":\"removeDestinations\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IStrategyHook\",\"name\":\"hookToRemove\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"cleanupData\",\"type\":\"bytes\"}],\"name\":\"removeHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewarder\",\"outputs\":[{\"internalType\":\"contract IMainRewarder\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newFeeSink\",\"type\":\"address\"}],\"name\":\"setFeeSink\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"setPeriodicFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newPeriodicFeeSink\",\"type\":\"address\"}],\"name\":\"setPeriodicFeeSink\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint48\",\"name\":\"newUnlockPeriodSeconds\",\"type\":\"uint48\"}],\"name\":\"setProfitUnlockPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setRebalanceFeeHighWaterMarkEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRewarder\",\"type\":\"address\"}],\"name\":\"setRewarder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"setStreamingFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"newSymbol\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"newName\",\"type\":\"string\"}],\"name\":\"setSymbolAndDescAfterShutdown\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum IAutopool.VaultShutdownStatus\",\"name\":\"reason\",\"type\":\"uint8\"}],\"name\":\"shutdown\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"shutdownStatus\",\"outputs\":[{\"internalType\":\"enum IAutopool.VaultShutdownStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalAssets\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum IAutopool.TotalAssetPurpose\",\"name\":\"purpose\",\"type\":\"uint8\"}],\"name\":\"totalAssets\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numToProcess\",\"type\":\"uint256\"}],\"name\":\"updateDebtReporting\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultType\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"assets\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]","ContractName":"AutopoolETH","CompilerVersion":"0.8.24+commit.e11b9ed9","OptimizationUsed":"true","EVMVersion":"cancun","FileName":"src/vault/AutopoolETH.sol","Address":"0xfb2ebdedc38a7d19080e44ab1d621bc9afad0695"}],"status":"1"}