ETH Price: $1,873.62 (-0.24%)

Transaction Decoder

Block:
21235716 at Nov-21-2024 11:10:47 AM +UTC
Transaction Fee:
0.000801580276302975 ETH $1.50
Gas Used:
52,335 Gas / 15.316332785 Gwei

Emitted Events:

233 ERC1967Proxy.0xeb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb56( 0xeb3b05c070c24f667611fdb3ff75fe007d42401c573aed8d8faca95fd00ccb56, 0x000000000000000000000000a9cf2fa810a714dfb0886cc3351c262e8d8393ee, 0000000000000000000000000000000000000000000000e9aec5ccf8a26e0747, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000199c211fb9b4c1b57 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
13.956539849353042601 Eth13.956641379253042601 Eth0.0001015299
0x4FEF9D74...a63dd47Df
(MetaMask: Pooled Staking)
455.992720403316394056 Eth426.466538523982081265 Eth29.526181879334312791
0xa9CF2FA8...e8d8393Ee
0.006212179896387678 Eth
Nonce: 51
29.531592478954397494 Eth
Nonce: 52
29.525380299058009816

Execution Trace

ERC1967Proxy.8697d2c2( )
  • EthFoxVault.claimExitedAssets( positionTicket=4310685066475586062151, timestamp=1731753875, exitQueueIndex=315 ) => ( newPositionTicket=0, claimedShares=29083763572615285046, claimedAssets=29526181879334312791 )
    • ETH 29.526181879334312791 0xa9cf2fa810a714dfb0886cc3351c262e8d8393ee.CALL( )
      File 1 of 2: ERC1967Proxy
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev This is the interface that {BeaconProxy} expects of its beacon.
       */
      interface IBeacon {
          /**
           * @dev Must return an address that can be used as a delegate call target.
           *
           * {UpgradeableBeacon} will check that this address is a contract.
           */
          function implementation() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
      pragma solidity ^0.8.20;
      import {Proxy} from "../Proxy.sol";
      import {ERC1967Utils} from "./ERC1967Utils.sol";
      /**
       * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
       * implementation address that can be changed. This address is stored in storage in the location specified by
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
       * implementation behind the proxy.
       */
      contract ERC1967Proxy is Proxy {
          /**
           * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
           *
           * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
           * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
           *
           * Requirements:
           *
           * - If `data` is empty, `msg.value` must be zero.
           */
          constructor(address implementation, bytes memory _data) payable {
              ERC1967Utils.upgradeToAndCall(implementation, _data);
          }
          /**
           * @dev Returns the current implementation address.
           *
           * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
           * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
           * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
           */
          function _implementation() internal view virtual override returns (address) {
              return ERC1967Utils.getImplementation();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
      pragma solidity ^0.8.20;
      import {IBeacon} from "../beacon/IBeacon.sol";
      import {Address} from "../../utils/Address.sol";
      import {StorageSlot} from "../../utils/StorageSlot.sol";
      /**
       * @dev This abstract contract provides getters and event emitting update functions for
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
       */
      library ERC1967Utils {
          // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
          // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @dev Emitted when the beacon is changed.
           */
          event BeaconUpgraded(address indexed beacon);
          /**
           * @dev Storage slot with the address of the current implementation.
           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /**
           * @dev The `implementation` of the proxy is invalid.
           */
          error ERC1967InvalidImplementation(address implementation);
          /**
           * @dev The `admin` of the proxy is invalid.
           */
          error ERC1967InvalidAdmin(address admin);
          /**
           * @dev The `beacon` of the proxy is invalid.
           */
          error ERC1967InvalidBeacon(address beacon);
          /**
           * @dev An upgrade function sees `msg.value > 0` that may be lost.
           */
          error ERC1967NonPayable();
          /**
           * @dev Returns the current implementation address.
           */
          function getImplementation() internal view returns (address) {
              return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 implementation slot.
           */
          function _setImplementation(address newImplementation) private {
              if (newImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(newImplementation);
              }
              StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
          }
          /**
           * @dev Performs implementation upgrade with additional setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-Upgraded} event.
           */
          function upgradeToAndCall(address newImplementation, bytes memory data) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
              if (data.length > 0) {
                  Address.functionDelegateCall(newImplementation, data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Storage slot with the admin of the contract.
           * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /**
           * @dev Returns the current admin.
           *
           * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
           * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
           * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
           */
          function getAdmin() internal view returns (address) {
              return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 admin slot.
           */
          function _setAdmin(address newAdmin) private {
              if (newAdmin == address(0)) {
                  revert ERC1967InvalidAdmin(address(0));
              }
              StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
          }
          /**
           * @dev Changes the admin of the proxy.
           *
           * Emits an {IERC1967-AdminChanged} event.
           */
          function changeAdmin(address newAdmin) internal {
              emit AdminChanged(getAdmin(), newAdmin);
              _setAdmin(newAdmin);
          }
          /**
           * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
           * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          /**
           * @dev Returns the current beacon.
           */
          function getBeacon() internal view returns (address) {
              return StorageSlot.getAddressSlot(BEACON_SLOT).value;
          }
          /**
           * @dev Stores a new beacon in the EIP1967 beacon slot.
           */
          function _setBeacon(address newBeacon) private {
              if (newBeacon.code.length == 0) {
                  revert ERC1967InvalidBeacon(newBeacon);
              }
              StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
              address beaconImplementation = IBeacon(newBeacon).implementation();
              if (beaconImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(beaconImplementation);
              }
          }
          /**
           * @dev Change the beacon and trigger a setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-BeaconUpgraded} event.
           *
           * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
           * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
           * efficiency.
           */
          function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
              _setBeacon(newBeacon);
              emit BeaconUpgraded(newBeacon);
              if (data.length > 0) {
                  Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
           * if an upgrade doesn't perform an initialization call.
           */
          function _checkNonPayable() private {
              if (msg.value > 0) {
                  revert ERC1967NonPayable();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
       * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
       * be specified by overriding the virtual {_implementation} function.
       *
       * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
       * different contract through the {_delegate} function.
       *
       * The success and return data of the delegated call will be returned back to the caller of the proxy.
       */
      abstract contract Proxy {
          /**
           * @dev Delegates the current call to `implementation`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _delegate(address implementation) internal virtual {
              assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize())
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize())
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 {
                      revert(0, returndatasize())
                  }
                  default {
                      return(0, returndatasize())
                  }
              }
          }
          /**
           * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
           * function and {_fallback} should delegate.
           */
          function _implementation() internal view virtual returns (address);
          /**
           * @dev Delegates the current call to the address returned by `_implementation()`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _fallback() internal virtual {
              _delegate(_implementation());
          }
          /**
           * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
           * function in the contract matches the call data.
           */
          fallback() external payable virtual {
              _fallback();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
      // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
      pragma solidity ^0.8.20;
      /**
       * @dev Library for reading and writing primitive types to specific storage slots.
       *
       * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
       * This library helps with reading and writing to such slots without the need for inline assembly.
       *
       * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
       *
       * Example usage to set ERC1967 implementation slot:
       * ```solidity
       * contract ERC1967 {
       *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
       *
       *     function _getImplementation() internal view returns (address) {
       *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
       *     }
       *
       *     function _setImplementation(address newImplementation) internal {
       *         require(newImplementation.code.length > 0);
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
          struct BooleanSlot {
              bool value;
          }
          struct Bytes32Slot {
              bytes32 value;
          }
          struct Uint256Slot {
              uint256 value;
          }
          struct StringSlot {
              string value;
          }
          struct BytesSlot {
              bytes value;
          }
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` with member `value` located at `slot`.
           */
          function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
           */
          function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` with member `value` located at `slot`.
           */
          function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
           */
          function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
      }
      

      File 2 of 2: EthFoxVault
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```solidity
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       *
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Storage of the initializable contract.
           *
           * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
           * when using with upgradeable contracts.
           *
           * @custom:storage-location erc7201:openzeppelin.storage.Initializable
           */
          struct InitializableStorage {
              /**
               * @dev Indicates that the contract has been initialized.
               */
              uint64 _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool _initializing;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
          /**
           * @dev The contract is already initialized.
           */
          error InvalidInitialization();
          /**
           * @dev The contract is not initializing.
           */
          error NotInitializing();
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint64 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts.
           *
           * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
           * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
           * production.
           *
           * Emits an {Initialized} event.
           */
          modifier initializer() {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              // Cache values to avoid duplicated sloads
              bool isTopLevelCall = !$._initializing;
              uint64 initialized = $._initialized;
              // Allowed calls:
              // - initialSetup: the contract is not in the initializing state and no previous version was
              //                 initialized
              // - construction: the contract is initialized at version 1 (no reininitialization) and the
              //                 current contract is just being deployed
              bool initialSetup = initialized == 0 && isTopLevelCall;
              bool construction = initialized == 1 && address(this).code.length == 0;
              if (!initialSetup && !construction) {
                  revert InvalidInitialization();
              }
              $._initialized = 1;
              if (isTopLevelCall) {
                  $._initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  $._initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * A reinitializer may be used after the original initialization step. This is essential to configure modules that
           * are added through upgrades and that require initialization.
           *
           * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
           * cannot be nested. If one is invoked in the context of another, execution will revert.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           *
           * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
           *
           * Emits an {Initialized} event.
           */
          modifier reinitializer(uint64 version) {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              if ($._initializing || $._initialized >= version) {
                  revert InvalidInitialization();
              }
              $._initialized = version;
              $._initializing = true;
              _;
              $._initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              _checkInitializing();
              _;
          }
          /**
           * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
           */
          function _checkInitializing() internal view virtual {
              if (!_isInitializing()) {
                  revert NotInitializing();
              }
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           *
           * Emits an {Initialized} event the first time it is successfully executed.
           */
          function _disableInitializers() internal virtual {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              if ($._initializing) {
                  revert InvalidInitialization();
              }
              if ($._initialized != type(uint64).max) {
                  $._initialized = type(uint64).max;
                  emit Initialized(type(uint64).max);
              }
          }
          /**
           * @dev Returns the highest version that has been initialized. See {reinitializer}.
           */
          function _getInitializedVersion() internal view returns (uint64) {
              return _getInitializableStorage()._initialized;
          }
          /**
           * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
           */
          function _isInitializing() internal view returns (bool) {
              return _getInitializableStorage()._initializing;
          }
          /**
           * @dev Returns a pointer to the storage namespace.
           */
          // solhint-disable-next-line var-name-mixedcase
          function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
              assembly {
                  $.slot := INITIALIZABLE_STORAGE
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
      pragma solidity ^0.8.20;
      import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
      import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
      import {Initializable} from "./Initializable.sol";
      /**
       * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
       * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
       *
       * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
       * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
       * `UUPSUpgradeable` with a custom implementation of upgrades.
       *
       * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
       */
      abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
          /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
          address private immutable __self = address(this);
          /**
           * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
           * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
           * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
           * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
           * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
           * during an upgrade.
           */
          string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
          /**
           * @dev The call is from an unauthorized context.
           */
          error UUPSUnauthorizedCallContext();
          /**
           * @dev The storage `slot` is unsupported as a UUID.
           */
          error UUPSUnsupportedProxiableUUID(bytes32 slot);
          /**
           * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
           * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
           * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
           * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
           * fail.
           */
          modifier onlyProxy() {
              _checkProxy();
              _;
          }
          /**
           * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
           * callable on the implementing contract but not through proxies.
           */
          modifier notDelegated() {
              _checkNotDelegated();
              _;
          }
          function __UUPSUpgradeable_init() internal onlyInitializing {
          }
          function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
          }
          /**
           * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
           * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
           *
           * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
           * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
           * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
           */
          function proxiableUUID() external view virtual notDelegated returns (bytes32) {
              return ERC1967Utils.IMPLEMENTATION_SLOT;
          }
          /**
           * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
           * encoded in `data`.
           *
           * Calls {_authorizeUpgrade}.
           *
           * Emits an {Upgraded} event.
           *
           * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
           */
          function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
              _authorizeUpgrade(newImplementation);
              _upgradeToAndCallUUPS(newImplementation, data);
          }
          /**
           * @dev Reverts if the execution is not performed via delegatecall or the execution
           * context is not of a proxy with an ERC1967-compliant implementation pointing to self.
           * See {_onlyProxy}.
           */
          function _checkProxy() internal view virtual {
              if (
                  address(this) == __self || // Must be called through delegatecall
                  ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
              ) {
                  revert UUPSUnauthorizedCallContext();
              }
          }
          /**
           * @dev Reverts if the execution is performed via delegatecall.
           * See {notDelegated}.
           */
          function _checkNotDelegated() internal view virtual {
              if (address(this) != __self) {
                  // Must not be called through delegatecall
                  revert UUPSUnauthorizedCallContext();
              }
          }
          /**
           * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
           * {upgradeToAndCall}.
           *
           * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
           *
           * ```solidity
           * function _authorizeUpgrade(address) internal onlyOwner {}
           * ```
           */
          function _authorizeUpgrade(address newImplementation) internal virtual;
          /**
           * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
           *
           * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
           * is expected to be the implementation slot in ERC1967.
           *
           * Emits an {IERC1967-Upgraded} event.
           */
          function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
              try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                  if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                      revert UUPSUnsupportedProxiableUUID(slot);
                  }
                  ERC1967Utils.upgradeToAndCall(newImplementation, data);
              } catch {
                  // The implementation is not UUPS
                  revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
      pragma solidity ^0.8.20;
      import {Initializable} from "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuardUpgradeable is Initializable {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant NOT_ENTERED = 1;
          uint256 private constant ENTERED = 2;
          /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
          struct ReentrancyGuardStorage {
              uint256 _status;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
          function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
              assembly {
                  $.slot := ReentrancyGuardStorageLocation
              }
          }
          /**
           * @dev Unauthorized reentrant call.
           */
          error ReentrancyGuardReentrantCall();
          function __ReentrancyGuard_init() internal onlyInitializing {
              __ReentrancyGuard_init_unchained();
          }
          function __ReentrancyGuard_init_unchained() internal onlyInitializing {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              $._status = NOT_ENTERED;
          }
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and making it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              _nonReentrantBefore();
              _;
              _nonReentrantAfter();
          }
          function _nonReentrantBefore() private {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              // On the first call to nonReentrant, _status will be NOT_ENTERED
              if ($._status == ENTERED) {
                  revert ReentrancyGuardReentrantCall();
              }
              // Any calls to nonReentrant after this point will fail
              $._status = ENTERED;
          }
          function _nonReentrantAfter() private {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              $._status = NOT_ENTERED;
          }
          /**
           * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
           * `nonReentrant` function in the call stack.
           */
          function _reentrancyGuardEntered() internal view returns (bool) {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              return $._status == ENTERED;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
       * proxy whose upgrades are fully controlled by the current implementation.
       */
      interface IERC1822Proxiable {
          /**
           * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
           * address.
           *
           * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
           * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
           * function revert if invoked through a proxy.
           */
          function proxiableUUID() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
      pragma solidity ^0.8.20;
      interface IERC5267 {
          /**
           * @dev MAY be emitted to signal that the domain could have changed.
           */
          event EIP712DomainChanged();
          /**
           * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
           * signature.
           */
          function eip712Domain()
              external
              view
              returns (
                  bytes1 fields,
                  string memory name,
                  string memory version,
                  uint256 chainId,
                  address verifyingContract,
                  bytes32 salt,
                  uint256[] memory extensions
              );
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev This is the interface that {BeaconProxy} expects of its beacon.
       */
      interface IBeacon {
          /**
           * @dev Must return an address that can be used as a delegate call target.
           *
           * {UpgradeableBeacon} will check that this address is a contract.
           */
          function implementation() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
      pragma solidity ^0.8.20;
      import {IBeacon} from "../beacon/IBeacon.sol";
      import {Address} from "../../utils/Address.sol";
      import {StorageSlot} from "../../utils/StorageSlot.sol";
      /**
       * @dev This abstract contract provides getters and event emitting update functions for
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
       */
      library ERC1967Utils {
          // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
          // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @dev Emitted when the beacon is changed.
           */
          event BeaconUpgraded(address indexed beacon);
          /**
           * @dev Storage slot with the address of the current implementation.
           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /**
           * @dev The `implementation` of the proxy is invalid.
           */
          error ERC1967InvalidImplementation(address implementation);
          /**
           * @dev The `admin` of the proxy is invalid.
           */
          error ERC1967InvalidAdmin(address admin);
          /**
           * @dev The `beacon` of the proxy is invalid.
           */
          error ERC1967InvalidBeacon(address beacon);
          /**
           * @dev An upgrade function sees `msg.value > 0` that may be lost.
           */
          error ERC1967NonPayable();
          /**
           * @dev Returns the current implementation address.
           */
          function getImplementation() internal view returns (address) {
              return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 implementation slot.
           */
          function _setImplementation(address newImplementation) private {
              if (newImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(newImplementation);
              }
              StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
          }
          /**
           * @dev Performs implementation upgrade with additional setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-Upgraded} event.
           */
          function upgradeToAndCall(address newImplementation, bytes memory data) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
              if (data.length > 0) {
                  Address.functionDelegateCall(newImplementation, data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Storage slot with the admin of the contract.
           * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /**
           * @dev Returns the current admin.
           *
           * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
           * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
           * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
           */
          function getAdmin() internal view returns (address) {
              return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 admin slot.
           */
          function _setAdmin(address newAdmin) private {
              if (newAdmin == address(0)) {
                  revert ERC1967InvalidAdmin(address(0));
              }
              StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
          }
          /**
           * @dev Changes the admin of the proxy.
           *
           * Emits an {IERC1967-AdminChanged} event.
           */
          function changeAdmin(address newAdmin) internal {
              emit AdminChanged(getAdmin(), newAdmin);
              _setAdmin(newAdmin);
          }
          /**
           * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
           * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          /**
           * @dev Returns the current beacon.
           */
          function getBeacon() internal view returns (address) {
              return StorageSlot.getAddressSlot(BEACON_SLOT).value;
          }
          /**
           * @dev Stores a new beacon in the EIP1967 beacon slot.
           */
          function _setBeacon(address newBeacon) private {
              if (newBeacon.code.length == 0) {
                  revert ERC1967InvalidBeacon(newBeacon);
              }
              StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
              address beaconImplementation = IBeacon(newBeacon).implementation();
              if (beaconImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(beaconImplementation);
              }
          }
          /**
           * @dev Change the beacon and trigger a setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-BeaconUpgraded} event.
           *
           * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
           * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
           * efficiency.
           */
          function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
              _setBeacon(newBeacon);
              emit BeaconUpgraded(newBeacon);
              if (data.length > 0) {
                  Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
           * if an upgrade doesn't perform an initialization call.
           */
          function _checkNonPayable() private {
              if (msg.value > 0) {
                  revert ERC1967NonPayable();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev These functions deal with verification of Merkle Tree proofs.
       *
       * The tree and the proofs can be generated using our
       * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
       * You will find a quickstart guide in the readme.
       *
       * WARNING: You should avoid using leaf values that are 64 bytes long prior to
       * hashing, or use a hash function other than keccak256 for hashing leaves.
       * This is because the concatenation of a sorted pair of internal nodes in
       * the Merkle tree could be reinterpreted as a leaf value.
       * OpenZeppelin's JavaScript library generates Merkle trees that are safe
       * against this attack out of the box.
       */
      library MerkleProof {
          /**
           *@dev The multiproof provided is not valid.
           */
          error MerkleProofInvalidMultiproof();
          /**
           * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
           * defined by `root`. For this, a `proof` must be provided, containing
           * sibling hashes on the branch from the leaf to the root of the tree. Each
           * pair of leaves and each pair of pre-images are assumed to be sorted.
           */
          function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
              return processProof(proof, leaf) == root;
          }
          /**
           * @dev Calldata version of {verify}
           */
          function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
              return processProofCalldata(proof, leaf) == root;
          }
          /**
           * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
           * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
           * hash matches the root of the tree. When processing the proof, the pairs
           * of leafs & pre-images are assumed to be sorted.
           */
          function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
              bytes32 computedHash = leaf;
              for (uint256 i = 0; i < proof.length; i++) {
                  computedHash = _hashPair(computedHash, proof[i]);
              }
              return computedHash;
          }
          /**
           * @dev Calldata version of {processProof}
           */
          function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
              bytes32 computedHash = leaf;
              for (uint256 i = 0; i < proof.length; i++) {
                  computedHash = _hashPair(computedHash, proof[i]);
              }
              return computedHash;
          }
          /**
           * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
           * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
           *
           * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
           */
          function multiProofVerify(
              bytes32[] memory proof,
              bool[] memory proofFlags,
              bytes32 root,
              bytes32[] memory leaves
          ) internal pure returns (bool) {
              return processMultiProof(proof, proofFlags, leaves) == root;
          }
          /**
           * @dev Calldata version of {multiProofVerify}
           *
           * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
           */
          function multiProofVerifyCalldata(
              bytes32[] calldata proof,
              bool[] calldata proofFlags,
              bytes32 root,
              bytes32[] memory leaves
          ) internal pure returns (bool) {
              return processMultiProofCalldata(proof, proofFlags, leaves) == root;
          }
          /**
           * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
           * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
           * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
           * respectively.
           *
           * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
           * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
           * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
           */
          function processMultiProof(
              bytes32[] memory proof,
              bool[] memory proofFlags,
              bytes32[] memory leaves
          ) internal pure returns (bytes32 merkleRoot) {
              // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
              // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
              // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
              // the Merkle tree.
              uint256 leavesLen = leaves.length;
              uint256 proofLen = proof.length;
              uint256 totalHashes = proofFlags.length;
              // Check proof validity.
              if (leavesLen + proofLen != totalHashes + 1) {
                  revert MerkleProofInvalidMultiproof();
              }
              // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
              // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
              bytes32[] memory hashes = new bytes32[](totalHashes);
              uint256 leafPos = 0;
              uint256 hashPos = 0;
              uint256 proofPos = 0;
              // At each step, we compute the next hash using two values:
              // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
              //   get the next hash.
              // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
              //   `proof` array.
              for (uint256 i = 0; i < totalHashes; i++) {
                  bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                  bytes32 b = proofFlags[i]
                      ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                      : proof[proofPos++];
                  hashes[i] = _hashPair(a, b);
              }
              if (totalHashes > 0) {
                  if (proofPos != proofLen) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  unchecked {
                      return hashes[totalHashes - 1];
                  }
              } else if (leavesLen > 0) {
                  return leaves[0];
              } else {
                  return proof[0];
              }
          }
          /**
           * @dev Calldata version of {processMultiProof}.
           *
           * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
           */
          function processMultiProofCalldata(
              bytes32[] calldata proof,
              bool[] calldata proofFlags,
              bytes32[] memory leaves
          ) internal pure returns (bytes32 merkleRoot) {
              // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
              // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
              // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
              // the Merkle tree.
              uint256 leavesLen = leaves.length;
              uint256 proofLen = proof.length;
              uint256 totalHashes = proofFlags.length;
              // Check proof validity.
              if (leavesLen + proofLen != totalHashes + 1) {
                  revert MerkleProofInvalidMultiproof();
              }
              // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
              // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
              bytes32[] memory hashes = new bytes32[](totalHashes);
              uint256 leafPos = 0;
              uint256 hashPos = 0;
              uint256 proofPos = 0;
              // At each step, we compute the next hash using two values:
              // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
              //   get the next hash.
              // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
              //   `proof` array.
              for (uint256 i = 0; i < totalHashes; i++) {
                  bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                  bytes32 b = proofFlags[i]
                      ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                      : proof[proofPos++];
                  hashes[i] = _hashPair(a, b);
              }
              if (totalHashes > 0) {
                  if (proofPos != proofLen) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  unchecked {
                      return hashes[totalHashes - 1];
                  }
              } else if (leavesLen > 0) {
                  return leaves[0];
              } else {
                  return proof[0];
              }
          }
          /**
           * @dev Sorts the pair (a, b) and hashes the result.
           */
          function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
              return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
          }
          /**
           * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
           */
          function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, a)
                  mstore(0x20, b)
                  value := keccak256(0x00, 0x40)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Standard math utilities missing in the Solidity language.
       */
      library Math {
          /**
           * @dev Muldiv operation overflow.
           */
          error MathOverflowedMulDiv();
          enum Rounding {
              Floor, // Toward negative infinity
              Ceil, // Toward positive infinity
              Trunc, // Toward zero
              Expand // Away from zero
          }
          /**
           * @dev Returns the addition of two unsigned integers, with an overflow flag.
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  uint256 c = a + b;
                  if (c < a) return (false, 0);
                  return (true, c);
              }
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b > a) return (false, 0);
                  return (true, a - b);
              }
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) return (true, 0);
                  uint256 c = a * b;
                  if (c / a != b) return (false, 0);
                  return (true, c);
              }
          }
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a / b);
              }
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a % b);
              }
          }
          /**
           * @dev Returns the largest of two numbers.
           */
          function max(uint256 a, uint256 b) internal pure returns (uint256) {
              return a > b ? a : b;
          }
          /**
           * @dev Returns the smallest of two numbers.
           */
          function min(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? a : b;
          }
          /**
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow.
              return (a & b) + (a ^ b) / 2;
          }
          /**
           * @dev Returns the ceiling of the division of two numbers.
           *
           * This differs from standard division with `/` in that it rounds towards infinity instead
           * of rounding towards zero.
           */
          function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
              if (b == 0) {
                  // Guarantee the same behavior as in a regular Solidity division.
                  return a / b;
              }
              // (a + b - 1) / b can overflow on addition, so we distribute.
              return a == 0 ? 0 : (a - 1) / b + 1;
          }
          /**
           * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
           * denominator == 0.
           * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
           * Uniswap Labs also under MIT license.
           */
          function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
              unchecked {
                  // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                  // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                  // variables such that product = prod1 * 2^256 + prod0.
                  uint256 prod0 = x * y; // Least significant 256 bits of the product
                  uint256 prod1; // Most significant 256 bits of the product
                  assembly {
                      let mm := mulmod(x, y, not(0))
                      prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                  }
                  // Handle non-overflow cases, 256 by 256 division.
                  if (prod1 == 0) {
                      // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                      // The surrounding unchecked block does not change this fact.
                      // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                      return prod0 / denominator;
                  }
                  // Make sure the result is less than 2^256. Also prevents denominator == 0.
                  if (denominator <= prod1) {
                      revert MathOverflowedMulDiv();
                  }
                  ///////////////////////////////////////////////
                  // 512 by 256 division.
                  ///////////////////////////////////////////////
                  // Make division exact by subtracting the remainder from [prod1 prod0].
                  uint256 remainder;
                  assembly {
                      // Compute remainder using mulmod.
                      remainder := mulmod(x, y, denominator)
                      // Subtract 256 bit number from 512 bit number.
                      prod1 := sub(prod1, gt(remainder, prod0))
                      prod0 := sub(prod0, remainder)
                  }
                  // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                  // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                  uint256 twos = denominator & (0 - denominator);
                  assembly {
                      // Divide denominator by twos.
                      denominator := div(denominator, twos)
                      // Divide [prod1 prod0] by twos.
                      prod0 := div(prod0, twos)
                      // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                      twos := add(div(sub(0, twos), twos), 1)
                  }
                  // Shift in bits from prod1 into prod0.
                  prod0 |= prod1 * twos;
                  // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                  // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                  // four bits. That is, denominator * inv = 1 mod 2^4.
                  uint256 inverse = (3 * denominator) ^ 2;
                  // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                  // works in modular arithmetic, doubling the correct bits in each step.
                  inverse *= 2 - denominator * inverse; // inverse mod 2^8
                  inverse *= 2 - denominator * inverse; // inverse mod 2^16
                  inverse *= 2 - denominator * inverse; // inverse mod 2^32
                  inverse *= 2 - denominator * inverse; // inverse mod 2^64
                  inverse *= 2 - denominator * inverse; // inverse mod 2^128
                  inverse *= 2 - denominator * inverse; // inverse mod 2^256
                  // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                  // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                  // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                  // is no longer required.
                  result = prod0 * inverse;
                  return result;
              }
          }
          /**
           * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
           */
          function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
              uint256 result = mulDiv(x, y, denominator);
              if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                  result += 1;
              }
              return result;
          }
          /**
           * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
           * towards zero.
           *
           * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
           */
          function sqrt(uint256 a) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
              // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
              //
              // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
              // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
              //
              // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
              // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
              // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
              //
              // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
              uint256 result = 1 << (log2(a) >> 1);
              // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
              // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
              // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
              // into the expected uint128 result.
              unchecked {
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  return min(result, a / result);
              }
          }
          /**
           * @notice Calculates sqrt(a), following the selected rounding direction.
           */
          function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = sqrt(a);
                  return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 2 of a positive value rounded towards zero.
           * Returns 0 if given 0.
           */
          function log2(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >> 128 > 0) {
                      value >>= 128;
                      result += 128;
                  }
                  if (value >> 64 > 0) {
                      value >>= 64;
                      result += 64;
                  }
                  if (value >> 32 > 0) {
                      value >>= 32;
                      result += 32;
                  }
                  if (value >> 16 > 0) {
                      value >>= 16;
                      result += 16;
                  }
                  if (value >> 8 > 0) {
                      value >>= 8;
                      result += 8;
                  }
                  if (value >> 4 > 0) {
                      value >>= 4;
                      result += 4;
                  }
                  if (value >> 2 > 0) {
                      value >>= 2;
                      result += 2;
                  }
                  if (value >> 1 > 0) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log2(value);
                  return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 10 of a positive value rounded towards zero.
           * Returns 0 if given 0.
           */
          function log10(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >= 10 ** 64) {
                      value /= 10 ** 64;
                      result += 64;
                  }
                  if (value >= 10 ** 32) {
                      value /= 10 ** 32;
                      result += 32;
                  }
                  if (value >= 10 ** 16) {
                      value /= 10 ** 16;
                      result += 16;
                  }
                  if (value >= 10 ** 8) {
                      value /= 10 ** 8;
                      result += 8;
                  }
                  if (value >= 10 ** 4) {
                      value /= 10 ** 4;
                      result += 4;
                  }
                  if (value >= 10 ** 2) {
                      value /= 10 ** 2;
                      result += 2;
                  }
                  if (value >= 10 ** 1) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log10(value);
                  return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 256 of a positive value rounded towards zero.
           * Returns 0 if given 0.
           *
           * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
           */
          function log256(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >> 128 > 0) {
                      value >>= 128;
                      result += 16;
                  }
                  if (value >> 64 > 0) {
                      value >>= 64;
                      result += 8;
                  }
                  if (value >> 32 > 0) {
                      value >>= 32;
                      result += 4;
                  }
                  if (value >> 16 > 0) {
                      value >>= 16;
                      result += 2;
                  }
                  if (value >> 8 > 0) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log256(value);
                  return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
              }
          }
          /**
           * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
           */
          function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
              return uint8(rounding) % 2 == 1;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
      // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
      pragma solidity ^0.8.20;
      /**
       * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
       * checks.
       *
       * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
       * easily result in undesired exploitation or bugs, since developers usually
       * assume that overflows raise errors. `SafeCast` restores this intuition by
       * reverting the transaction when such an operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeCast {
          /**
           * @dev Value doesn't fit in an uint of `bits` size.
           */
          error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
          /**
           * @dev An int value doesn't fit in an uint of `bits` size.
           */
          error SafeCastOverflowedIntToUint(int256 value);
          /**
           * @dev Value doesn't fit in an int of `bits` size.
           */
          error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
          /**
           * @dev An uint value doesn't fit in an int of `bits` size.
           */
          error SafeCastOverflowedUintToInt(uint256 value);
          /**
           * @dev Returns the downcasted uint248 from uint256, reverting on
           * overflow (when the input is greater than largest uint248).
           *
           * Counterpart to Solidity's `uint248` operator.
           *
           * Requirements:
           *
           * - input must fit into 248 bits
           */
          function toUint248(uint256 value) internal pure returns (uint248) {
              if (value > type(uint248).max) {
                  revert SafeCastOverflowedUintDowncast(248, value);
              }
              return uint248(value);
          }
          /**
           * @dev Returns the downcasted uint240 from uint256, reverting on
           * overflow (when the input is greater than largest uint240).
           *
           * Counterpart to Solidity's `uint240` operator.
           *
           * Requirements:
           *
           * - input must fit into 240 bits
           */
          function toUint240(uint256 value) internal pure returns (uint240) {
              if (value > type(uint240).max) {
                  revert SafeCastOverflowedUintDowncast(240, value);
              }
              return uint240(value);
          }
          /**
           * @dev Returns the downcasted uint232 from uint256, reverting on
           * overflow (when the input is greater than largest uint232).
           *
           * Counterpart to Solidity's `uint232` operator.
           *
           * Requirements:
           *
           * - input must fit into 232 bits
           */
          function toUint232(uint256 value) internal pure returns (uint232) {
              if (value > type(uint232).max) {
                  revert SafeCastOverflowedUintDowncast(232, value);
              }
              return uint232(value);
          }
          /**
           * @dev Returns the downcasted uint224 from uint256, reverting on
           * overflow (when the input is greater than largest uint224).
           *
           * Counterpart to Solidity's `uint224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           */
          function toUint224(uint256 value) internal pure returns (uint224) {
              if (value > type(uint224).max) {
                  revert SafeCastOverflowedUintDowncast(224, value);
              }
              return uint224(value);
          }
          /**
           * @dev Returns the downcasted uint216 from uint256, reverting on
           * overflow (when the input is greater than largest uint216).
           *
           * Counterpart to Solidity's `uint216` operator.
           *
           * Requirements:
           *
           * - input must fit into 216 bits
           */
          function toUint216(uint256 value) internal pure returns (uint216) {
              if (value > type(uint216).max) {
                  revert SafeCastOverflowedUintDowncast(216, value);
              }
              return uint216(value);
          }
          /**
           * @dev Returns the downcasted uint208 from uint256, reverting on
           * overflow (when the input is greater than largest uint208).
           *
           * Counterpart to Solidity's `uint208` operator.
           *
           * Requirements:
           *
           * - input must fit into 208 bits
           */
          function toUint208(uint256 value) internal pure returns (uint208) {
              if (value > type(uint208).max) {
                  revert SafeCastOverflowedUintDowncast(208, value);
              }
              return uint208(value);
          }
          /**
           * @dev Returns the downcasted uint200 from uint256, reverting on
           * overflow (when the input is greater than largest uint200).
           *
           * Counterpart to Solidity's `uint200` operator.
           *
           * Requirements:
           *
           * - input must fit into 200 bits
           */
          function toUint200(uint256 value) internal pure returns (uint200) {
              if (value > type(uint200).max) {
                  revert SafeCastOverflowedUintDowncast(200, value);
              }
              return uint200(value);
          }
          /**
           * @dev Returns the downcasted uint192 from uint256, reverting on
           * overflow (when the input is greater than largest uint192).
           *
           * Counterpart to Solidity's `uint192` operator.
           *
           * Requirements:
           *
           * - input must fit into 192 bits
           */
          function toUint192(uint256 value) internal pure returns (uint192) {
              if (value > type(uint192).max) {
                  revert SafeCastOverflowedUintDowncast(192, value);
              }
              return uint192(value);
          }
          /**
           * @dev Returns the downcasted uint184 from uint256, reverting on
           * overflow (when the input is greater than largest uint184).
           *
           * Counterpart to Solidity's `uint184` operator.
           *
           * Requirements:
           *
           * - input must fit into 184 bits
           */
          function toUint184(uint256 value) internal pure returns (uint184) {
              if (value > type(uint184).max) {
                  revert SafeCastOverflowedUintDowncast(184, value);
              }
              return uint184(value);
          }
          /**
           * @dev Returns the downcasted uint176 from uint256, reverting on
           * overflow (when the input is greater than largest uint176).
           *
           * Counterpart to Solidity's `uint176` operator.
           *
           * Requirements:
           *
           * - input must fit into 176 bits
           */
          function toUint176(uint256 value) internal pure returns (uint176) {
              if (value > type(uint176).max) {
                  revert SafeCastOverflowedUintDowncast(176, value);
              }
              return uint176(value);
          }
          /**
           * @dev Returns the downcasted uint168 from uint256, reverting on
           * overflow (when the input is greater than largest uint168).
           *
           * Counterpart to Solidity's `uint168` operator.
           *
           * Requirements:
           *
           * - input must fit into 168 bits
           */
          function toUint168(uint256 value) internal pure returns (uint168) {
              if (value > type(uint168).max) {
                  revert SafeCastOverflowedUintDowncast(168, value);
              }
              return uint168(value);
          }
          /**
           * @dev Returns the downcasted uint160 from uint256, reverting on
           * overflow (when the input is greater than largest uint160).
           *
           * Counterpart to Solidity's `uint160` operator.
           *
           * Requirements:
           *
           * - input must fit into 160 bits
           */
          function toUint160(uint256 value) internal pure returns (uint160) {
              if (value > type(uint160).max) {
                  revert SafeCastOverflowedUintDowncast(160, value);
              }
              return uint160(value);
          }
          /**
           * @dev Returns the downcasted uint152 from uint256, reverting on
           * overflow (when the input is greater than largest uint152).
           *
           * Counterpart to Solidity's `uint152` operator.
           *
           * Requirements:
           *
           * - input must fit into 152 bits
           */
          function toUint152(uint256 value) internal pure returns (uint152) {
              if (value > type(uint152).max) {
                  revert SafeCastOverflowedUintDowncast(152, value);
              }
              return uint152(value);
          }
          /**
           * @dev Returns the downcasted uint144 from uint256, reverting on
           * overflow (when the input is greater than largest uint144).
           *
           * Counterpart to Solidity's `uint144` operator.
           *
           * Requirements:
           *
           * - input must fit into 144 bits
           */
          function toUint144(uint256 value) internal pure returns (uint144) {
              if (value > type(uint144).max) {
                  revert SafeCastOverflowedUintDowncast(144, value);
              }
              return uint144(value);
          }
          /**
           * @dev Returns the downcasted uint136 from uint256, reverting on
           * overflow (when the input is greater than largest uint136).
           *
           * Counterpart to Solidity's `uint136` operator.
           *
           * Requirements:
           *
           * - input must fit into 136 bits
           */
          function toUint136(uint256 value) internal pure returns (uint136) {
              if (value > type(uint136).max) {
                  revert SafeCastOverflowedUintDowncast(136, value);
              }
              return uint136(value);
          }
          /**
           * @dev Returns the downcasted uint128 from uint256, reverting on
           * overflow (when the input is greater than largest uint128).
           *
           * Counterpart to Solidity's `uint128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           */
          function toUint128(uint256 value) internal pure returns (uint128) {
              if (value > type(uint128).max) {
                  revert SafeCastOverflowedUintDowncast(128, value);
              }
              return uint128(value);
          }
          /**
           * @dev Returns the downcasted uint120 from uint256, reverting on
           * overflow (when the input is greater than largest uint120).
           *
           * Counterpart to Solidity's `uint120` operator.
           *
           * Requirements:
           *
           * - input must fit into 120 bits
           */
          function toUint120(uint256 value) internal pure returns (uint120) {
              if (value > type(uint120).max) {
                  revert SafeCastOverflowedUintDowncast(120, value);
              }
              return uint120(value);
          }
          /**
           * @dev Returns the downcasted uint112 from uint256, reverting on
           * overflow (when the input is greater than largest uint112).
           *
           * Counterpart to Solidity's `uint112` operator.
           *
           * Requirements:
           *
           * - input must fit into 112 bits
           */
          function toUint112(uint256 value) internal pure returns (uint112) {
              if (value > type(uint112).max) {
                  revert SafeCastOverflowedUintDowncast(112, value);
              }
              return uint112(value);
          }
          /**
           * @dev Returns the downcasted uint104 from uint256, reverting on
           * overflow (when the input is greater than largest uint104).
           *
           * Counterpart to Solidity's `uint104` operator.
           *
           * Requirements:
           *
           * - input must fit into 104 bits
           */
          function toUint104(uint256 value) internal pure returns (uint104) {
              if (value > type(uint104).max) {
                  revert SafeCastOverflowedUintDowncast(104, value);
              }
              return uint104(value);
          }
          /**
           * @dev Returns the downcasted uint96 from uint256, reverting on
           * overflow (when the input is greater than largest uint96).
           *
           * Counterpart to Solidity's `uint96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           */
          function toUint96(uint256 value) internal pure returns (uint96) {
              if (value > type(uint96).max) {
                  revert SafeCastOverflowedUintDowncast(96, value);
              }
              return uint96(value);
          }
          /**
           * @dev Returns the downcasted uint88 from uint256, reverting on
           * overflow (when the input is greater than largest uint88).
           *
           * Counterpart to Solidity's `uint88` operator.
           *
           * Requirements:
           *
           * - input must fit into 88 bits
           */
          function toUint88(uint256 value) internal pure returns (uint88) {
              if (value > type(uint88).max) {
                  revert SafeCastOverflowedUintDowncast(88, value);
              }
              return uint88(value);
          }
          /**
           * @dev Returns the downcasted uint80 from uint256, reverting on
           * overflow (when the input is greater than largest uint80).
           *
           * Counterpart to Solidity's `uint80` operator.
           *
           * Requirements:
           *
           * - input must fit into 80 bits
           */
          function toUint80(uint256 value) internal pure returns (uint80) {
              if (value > type(uint80).max) {
                  revert SafeCastOverflowedUintDowncast(80, value);
              }
              return uint80(value);
          }
          /**
           * @dev Returns the downcasted uint72 from uint256, reverting on
           * overflow (when the input is greater than largest uint72).
           *
           * Counterpart to Solidity's `uint72` operator.
           *
           * Requirements:
           *
           * - input must fit into 72 bits
           */
          function toUint72(uint256 value) internal pure returns (uint72) {
              if (value > type(uint72).max) {
                  revert SafeCastOverflowedUintDowncast(72, value);
              }
              return uint72(value);
          }
          /**
           * @dev Returns the downcasted uint64 from uint256, reverting on
           * overflow (when the input is greater than largest uint64).
           *
           * Counterpart to Solidity's `uint64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           */
          function toUint64(uint256 value) internal pure returns (uint64) {
              if (value > type(uint64).max) {
                  revert SafeCastOverflowedUintDowncast(64, value);
              }
              return uint64(value);
          }
          /**
           * @dev Returns the downcasted uint56 from uint256, reverting on
           * overflow (when the input is greater than largest uint56).
           *
           * Counterpart to Solidity's `uint56` operator.
           *
           * Requirements:
           *
           * - input must fit into 56 bits
           */
          function toUint56(uint256 value) internal pure returns (uint56) {
              if (value > type(uint56).max) {
                  revert SafeCastOverflowedUintDowncast(56, value);
              }
              return uint56(value);
          }
          /**
           * @dev Returns the downcasted uint48 from uint256, reverting on
           * overflow (when the input is greater than largest uint48).
           *
           * Counterpart to Solidity's `uint48` operator.
           *
           * Requirements:
           *
           * - input must fit into 48 bits
           */
          function toUint48(uint256 value) internal pure returns (uint48) {
              if (value > type(uint48).max) {
                  revert SafeCastOverflowedUintDowncast(48, value);
              }
              return uint48(value);
          }
          /**
           * @dev Returns the downcasted uint40 from uint256, reverting on
           * overflow (when the input is greater than largest uint40).
           *
           * Counterpart to Solidity's `uint40` operator.
           *
           * Requirements:
           *
           * - input must fit into 40 bits
           */
          function toUint40(uint256 value) internal pure returns (uint40) {
              if (value > type(uint40).max) {
                  revert SafeCastOverflowedUintDowncast(40, value);
              }
              return uint40(value);
          }
          /**
           * @dev Returns the downcasted uint32 from uint256, reverting on
           * overflow (when the input is greater than largest uint32).
           *
           * Counterpart to Solidity's `uint32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           */
          function toUint32(uint256 value) internal pure returns (uint32) {
              if (value > type(uint32).max) {
                  revert SafeCastOverflowedUintDowncast(32, value);
              }
              return uint32(value);
          }
          /**
           * @dev Returns the downcasted uint24 from uint256, reverting on
           * overflow (when the input is greater than largest uint24).
           *
           * Counterpart to Solidity's `uint24` operator.
           *
           * Requirements:
           *
           * - input must fit into 24 bits
           */
          function toUint24(uint256 value) internal pure returns (uint24) {
              if (value > type(uint24).max) {
                  revert SafeCastOverflowedUintDowncast(24, value);
              }
              return uint24(value);
          }
          /**
           * @dev Returns the downcasted uint16 from uint256, reverting on
           * overflow (when the input is greater than largest uint16).
           *
           * Counterpart to Solidity's `uint16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           */
          function toUint16(uint256 value) internal pure returns (uint16) {
              if (value > type(uint16).max) {
                  revert SafeCastOverflowedUintDowncast(16, value);
              }
              return uint16(value);
          }
          /**
           * @dev Returns the downcasted uint8 from uint256, reverting on
           * overflow (when the input is greater than largest uint8).
           *
           * Counterpart to Solidity's `uint8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits
           */
          function toUint8(uint256 value) internal pure returns (uint8) {
              if (value > type(uint8).max) {
                  revert SafeCastOverflowedUintDowncast(8, value);
              }
              return uint8(value);
          }
          /**
           * @dev Converts a signed int256 into an unsigned uint256.
           *
           * Requirements:
           *
           * - input must be greater than or equal to 0.
           */
          function toUint256(int256 value) internal pure returns (uint256) {
              if (value < 0) {
                  revert SafeCastOverflowedIntToUint(value);
              }
              return uint256(value);
          }
          /**
           * @dev Returns the downcasted int248 from int256, reverting on
           * overflow (when the input is less than smallest int248 or
           * greater than largest int248).
           *
           * Counterpart to Solidity's `int248` operator.
           *
           * Requirements:
           *
           * - input must fit into 248 bits
           */
          function toInt248(int256 value) internal pure returns (int248 downcasted) {
              downcasted = int248(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(248, value);
              }
          }
          /**
           * @dev Returns the downcasted int240 from int256, reverting on
           * overflow (when the input is less than smallest int240 or
           * greater than largest int240).
           *
           * Counterpart to Solidity's `int240` operator.
           *
           * Requirements:
           *
           * - input must fit into 240 bits
           */
          function toInt240(int256 value) internal pure returns (int240 downcasted) {
              downcasted = int240(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(240, value);
              }
          }
          /**
           * @dev Returns the downcasted int232 from int256, reverting on
           * overflow (when the input is less than smallest int232 or
           * greater than largest int232).
           *
           * Counterpart to Solidity's `int232` operator.
           *
           * Requirements:
           *
           * - input must fit into 232 bits
           */
          function toInt232(int256 value) internal pure returns (int232 downcasted) {
              downcasted = int232(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(232, value);
              }
          }
          /**
           * @dev Returns the downcasted int224 from int256, reverting on
           * overflow (when the input is less than smallest int224 or
           * greater than largest int224).
           *
           * Counterpart to Solidity's `int224` operator.
           *
           * Requirements:
           *
           * - input must fit into 224 bits
           */
          function toInt224(int256 value) internal pure returns (int224 downcasted) {
              downcasted = int224(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(224, value);
              }
          }
          /**
           * @dev Returns the downcasted int216 from int256, reverting on
           * overflow (when the input is less than smallest int216 or
           * greater than largest int216).
           *
           * Counterpart to Solidity's `int216` operator.
           *
           * Requirements:
           *
           * - input must fit into 216 bits
           */
          function toInt216(int256 value) internal pure returns (int216 downcasted) {
              downcasted = int216(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(216, value);
              }
          }
          /**
           * @dev Returns the downcasted int208 from int256, reverting on
           * overflow (when the input is less than smallest int208 or
           * greater than largest int208).
           *
           * Counterpart to Solidity's `int208` operator.
           *
           * Requirements:
           *
           * - input must fit into 208 bits
           */
          function toInt208(int256 value) internal pure returns (int208 downcasted) {
              downcasted = int208(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(208, value);
              }
          }
          /**
           * @dev Returns the downcasted int200 from int256, reverting on
           * overflow (when the input is less than smallest int200 or
           * greater than largest int200).
           *
           * Counterpart to Solidity's `int200` operator.
           *
           * Requirements:
           *
           * - input must fit into 200 bits
           */
          function toInt200(int256 value) internal pure returns (int200 downcasted) {
              downcasted = int200(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(200, value);
              }
          }
          /**
           * @dev Returns the downcasted int192 from int256, reverting on
           * overflow (when the input is less than smallest int192 or
           * greater than largest int192).
           *
           * Counterpart to Solidity's `int192` operator.
           *
           * Requirements:
           *
           * - input must fit into 192 bits
           */
          function toInt192(int256 value) internal pure returns (int192 downcasted) {
              downcasted = int192(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(192, value);
              }
          }
          /**
           * @dev Returns the downcasted int184 from int256, reverting on
           * overflow (when the input is less than smallest int184 or
           * greater than largest int184).
           *
           * Counterpart to Solidity's `int184` operator.
           *
           * Requirements:
           *
           * - input must fit into 184 bits
           */
          function toInt184(int256 value) internal pure returns (int184 downcasted) {
              downcasted = int184(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(184, value);
              }
          }
          /**
           * @dev Returns the downcasted int176 from int256, reverting on
           * overflow (when the input is less than smallest int176 or
           * greater than largest int176).
           *
           * Counterpart to Solidity's `int176` operator.
           *
           * Requirements:
           *
           * - input must fit into 176 bits
           */
          function toInt176(int256 value) internal pure returns (int176 downcasted) {
              downcasted = int176(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(176, value);
              }
          }
          /**
           * @dev Returns the downcasted int168 from int256, reverting on
           * overflow (when the input is less than smallest int168 or
           * greater than largest int168).
           *
           * Counterpart to Solidity's `int168` operator.
           *
           * Requirements:
           *
           * - input must fit into 168 bits
           */
          function toInt168(int256 value) internal pure returns (int168 downcasted) {
              downcasted = int168(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(168, value);
              }
          }
          /**
           * @dev Returns the downcasted int160 from int256, reverting on
           * overflow (when the input is less than smallest int160 or
           * greater than largest int160).
           *
           * Counterpart to Solidity's `int160` operator.
           *
           * Requirements:
           *
           * - input must fit into 160 bits
           */
          function toInt160(int256 value) internal pure returns (int160 downcasted) {
              downcasted = int160(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(160, value);
              }
          }
          /**
           * @dev Returns the downcasted int152 from int256, reverting on
           * overflow (when the input is less than smallest int152 or
           * greater than largest int152).
           *
           * Counterpart to Solidity's `int152` operator.
           *
           * Requirements:
           *
           * - input must fit into 152 bits
           */
          function toInt152(int256 value) internal pure returns (int152 downcasted) {
              downcasted = int152(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(152, value);
              }
          }
          /**
           * @dev Returns the downcasted int144 from int256, reverting on
           * overflow (when the input is less than smallest int144 or
           * greater than largest int144).
           *
           * Counterpart to Solidity's `int144` operator.
           *
           * Requirements:
           *
           * - input must fit into 144 bits
           */
          function toInt144(int256 value) internal pure returns (int144 downcasted) {
              downcasted = int144(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(144, value);
              }
          }
          /**
           * @dev Returns the downcasted int136 from int256, reverting on
           * overflow (when the input is less than smallest int136 or
           * greater than largest int136).
           *
           * Counterpart to Solidity's `int136` operator.
           *
           * Requirements:
           *
           * - input must fit into 136 bits
           */
          function toInt136(int256 value) internal pure returns (int136 downcasted) {
              downcasted = int136(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(136, value);
              }
          }
          /**
           * @dev Returns the downcasted int128 from int256, reverting on
           * overflow (when the input is less than smallest int128 or
           * greater than largest int128).
           *
           * Counterpart to Solidity's `int128` operator.
           *
           * Requirements:
           *
           * - input must fit into 128 bits
           */
          function toInt128(int256 value) internal pure returns (int128 downcasted) {
              downcasted = int128(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(128, value);
              }
          }
          /**
           * @dev Returns the downcasted int120 from int256, reverting on
           * overflow (when the input is less than smallest int120 or
           * greater than largest int120).
           *
           * Counterpart to Solidity's `int120` operator.
           *
           * Requirements:
           *
           * - input must fit into 120 bits
           */
          function toInt120(int256 value) internal pure returns (int120 downcasted) {
              downcasted = int120(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(120, value);
              }
          }
          /**
           * @dev Returns the downcasted int112 from int256, reverting on
           * overflow (when the input is less than smallest int112 or
           * greater than largest int112).
           *
           * Counterpart to Solidity's `int112` operator.
           *
           * Requirements:
           *
           * - input must fit into 112 bits
           */
          function toInt112(int256 value) internal pure returns (int112 downcasted) {
              downcasted = int112(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(112, value);
              }
          }
          /**
           * @dev Returns the downcasted int104 from int256, reverting on
           * overflow (when the input is less than smallest int104 or
           * greater than largest int104).
           *
           * Counterpart to Solidity's `int104` operator.
           *
           * Requirements:
           *
           * - input must fit into 104 bits
           */
          function toInt104(int256 value) internal pure returns (int104 downcasted) {
              downcasted = int104(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(104, value);
              }
          }
          /**
           * @dev Returns the downcasted int96 from int256, reverting on
           * overflow (when the input is less than smallest int96 or
           * greater than largest int96).
           *
           * Counterpart to Solidity's `int96` operator.
           *
           * Requirements:
           *
           * - input must fit into 96 bits
           */
          function toInt96(int256 value) internal pure returns (int96 downcasted) {
              downcasted = int96(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(96, value);
              }
          }
          /**
           * @dev Returns the downcasted int88 from int256, reverting on
           * overflow (when the input is less than smallest int88 or
           * greater than largest int88).
           *
           * Counterpart to Solidity's `int88` operator.
           *
           * Requirements:
           *
           * - input must fit into 88 bits
           */
          function toInt88(int256 value) internal pure returns (int88 downcasted) {
              downcasted = int88(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(88, value);
              }
          }
          /**
           * @dev Returns the downcasted int80 from int256, reverting on
           * overflow (when the input is less than smallest int80 or
           * greater than largest int80).
           *
           * Counterpart to Solidity's `int80` operator.
           *
           * Requirements:
           *
           * - input must fit into 80 bits
           */
          function toInt80(int256 value) internal pure returns (int80 downcasted) {
              downcasted = int80(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(80, value);
              }
          }
          /**
           * @dev Returns the downcasted int72 from int256, reverting on
           * overflow (when the input is less than smallest int72 or
           * greater than largest int72).
           *
           * Counterpart to Solidity's `int72` operator.
           *
           * Requirements:
           *
           * - input must fit into 72 bits
           */
          function toInt72(int256 value) internal pure returns (int72 downcasted) {
              downcasted = int72(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(72, value);
              }
          }
          /**
           * @dev Returns the downcasted int64 from int256, reverting on
           * overflow (when the input is less than smallest int64 or
           * greater than largest int64).
           *
           * Counterpart to Solidity's `int64` operator.
           *
           * Requirements:
           *
           * - input must fit into 64 bits
           */
          function toInt64(int256 value) internal pure returns (int64 downcasted) {
              downcasted = int64(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(64, value);
              }
          }
          /**
           * @dev Returns the downcasted int56 from int256, reverting on
           * overflow (when the input is less than smallest int56 or
           * greater than largest int56).
           *
           * Counterpart to Solidity's `int56` operator.
           *
           * Requirements:
           *
           * - input must fit into 56 bits
           */
          function toInt56(int256 value) internal pure returns (int56 downcasted) {
              downcasted = int56(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(56, value);
              }
          }
          /**
           * @dev Returns the downcasted int48 from int256, reverting on
           * overflow (when the input is less than smallest int48 or
           * greater than largest int48).
           *
           * Counterpart to Solidity's `int48` operator.
           *
           * Requirements:
           *
           * - input must fit into 48 bits
           */
          function toInt48(int256 value) internal pure returns (int48 downcasted) {
              downcasted = int48(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(48, value);
              }
          }
          /**
           * @dev Returns the downcasted int40 from int256, reverting on
           * overflow (when the input is less than smallest int40 or
           * greater than largest int40).
           *
           * Counterpart to Solidity's `int40` operator.
           *
           * Requirements:
           *
           * - input must fit into 40 bits
           */
          function toInt40(int256 value) internal pure returns (int40 downcasted) {
              downcasted = int40(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(40, value);
              }
          }
          /**
           * @dev Returns the downcasted int32 from int256, reverting on
           * overflow (when the input is less than smallest int32 or
           * greater than largest int32).
           *
           * Counterpart to Solidity's `int32` operator.
           *
           * Requirements:
           *
           * - input must fit into 32 bits
           */
          function toInt32(int256 value) internal pure returns (int32 downcasted) {
              downcasted = int32(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(32, value);
              }
          }
          /**
           * @dev Returns the downcasted int24 from int256, reverting on
           * overflow (when the input is less than smallest int24 or
           * greater than largest int24).
           *
           * Counterpart to Solidity's `int24` operator.
           *
           * Requirements:
           *
           * - input must fit into 24 bits
           */
          function toInt24(int256 value) internal pure returns (int24 downcasted) {
              downcasted = int24(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(24, value);
              }
          }
          /**
           * @dev Returns the downcasted int16 from int256, reverting on
           * overflow (when the input is less than smallest int16 or
           * greater than largest int16).
           *
           * Counterpart to Solidity's `int16` operator.
           *
           * Requirements:
           *
           * - input must fit into 16 bits
           */
          function toInt16(int256 value) internal pure returns (int16 downcasted) {
              downcasted = int16(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(16, value);
              }
          }
          /**
           * @dev Returns the downcasted int8 from int256, reverting on
           * overflow (when the input is less than smallest int8 or
           * greater than largest int8).
           *
           * Counterpart to Solidity's `int8` operator.
           *
           * Requirements:
           *
           * - input must fit into 8 bits
           */
          function toInt8(int256 value) internal pure returns (int8 downcasted) {
              downcasted = int8(value);
              if (downcasted != value) {
                  revert SafeCastOverflowedIntDowncast(8, value);
              }
          }
          /**
           * @dev Converts an unsigned uint256 into a signed int256.
           *
           * Requirements:
           *
           * - input must be less than or equal to maxInt256.
           */
          function toInt256(uint256 value) internal pure returns (int256) {
              // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
              if (value > uint256(type(int256).max)) {
                  revert SafeCastOverflowedUintToInt(value);
              }
              return int256(value);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
      // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
      pragma solidity ^0.8.20;
      /**
       * @dev Library for reading and writing primitive types to specific storage slots.
       *
       * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
       * This library helps with reading and writing to such slots without the need for inline assembly.
       *
       * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
       *
       * Example usage to set ERC1967 implementation slot:
       * ```solidity
       * contract ERC1967 {
       *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
       *
       *     function _getImplementation() internal view returns (address) {
       *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
       *     }
       *
       *     function _setImplementation(address newImplementation) internal {
       *         require(newImplementation.code.length > 0);
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
          struct BooleanSlot {
              bool value;
          }
          struct Bytes32Slot {
              bytes32 value;
          }
          struct Uint256Slot {
              uint256 value;
          }
          struct StringSlot {
              string value;
          }
          struct BytesSlot {
              bytes value;
          }
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` with member `value` located at `slot`.
           */
          function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
           */
          function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` with member `value` located at `slot`.
           */
          function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
           */
          function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
      }
      // SPDX-License-Identifier: GPL-2.0-or-later
      pragma solidity =0.8.22;
      import '../interfaces/IMulticall.sol';
      /**
       * @title Multicall
       * @author Uniswap
       * @notice Adopted from https://github.com/Uniswap/v3-periphery/blob/1d69caf0d6c8cfeae9acd1f34ead30018d6e6400/contracts/base/Multicall.sol
       * @notice Enables calling multiple methods in a single call to the contract
       */
      abstract contract Multicall is IMulticall {
        /// @inheritdoc IMulticall
        function multicall(bytes[] calldata data) external override returns (bytes[] memory results) {
          uint256 dataLength = data.length;
          results = new bytes[](dataLength);
          for (uint256 i = 0; i < dataLength; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);
            if (!success) {
              // Next 5 lines from https://ethereum.stackexchange.com/a/83577
              if (result.length < 68) revert();
              assembly {
                result := add(result, 0x04)
              }
              revert(abi.decode(result, (string)));
            }
            results[i] = result;
          }
        }
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultAdmin} from './IVaultAdmin.sol';
      import {IVaultVersion} from './IVaultVersion.sol';
      import {IVaultFee} from './IVaultFee.sol';
      import {IVaultState} from './IVaultState.sol';
      import {IVaultValidators} from './IVaultValidators.sol';
      import {IVaultEnterExit} from './IVaultEnterExit.sol';
      import {IVaultOsToken} from './IVaultOsToken.sol';
      import {IVaultMev} from './IVaultMev.sol';
      import {IVaultEthStaking} from './IVaultEthStaking.sol';
      import {IVaultBlocklist} from './IVaultBlocklist.sol';
      import {IMulticall} from './IMulticall.sol';
      /**
       * @title IEthFoxVault
       * @author StakeWise
       * @notice Defines the interface for the EthFoxVault contract
       */
      interface IEthFoxVault is
        IVaultAdmin,
        IVaultVersion,
        IVaultFee,
        IVaultState,
        IVaultValidators,
        IVaultEnterExit,
        IVaultMev,
        IVaultEthStaking,
        IVaultBlocklist,
        IMulticall
      {
        /**
         * @notice Event emitted when a user is ejected from the Vault
         * @param user The address of the user
         * @param shares The amount of shares ejected
         */
        event UserEjected(address user, uint256 shares);
        /**
         * @dev Struct for initializing the EthFoxVault contract
         * @param admin The address of the Vault admin
         * @param ownMevEscrow The address of the MEV escrow contract
         * @param capacity The Vault stops accepting deposits after exceeding the capacity
         * @param feePercent The fee percent that is charged by the Vault
         * @param metadataIpfsHash The IPFS hash of the Vault's metadata file
         */
        struct EthFoxVaultInitParams {
          address admin;
          address ownMevEscrow;
          uint256 capacity;
          uint16 feePercent;
          string metadataIpfsHash;
        }
        /**
         * @notice Event emitted on EthFoxVault creation
         * @param admin The address of the Vault admin
         * @param ownMevEscrow The address of the MEV escrow contract
         * @param capacity The capacity of the Vault
         * @param feePercent The fee percent of the Vault
         * @param metadataIpfsHash The IPFS hash of the Vault metadata
         */
        event EthFoxVaultCreated(
          address admin,
          address ownMevEscrow,
          uint256 capacity,
          uint16 feePercent,
          string metadataIpfsHash
        );
        /**
         * @notice Initializes the EthFoxVault contract. Must transfer security deposit together with a call.
         * @param params The encoded parameters for initializing the EthFoxVault contract
         */
        function initialize(bytes calldata params) external payable;
        /**
         * @notice Ejects user from the Vault. Can only be called by the blocklist manager.
         *         The ejected user will be added to the blocklist and all his shares will be sent to the exit queue.
         * @param user The address of the user to eject
         */
        function ejectUser(address user) external;
      }
      // SPDX-License-Identifier: CC0-1.0
      pragma solidity =0.8.22;
      import {IValidatorsRegistry} from './IValidatorsRegistry.sol';
      /**
       * @title IEthValidatorsRegistry
       * @author Ethereum Foundation
       * @notice This is the Ethereum validators deposit contract interface.
       *         See https://github.com/ethereum/consensus-specs/blob/v1.2.0/solidity_deposit_contract/deposit_contract.sol.
       *         For more information see the Phase 0 specification under https://github.com/ethereum/consensus-specs.
       */
      interface IEthValidatorsRegistry is IValidatorsRegistry {
        /// @notice Submit a Phase 0 DepositData object.
        /// @param pubkey A BLS12-381 public key.
        /// @param withdrawal_credentials Commitment to a public key for withdrawals.
        /// @param signature A BLS12-381 signature.
        /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
        /// Used as a protection against malformed input.
        function deposit(
          bytes calldata pubkey,
          bytes calldata withdrawal_credentials,
          bytes calldata signature,
          bytes32 deposit_data_root
        ) external payable;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IERC5267} from '@openzeppelin/contracts/interfaces/IERC5267.sol';
      /**
       * @title IKeeperOracles
       * @author StakeWise
       * @notice Defines the interface for the KeeperOracles contract
       */
      interface IKeeperOracles is IERC5267 {
        /**
         * @notice Event emitted on the oracle addition
         * @param oracle The address of the added oracle
         */
        event OracleAdded(address indexed oracle);
        /**
         * @notice Event emitted on the oracle removal
         * @param oracle The address of the removed oracle
         */
        event OracleRemoved(address indexed oracle);
        /**
         * @notice Event emitted on oracles config update
         * @param configIpfsHash The IPFS hash of the new config
         */
        event ConfigUpdated(string configIpfsHash);
        /**
         * @notice Function for verifying whether oracle is registered or not
         * @param oracle The address of the oracle to check
         * @return `true` for the registered oracle, `false` otherwise
         */
        function isOracle(address oracle) external view returns (bool);
        /**
         * @notice Total Oracles
         * @return The total number of oracles registered
         */
        function totalOracles() external view returns (uint256);
        /**
         * @notice Function for adding oracle to the set
         * @param oracle The address of the oracle to add
         */
        function addOracle(address oracle) external;
        /**
         * @notice Function for removing oracle from the set
         * @param oracle The address of the oracle to remove
         */
        function removeOracle(address oracle) external;
        /**
         * @notice Function for updating the config IPFS hash
         * @param configIpfsHash The new config IPFS hash
         */
        function updateConfig(string calldata configIpfsHash) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IKeeperOracles} from './IKeeperOracles.sol';
      /**
       * @title IKeeperRewards
       * @author StakeWise
       * @notice Defines the interface for the Keeper contract rewards
       */
      interface IKeeperRewards is IKeeperOracles {
        /**
         * @notice Event emitted on rewards update
         * @param caller The address of the function caller
         * @param rewardsRoot The new rewards merkle tree root
         * @param avgRewardPerSecond The new average reward per second
         * @param updateTimestamp The update timestamp used for rewards calculation
         * @param nonce The nonce used for verifying signatures
         * @param rewardsIpfsHash The new rewards IPFS hash
         */
        event RewardsUpdated(
          address indexed caller,
          bytes32 indexed rewardsRoot,
          uint256 avgRewardPerSecond,
          uint64 updateTimestamp,
          uint64 nonce,
          string rewardsIpfsHash
        );
        /**
         * @notice Event emitted on Vault harvest
         * @param vault The address of the Vault
         * @param rewardsRoot The rewards merkle tree root
         * @param totalAssetsDelta The Vault total assets delta since last sync. Can be negative in case of penalty/slashing.
         * @param unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
         */
        event Harvested(
          address indexed vault,
          bytes32 indexed rewardsRoot,
          int256 totalAssetsDelta,
          uint256 unlockedMevDelta
        );
        /**
         * @notice Event emitted on rewards min oracles number update
         * @param oracles The new minimum number of oracles required to update rewards
         */
        event RewardsMinOraclesUpdated(uint256 oracles);
        /**
         * @notice A struct containing the last synced Vault's cumulative reward
         * @param assets The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
         * @param nonce The nonce of the last sync
         */
        struct Reward {
          int192 assets;
          uint64 nonce;
        }
        /**
         * @notice A struct containing the last unlocked Vault's cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
         * @param assets The shared MEV Vault's cumulative execution reward that can be withdrawn
         * @param nonce The nonce of the last sync
         */
        struct UnlockedMevReward {
          uint192 assets;
          uint64 nonce;
        }
        /**
         * @notice A struct containing parameters for rewards update
         * @param rewardsRoot The new rewards merkle root
         * @param avgRewardPerSecond The new average reward per second
         * @param updateTimestamp The update timestamp used for rewards calculation
         * @param rewardsIpfsHash The new IPFS hash with all the Vaults' rewards for the new root
         * @param signatures The concatenation of the Oracles' signatures
         */
        struct RewardsUpdateParams {
          bytes32 rewardsRoot;
          uint256 avgRewardPerSecond;
          uint64 updateTimestamp;
          string rewardsIpfsHash;
          bytes signatures;
        }
        /**
         * @notice A struct containing parameters for harvesting rewards. Can only be called by Vault.
         * @param rewardsRoot The rewards merkle root
         * @param reward The Vault cumulative reward earned since the start. Can be negative in case of penalty/slashing.
         * @param unlockedMevReward The Vault cumulative execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
         * @param proof The proof to verify that Vault's reward is correct
         */
        struct HarvestParams {
          bytes32 rewardsRoot;
          int160 reward;
          uint160 unlockedMevReward;
          bytes32[] proof;
        }
        /**
         * @notice Previous Rewards Root
         * @return The previous merkle tree root of the rewards accumulated by the Vaults
         */
        function prevRewardsRoot() external view returns (bytes32);
        /**
         * @notice Rewards Root
         * @return The latest merkle tree root of the rewards accumulated by the Vaults
         */
        function rewardsRoot() external view returns (bytes32);
        /**
         * @notice Rewards Nonce
         * @return The nonce used for updating rewards merkle tree root
         */
        function rewardsNonce() external view returns (uint64);
        /**
         * @notice The last rewards update
         * @return The timestamp of the last rewards update
         */
        function lastRewardsTimestamp() external view returns (uint64);
        /**
         * @notice The minimum number of oracles required to update rewards
         * @return The minimum number of oracles
         */
        function rewardsMinOracles() external view returns (uint256);
        /**
         * @notice The rewards delay
         * @return The delay in seconds between rewards updates
         */
        function rewardsDelay() external view returns (uint256);
        /**
         * @notice Get last synced Vault cumulative reward
         * @param vault The address of the Vault
         * @return assets The last synced reward assets
         * @return nonce The last synced reward nonce
         */
        function rewards(address vault) external view returns (int192 assets, uint64 nonce);
        /**
         * @notice Get last unlocked shared MEV Vault cumulative reward
         * @param vault The address of the Vault
         * @return assets The last synced reward assets
         * @return nonce The last synced reward nonce
         */
        function unlockedMevRewards(address vault) external view returns (uint192 assets, uint64 nonce);
        /**
         * @notice Checks whether Vault must be harvested
         * @param vault The address of the Vault
         * @return `true` if the Vault requires harvesting, `false` otherwise
         */
        function isHarvestRequired(address vault) external view returns (bool);
        /**
         * @notice Checks whether the Vault can be harvested
         * @param vault The address of the Vault
         * @return `true` if Vault can be harvested, `false` otherwise
         */
        function canHarvest(address vault) external view returns (bool);
        /**
         * @notice Checks whether rewards can be updated
         * @return `true` if rewards can be updated, `false` otherwise
         */
        function canUpdateRewards() external view returns (bool);
        /**
         * @notice Checks whether the Vault has registered validators
         * @param vault The address of the Vault
         * @return `true` if Vault is collateralized, `false` otherwise
         */
        function isCollateralized(address vault) external view returns (bool);
        /**
         * @notice Update rewards data
         * @param params The struct containing rewards update parameters
         */
        function updateRewards(RewardsUpdateParams calldata params) external;
        /**
         * @notice Harvest rewards. Can be called only by Vault.
         * @param params The struct containing rewards harvesting parameters
         * @return totalAssetsDelta The total reward/penalty accumulated by the Vault since the last sync
         * @return unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
         * @return harvested `true` when the rewards were harvested, `false` otherwise
         */
        function harvest(
          HarvestParams calldata params
        ) external returns (int256 totalAssetsDelta, uint256 unlockedMevDelta, bool harvested);
        /**
         * @notice Set min number of oracles for confirming rewards update. Can only be called by the owner.
         * @param _rewardsMinOracles The new min number of oracles for confirming rewards update
         */
        function setRewardsMinOracles(uint256 _rewardsMinOracles) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IKeeperRewards} from './IKeeperRewards.sol';
      import {IKeeperOracles} from './IKeeperOracles.sol';
      /**
       * @title IKeeperValidators
       * @author StakeWise
       * @notice Defines the interface for the Keeper validators
       */
      interface IKeeperValidators is IKeeperOracles, IKeeperRewards {
        /**
         * @notice Event emitted on validators approval
         * @param vault The address of the Vault
         * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
         */
        event ValidatorsApproval(address indexed vault, string exitSignaturesIpfsHash);
        /**
         * @notice Event emitted on exit signatures update
         * @param caller The address of the function caller
         * @param vault The address of the Vault
         * @param nonce The nonce used for verifying Oracles' signatures
         * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
         */
        event ExitSignaturesUpdated(
          address indexed caller,
          address indexed vault,
          uint256 nonce,
          string exitSignaturesIpfsHash
        );
        /**
         * @notice Event emitted on validators min oracles number update
         * @param oracles The new minimum number of oracles required to approve validators
         */
        event ValidatorsMinOraclesUpdated(uint256 oracles);
        /**
         * @notice Get nonce for the next vault exit signatures update
         * @param vault The address of the Vault to get the nonce for
         * @return The nonce of the Vault for updating signatures
         */
        function exitSignaturesNonces(address vault) external view returns (uint256);
        /**
         * @notice Struct for approving registration of one or more validators
         * @param validatorsRegistryRoot The deposit data root used to verify that oracles approved validators
         * @param deadline The deadline for submitting the approval
         * @param validators The concatenation of the validators' public key, signature and deposit data root
         * @param signatures The concatenation of Oracles' signatures
         * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
         */
        struct ApprovalParams {
          bytes32 validatorsRegistryRoot;
          uint256 deadline;
          bytes validators;
          bytes signatures;
          string exitSignaturesIpfsHash;
        }
        /**
         * @notice The minimum number of oracles required to update validators
         * @return The minimum number of oracles
         */
        function validatorsMinOracles() external view returns (uint256);
        /**
         * @notice Function for approving validators registration
         * @param params The parameters for approving validators registration
         */
        function approveValidators(ApprovalParams calldata params) external;
        /**
         * @notice Function for updating exit signatures for every hard fork
         * @param vault The address of the Vault to update signatures for
         * @param deadline The deadline for submitting signatures update
         * @param exitSignaturesIpfsHash The IPFS hash with the validators' exit signatures
         * @param oraclesSignatures The concatenation of Oracles' signatures
         */
        function updateExitSignatures(
          address vault,
          uint256 deadline,
          string calldata exitSignaturesIpfsHash,
          bytes calldata oraclesSignatures
        ) external;
        /**
         * @notice Function for updating validators min oracles number
         * @param _validatorsMinOracles The new minimum number of oracles required to approve validators
         */
        function setValidatorsMinOracles(uint256 _validatorsMinOracles) external;
      }
      // SPDX-License-Identifier: GPL-2.0-or-later
      pragma solidity =0.8.22;
      /**
       * @title Multicall
       * @author Uniswap
       * @notice Adopted from https://github.com/Uniswap/v3-periphery/blob/1d69caf0d6c8cfeae9acd1f34ead30018d6e6400/contracts/base/Multicall.sol
       * @notice Enables calling multiple methods in a single call to the contract
       */
      interface IMulticall {
        /**
         * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
         * @param data The encoded function data for each of the calls to make to this contract
         * @return results The results from each of the calls passed in via data
         */
        function multicall(bytes[] calldata data) external returns (bytes[] memory results);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      /**
       * @title IOwnMevEscrow
       * @author StakeWise
       * @notice Defines the interface for the OwnMevEscrow contract
       */
      interface IOwnMevEscrow {
        /**
         * @notice Event emitted on received MEV
         * @param assets The amount of MEV assets received
         */
        event MevReceived(uint256 assets);
        /**
         * @notice Event emitted on harvest
         * @param assets The amount of assets withdrawn
         */
        event Harvested(uint256 assets);
        /**
         * @notice Vault address
         * @return The address of the vault that owns the escrow
         */
        function vault() external view returns (address payable);
        /**
         * @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
         * @dev IMPORTANT: because control is transferred to the Vault, care must be
         *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
         *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
         * @return assets The amount of assets withdrawn
         */
        function harvest() external returns (uint256 assets);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      /**
       * @title ISharedMevEscrow
       * @author StakeWise
       * @notice Defines the interface for the SharedMevEscrow contract
       */
      interface ISharedMevEscrow {
        /**
         * @notice Event emitted on received MEV
         * @param assets The amount of MEV assets received
         */
        event MevReceived(uint256 assets);
        /**
         * @notice Event emitted on harvest
         * @param caller The function caller
         * @param assets The amount of assets withdrawn
         */
        event Harvested(address indexed caller, uint256 assets);
        /**
         * @notice Withdraws MEV accumulated in the escrow. Can be called only by the Vault.
         * @dev IMPORTANT: because control is transferred to the Vault, care must be
         *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
         *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
         * @param assets The amount of assets to withdraw
         */
        function harvest(uint256 assets) external;
      }
      // SPDX-License-Identifier: CC0-1.0
      pragma solidity =0.8.22;
      /**
       * @title IValidatorsRegistry
       * @author Ethereum Foundation
       * @notice The validators deposit contract common interface
       */
      interface IValidatorsRegistry {
        /// @notice A processed deposit event.
        event DepositEvent(
          bytes pubkey,
          bytes withdrawal_credentials,
          bytes amount,
          bytes signature,
          bytes index
        );
        /// @notice Query the current deposit root hash.
        /// @return The deposit root hash.
        function get_deposit_root() external view returns (bytes32);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      /**
       * @title IVaultState
       * @author StakeWise
       * @notice Defines the interface for the VaultAdmin contract
       */
      interface IVaultAdmin {
        /**
         * @notice Event emitted on metadata ipfs hash update
         * @param caller The address of the function caller
         * @param metadataIpfsHash The new metadata IPFS hash
         */
        event MetadataUpdated(address indexed caller, string metadataIpfsHash);
        /**
         * @notice The Vault admin
         * @return The address of the Vault admin
         */
        function admin() external view returns (address);
        /**
         * @notice Function for updating the metadata IPFS hash. Can only be called by Vault admin.
         * @param metadataIpfsHash The new metadata IPFS hash
         */
        function setMetadata(string calldata metadataIpfsHash) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultAdmin} from './IVaultAdmin.sol';
      /**
       * @title IVaultBlocklist
       * @author StakeWise
       * @notice Defines the interface for the VaultBlocklist contract
       */
      interface IVaultBlocklist is IVaultAdmin {
        /**
         * @notice Event emitted on blocklist update
         * @param caller The address of the function caller
         * @param account The address of the account updated
         * @param isBlocked Whether account is blocked or not
         */
        event BlocklistUpdated(address indexed caller, address indexed account, bool isBlocked);
        /**
         * @notice Event emitted when blocklist manager address is updated
         * @param caller The address of the function caller
         * @param blocklistManager The address of the new blocklist manager
         */
        event BlocklistManagerUpdated(address indexed caller, address indexed blocklistManager);
        /**
         * @notice Blocklist manager address
         * @return The address of the blocklist manager
         */
        function blocklistManager() external view returns (address);
        /**
         * @notice Checks whether account is blocked or not
         * @param account The account to check
         * @return `true` for the blocked account, `false` otherwise
         */
        function blockedAccounts(address account) external view returns (bool);
        /**
         * @notice Add or remove account from the blocklist. Can only be called by the blocklist manager.
         * @param account The account to add or remove to the blocklist
         * @param isBlocked Whether account should be blocked or not
         */
        function updateBlocklist(address account, bool isBlocked) external;
        /**
         * @notice Used to update the blocklist manager. Can only be called by the Vault admin.
         * @param _blocklistManager The address of the new blocklist manager
         */
        function setBlocklistManager(address _blocklistManager) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultState} from './IVaultState.sol';
      /**
       * @title IVaultEnterExit
       * @author StakeWise
       * @notice Defines the interface for the VaultEnterExit contract
       */
      interface IVaultEnterExit is IVaultState {
        /**
         * @notice Event emitted on deposit
         * @param caller The address that called the deposit function
         * @param receiver The address that received the shares
         * @param assets The number of assets deposited by the caller
         * @param shares The number of shares received
         * @param referrer The address of the referrer
         */
        event Deposited(
          address indexed caller,
          address indexed receiver,
          uint256 assets,
          uint256 shares,
          address referrer
        );
        /**
         * @notice Event emitted on redeem
         * @param owner The address that owns the shares
         * @param receiver The address that received withdrawn assets
         * @param assets The total number of withdrawn assets
         * @param shares The total number of withdrawn shares
         */
        event Redeemed(address indexed owner, address indexed receiver, uint256 assets, uint256 shares);
        /**
         * @notice Event emitted on shares added to the exit queue
         * @param owner The address that owns the shares
         * @param receiver The address that will receive withdrawn assets
         * @param positionTicket The exit queue ticket that was assigned to the position
         * @param shares The number of shares that queued for the exit
         */
        event ExitQueueEntered(
          address indexed owner,
          address indexed receiver,
          uint256 positionTicket,
          uint256 shares
        );
        /**
         * @notice Event emitted on claim of the exited assets
         * @param receiver The address that has received withdrawn assets
         * @param prevPositionTicket The exit queue ticket received after the `enterExitQueue` call
         * @param newPositionTicket The new exit queue ticket in case not all the shares were withdrawn. Otherwise 0.
         * @param withdrawnAssets The total number of assets withdrawn
         */
        event ExitedAssetsClaimed(
          address indexed receiver,
          uint256 prevPositionTicket,
          uint256 newPositionTicket,
          uint256 withdrawnAssets
        );
        /**
         * @notice Locks shares to the exit queue. The shares continue earning rewards until they will be burned by the Vault.
         * @param shares The number of shares to lock
         * @param receiver The address that will receive assets upon withdrawal
         * @return positionTicket The position ticket of the exit queue
         */
        function enterExitQueue(
          uint256 shares,
          address receiver
        ) external returns (uint256 positionTicket);
        /**
         * @notice Get the exit queue index to claim exited assets from
         * @param positionTicket The exit queue position ticket to get the index for
         * @return The exit queue index that should be used to claim exited assets.
         *         Returns -1 in case such index does not exist.
         */
        function getExitQueueIndex(uint256 positionTicket) external view returns (int256);
        /**
         * @notice Calculates the number of shares and assets that can be claimed from the exit queue.
         * @param receiver The address that will receive assets upon withdrawal
         * @param positionTicket The exit queue ticket received after the `enterExitQueue` call
         * @param timestamp The timestamp when the shares entered the exit queue
         * @param exitQueueIndex The exit queue index at which the shares were burned. It can be looked up by calling `getExitQueueIndex`.
         * @return leftShares The number of shares that are still in the queue
         * @return claimedShares The number of claimed shares
         * @return claimedAssets The number of claimed assets
         */
        function calculateExitedAssets(
          address receiver,
          uint256 positionTicket,
          uint256 timestamp,
          uint256 exitQueueIndex
        ) external view returns (uint256 leftShares, uint256 claimedShares, uint256 claimedAssets);
        /**
         * @notice Claims assets that were withdrawn by the Vault. It can be called only after the `enterExitQueue` call by the `receiver`.
         * @param positionTicket The exit queue ticket received after the `enterExitQueue` call
         * @param timestamp The timestamp when the shares entered the exit queue
         * @param exitQueueIndex The exit queue index at which the shares were burned. It can be looked up by calling `getExitQueueIndex`.
         * @return newPositionTicket The new exit queue ticket in case not all the shares were burned. Otherwise 0.
         * @return claimedShares The number of shares claimed
         * @return claimedAssets The number of assets claimed
         */
        function claimExitedAssets(
          uint256 positionTicket,
          uint256 timestamp,
          uint256 exitQueueIndex
        ) external returns (uint256 newPositionTicket, uint256 claimedShares, uint256 claimedAssets);
        /**
         * @notice Redeems assets from the Vault by utilising what has not been staked yet. Can only be called when vault is not collateralized.
         * @param shares The number of shares to burn
         * @param receiver The address that will receive assets
         * @return assets The number of assets withdrawn
         */
        function redeem(uint256 shares, address receiver) external returns (uint256 assets);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultState} from './IVaultState.sol';
      import {IVaultValidators} from './IVaultValidators.sol';
      import {IVaultEnterExit} from './IVaultEnterExit.sol';
      import {IKeeperRewards} from './IKeeperRewards.sol';
      import {IVaultMev} from './IVaultMev.sol';
      /**
       * @title IVaultEthStaking
       * @author StakeWise
       * @notice Defines the interface for the VaultEthStaking contract
       */
      interface IVaultEthStaking is IVaultState, IVaultValidators, IVaultEnterExit, IVaultMev {
        /**
         * @notice Deposit ETH to the Vault
         * @param receiver The address that will receive Vault's shares
         * @param referrer The address of the referrer. Set to zero address if not used.
         * @return shares The number of shares minted
         */
        function deposit(address receiver, address referrer) external payable returns (uint256 shares);
        /**
         * @notice Used by MEV escrow to transfer ETH.
         */
        function receiveFromMevEscrow() external payable;
        /**
         * @notice Updates Vault state and deposits ETH to the Vault
         * @param receiver The address that will receive Vault's shares
         * @param referrer The address of the referrer. Set to zero address if not used.
         * @param harvestParams The parameters for harvesting Keeper rewards
         * @return shares The number of shares minted
         */
        function updateStateAndDeposit(
          address receiver,
          address referrer,
          IKeeperRewards.HarvestParams calldata harvestParams
        ) external payable returns (uint256 shares);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultAdmin} from './IVaultAdmin.sol';
      /**
       * @title IVaultFee
       * @author StakeWise
       * @notice Defines the interface for the VaultFee contract
       */
      interface IVaultFee is IVaultAdmin {
        /**
         * @notice Event emitted on fee recipient update
         * @param caller The address of the function caller
         * @param feeRecipient The address of the new fee recipient
         */
        event FeeRecipientUpdated(address indexed caller, address indexed feeRecipient);
        /**
         * @notice The Vault's fee recipient
         * @return The address of the Vault's fee recipient
         */
        function feeRecipient() external view returns (address);
        /**
         * @notice The Vault's fee percent in BPS
         * @return The fee percent applied by the Vault on the rewards
         */
        function feePercent() external view returns (uint16);
        /**
         * @notice Function for updating the fee recipient address. Can only be called by the admin.
         * @param _feeRecipient The address of the new fee recipient
         */
        function setFeeRecipient(address _feeRecipient) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultState} from './IVaultState.sol';
      /**
       * @title IVaultMev
       * @author StakeWise
       * @notice Common interface for the VaultMev contracts
       */
      interface IVaultMev is IVaultState {
        /**
         * @notice The contract that accumulates MEV rewards
         * @return The MEV escrow contract address
         */
        function mevEscrow() external view returns (address);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IVaultState} from './IVaultState.sol';
      import {IVaultEnterExit} from './IVaultEnterExit.sol';
      /**
       * @title IVaultOsToken
       * @author StakeWise
       * @notice Defines the interface for the VaultOsToken contract
       */
      interface IVaultOsToken is IVaultState, IVaultEnterExit {
        /**
         * @notice Event emitted on minting osToken
         * @param caller The address of the function caller
         * @param receiver The address of the osToken receiver
         * @param assets The amount of minted assets
         * @param shares The amount of minted shares
         * @param referrer The address of the referrer
         */
        event OsTokenMinted(
          address indexed caller,
          address receiver,
          uint256 assets,
          uint256 shares,
          address referrer
        );
        /**
         * @notice Event emitted on burning OsToken
         * @param caller The address of the function caller
         * @param assets The amount of burned assets
         * @param shares The amount of burned shares
         */
        event OsTokenBurned(address indexed caller, uint256 assets, uint256 shares);
        /**
         * @notice Event emitted on osToken position liquidation
         * @param caller The address of the function caller
         * @param user The address of the user liquidated
         * @param receiver The address of the receiver of the liquidated assets
         * @param osTokenShares The amount of osToken shares to liquidate
         * @param shares The amount of vault shares burned
         * @param receivedAssets The amount of assets received
         */
        event OsTokenLiquidated(
          address indexed caller,
          address indexed user,
          address receiver,
          uint256 osTokenShares,
          uint256 shares,
          uint256 receivedAssets
        );
        /**
         * @notice Event emitted on osToken position redemption
         * @param caller The address of the function caller
         * @param user The address of the position owner to redeem from
         * @param receiver The address of the receiver of the redeemed assets
         * @param osTokenShares The amount of osToken shares to redeem
         * @param shares The amount of vault shares burned
         * @param assets The amount of assets received
         */
        event OsTokenRedeemed(
          address indexed caller,
          address indexed user,
          address receiver,
          uint256 osTokenShares,
          uint256 shares,
          uint256 assets
        );
        /**
         * @notice Struct of osToken position
         * @param shares The total number of minted osToken shares. Will increase based on the treasury fee.
         * @param cumulativeFeePerShare The cumulative fee per share
         */
        struct OsTokenPosition {
          uint128 shares;
          uint128 cumulativeFeePerShare;
        }
        /**
         * @notice Get total amount of minted osToken shares
         * @param user The address of the user
         * @return shares The number of minted osToken shares
         */
        function osTokenPositions(address user) external view returns (uint128 shares);
        /**
         * @notice Mints OsToken shares
         * @param receiver The address that will receive the minted OsToken shares
         * @param osTokenShares The number of OsToken shares to mint to the receiver
         * @param referrer The address of the referrer
         * @return assets The number of assets minted to the receiver
         */
        function mintOsToken(
          address receiver,
          uint256 osTokenShares,
          address referrer
        ) external returns (uint256 assets);
        /**
         * @notice Burns osToken shares
         * @param osTokenShares The number of shares to burn
         * @return assets The number of assets burned
         */
        function burnOsToken(uint128 osTokenShares) external returns (uint256 assets);
        /**
         * @notice Liquidates a user position and returns the number of received assets.
         *         Can only be called when health factor is below 1.
         * @param osTokenShares The number of shares to cover
         * @param owner The address of the position owner to liquidate
         * @param receiver The address of the receiver of the liquidated assets
         */
        function liquidateOsToken(uint256 osTokenShares, address owner, address receiver) external;
        /**
         * @notice Redeems osToken shares for assets. Can only be called when health factor is above redeemFromHealthFactor.
         * @param osTokenShares The number of osToken shares to redeem
         * @param owner The address of the position owner to redeem from
         * @param receiver The address of the receiver of the redeemed assets
         */
        function redeemOsToken(uint256 osTokenShares, address owner, address receiver) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      /**
       * @title IVaultsRegistry
       * @author StakeWise
       * @notice Defines the interface for the VaultsRegistry
       */
      interface IVaultsRegistry {
        /**
         * @notice Event emitted on a Vault addition
         * @param caller The address that has added the Vault
         * @param vault The address of the added Vault
         */
        event VaultAdded(address indexed caller, address indexed vault);
        /**
         * @notice Event emitted on adding Vault implementation contract
         * @param impl The address of the new implementation contract
         */
        event VaultImplAdded(address indexed impl);
        /**
         * @notice Event emitted on removing Vault implementation contract
         * @param impl The address of the removed implementation contract
         */
        event VaultImplRemoved(address indexed impl);
        /**
         * @notice Event emitted on whitelisting the factory
         * @param factory The address of the whitelisted factory
         */
        event FactoryAdded(address indexed factory);
        /**
         * @notice Event emitted on removing the factory from the whitelist
         * @param factory The address of the factory removed from the whitelist
         */
        event FactoryRemoved(address indexed factory);
        /**
         * @notice Registered Vaults
         * @param vault The address of the vault to check whether it is registered
         * @return `true` for the registered Vault, `false` otherwise
         */
        function vaults(address vault) external view returns (bool);
        /**
         * @notice Registered Vault implementations
         * @param impl The address of the vault implementation
         * @return `true` for the registered implementation, `false` otherwise
         */
        function vaultImpls(address impl) external view returns (bool);
        /**
         * @notice Registered Factories
         * @param factory The address of the factory to check whether it is whitelisted
         * @return `true` for the whitelisted Factory, `false` otherwise
         */
        function factories(address factory) external view returns (bool);
        /**
         * @notice Function for adding Vault to the registry. Can only be called by the whitelisted Factory.
         * @param vault The address of the Vault to add
         */
        function addVault(address vault) external;
        /**
         * @notice Function for adding Vault implementation contract
         * @param newImpl The address of the new implementation contract
         */
        function addVaultImpl(address newImpl) external;
        /**
         * @notice Function for removing Vault implementation contract
         * @param impl The address of the removed implementation contract
         */
        function removeVaultImpl(address impl) external;
        /**
         * @notice Function for adding the factory to the whitelist
         * @param factory The address of the factory to add to the whitelist
         */
        function addFactory(address factory) external;
        /**
         * @notice Function for removing the factory from the whitelist
         * @param factory The address of the factory to remove from the whitelist
         */
        function removeFactory(address factory) external;
        /**
         * @notice Function for initializing the registry. Can only be called once during the deployment.
         * @param _owner The address of the owner of the contract
         */
        function initialize(address _owner) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IKeeperRewards} from './IKeeperRewards.sol';
      import {IVaultFee} from './IVaultFee.sol';
      /**
       * @title IVaultState
       * @author StakeWise
       * @notice Defines the interface for the VaultState contract
       */
      interface IVaultState is IVaultFee {
        /**
         * @notice Event emitted on checkpoint creation
         * @param shares The number of burned shares
         * @param assets The amount of exited assets
         */
        event CheckpointCreated(uint256 shares, uint256 assets);
        /**
         * @notice Event emitted on minting fee recipient shares
         * @param receiver The address of the fee recipient
         * @param shares The number of minted shares
         * @param assets The amount of minted assets
         */
        event FeeSharesMinted(address receiver, uint256 shares, uint256 assets);
        /**
         * @notice Total assets in the Vault
         * @return The total amount of the underlying asset that is "managed" by Vault
         */
        function totalAssets() external view returns (uint256);
        /**
         * @notice Function for retrieving total shares
         * @return The amount of shares in existence
         */
        function totalShares() external view returns (uint256);
        /**
         * @notice The Vault's capacity
         * @return The amount after which the Vault stops accepting deposits
         */
        function capacity() external view returns (uint256);
        /**
         * @notice Total assets available in the Vault. They can be staked or withdrawn.
         * @return The total amount of withdrawable assets
         */
        function withdrawableAssets() external view returns (uint256);
        /**
         * @notice Queued Shares
         * @return The total number of shares queued for exit
         */
        function queuedShares() external view returns (uint128);
        /**
         * @notice Returns the number of shares held by an account
         * @param account The account for which to look up the number of shares it has, i.e. its balance
         * @return The number of shares held by the account
         */
        function getShares(address account) external view returns (uint256);
        /**
         * @notice Converts shares to assets
         * @param assets The amount of assets to convert to shares
         * @return shares The amount of shares that the Vault would exchange for the amount of assets provided
         */
        function convertToShares(uint256 assets) external view returns (uint256 shares);
        /**
         * @notice Converts assets to shares
         * @param shares The amount of shares to convert to assets
         * @return assets The amount of assets that the Vault would exchange for the amount of shares provided
         */
        function convertToAssets(uint256 shares) external view returns (uint256 assets);
        /**
         * @notice Check whether state update is required
         * @return `true` if state update is required, `false` otherwise
         */
        function isStateUpdateRequired() external view returns (bool);
        /**
         * @notice Updates the total amount of assets in the Vault and its exit queue
         * @param harvestParams The parameters for harvesting Keeper rewards
         */
        function updateState(IKeeperRewards.HarvestParams calldata harvestParams) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IKeeperValidators} from './IKeeperValidators.sol';
      import {IVaultAdmin} from './IVaultAdmin.sol';
      import {IVaultState} from './IVaultState.sol';
      /**
       * @title IVaultValidators
       * @author StakeWise
       * @notice Defines the interface for VaultValidators contract
       */
      interface IVaultValidators is IVaultAdmin, IVaultState {
        /**
         * @notice Event emitted on validator registration
         * @param publicKey The public key of the validator that was registered
         */
        event ValidatorRegistered(bytes publicKey);
        /**
         * @notice Event emitted on keys manager address update
         * @param caller The address of the function caller
         * @param keysManager The address of the new keys manager
         */
        event KeysManagerUpdated(address indexed caller, address indexed keysManager);
        /**
         * @notice Event emitted on validators merkle tree root update
         * @param caller The address of the function caller
         * @param validatorsRoot The new validators merkle tree root
         */
        event ValidatorsRootUpdated(address indexed caller, bytes32 indexed validatorsRoot);
        /**
         * @notice The Vault keys manager address
         * @return The address that can update validators merkle tree root
         */
        function keysManager() external view returns (address);
        /**
         * @notice The Vault validators root
         * @return The merkle tree root to use for verifying validators deposit data
         */
        function validatorsRoot() external view returns (bytes32);
        /**
         * @notice The Vault validator index
         * @return The index of the next validator to be registered in the current deposit data file
         */
        function validatorIndex() external view returns (uint256);
        /**
         * @notice Function for registering single validator
         * @param keeperParams The parameters for getting approval from Keeper oracles
         * @param proof The proof used to verify that the validator is part of the validators merkle tree
         */
        function registerValidator(
          IKeeperValidators.ApprovalParams calldata keeperParams,
          bytes32[] calldata proof
        ) external;
        /**
         * @notice Function for registering multiple validators
         * @param keeperParams The parameters for getting approval from Keeper oracles
         * @param indexes The indexes of the leaves for the merkle tree multi proof verification
         * @param proofFlags The multi proof flags for the merkle tree verification
         * @param proof The proof used for the merkle tree verification
         */
        function registerValidators(
          IKeeperValidators.ApprovalParams calldata keeperParams,
          uint256[] calldata indexes,
          bool[] calldata proofFlags,
          bytes32[] calldata proof
        ) external;
        /**
         * @notice Function for updating the keys manager. Can only be called by the admin.
         * @param _keysManager The new keys manager address
         */
        function setKeysManager(address _keysManager) external;
        /**
         * @notice Function for updating the validators merkle tree root. Can only be called by the keys manager.
         * @param _validatorsRoot The new validators merkle tree root
         */
        function setValidatorsRoot(bytes32 _validatorsRoot) external;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IERC1822Proxiable} from '@openzeppelin/contracts/interfaces/draft-IERC1822.sol';
      import {IVaultAdmin} from './IVaultAdmin.sol';
      /**
       * @title IVaultVersion
       * @author StakeWise
       * @notice Defines the interface for VaultVersion contract
       */
      interface IVaultVersion is IERC1822Proxiable, IVaultAdmin {
        /**
         * @notice Vault Unique Identifier
         * @return The unique identifier of the Vault
         */
        function vaultId() external pure returns (bytes32);
        /**
         * @notice Version
         * @return The version of the Vault implementation contract
         */
        function version() external pure returns (uint8);
        /**
         * @notice Implementation
         * @return The address of the Vault implementation contract
         */
        function implementation() external view returns (address);
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      /**
       * @title Errors
       * @author StakeWise
       * @notice Contains all the custom errors
       */
      library Errors {
        error AccessDenied();
        error InvalidShares();
        error InvalidAssets();
        error ZeroAddress();
        error InsufficientAssets();
        error CapacityExceeded();
        error InvalidCapacity();
        error InvalidSecurityDeposit();
        error InvalidFeeRecipient();
        error InvalidFeePercent();
        error NotHarvested();
        error NotCollateralized();
        error Collateralized();
        error InvalidProof();
        error LowLtv();
        error RedemptionExceeded();
        error InvalidPosition();
        error InvalidLtv();
        error InvalidHealthFactor();
        error InvalidReceivedAssets();
        error InvalidTokenMeta();
        error UpgradeFailed();
        error InvalidValidator();
        error InvalidValidators();
        error WhitelistAlreadyUpdated();
        error DeadlineExpired();
        error PermitInvalidSigner();
        error InvalidValidatorsRegistryRoot();
        error InvalidVault();
        error AlreadyAdded();
        error AlreadyRemoved();
        error InvalidOracles();
        error NotEnoughSignatures();
        error InvalidOracle();
        error TooEarlyUpdate();
        error InvalidAvgRewardPerSecond();
        error InvalidRewardsRoot();
        error HarvestFailed();
        error InvalidRedeemFromLtvPercent();
        error InvalidLiqThresholdPercent();
        error InvalidLiqBonusPercent();
        error InvalidLtvPercent();
        error InvalidCheckpointIndex();
        error InvalidCheckpointValue();
        error MaxOraclesExceeded();
        error ClaimTooEarly();
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
      import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
      import {Errors} from './Errors.sol';
      /**
       * @title ExitQueue
       * @author StakeWise
       * @notice ExitQueue represent checkpoints of burned shares and exited assets
       */
      library ExitQueue {
        /**
         * @notice A struct containing checkpoint data
         * @param totalTickets The cumulative number of tickets (shares) exited
         * @param exitedAssets The number of assets that exited in this checkpoint
         */
        struct Checkpoint {
          uint160 totalTickets;
          uint96 exitedAssets;
        }
        /**
         * @notice A struct containing the history of checkpoints data
         * @param checkpoints An array of checkpoints
         */
        struct History {
          Checkpoint[] checkpoints;
        }
        /**
         * @notice Get the latest checkpoint total tickets
         * @param self An array containing checkpoints
         * @return The current total tickets or zero if there are no checkpoints
         */
        function getLatestTotalTickets(History storage self) internal view returns (uint256) {
          uint256 pos = self.checkpoints.length;
          unchecked {
            // cannot underflow as subtraction happens in case pos > 0
            return pos == 0 ? 0 : _unsafeAccess(self.checkpoints, pos - 1).totalTickets;
          }
        }
        /**
         * @notice Get checkpoint index for the burned shares
         * @param self An array containing checkpoints
         * @param positionTicket The position ticket to search the closest checkpoint for
         * @return The checkpoint index or the length of checkpoints array in case there is no such
         */
        function getCheckpointIndex(
          History storage self,
          uint256 positionTicket
        ) internal view returns (uint256) {
          uint256 high = self.checkpoints.length;
          uint256 low;
          while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self.checkpoints, mid).totalTickets > positionTicket) {
              high = mid;
            } else {
              unchecked {
                // cannot underflow as mid < high
                low = mid + 1;
              }
            }
          }
          return high;
        }
        /**
         * @notice Calculates burned shares and exited assets
         * @param self An array containing checkpoints
         * @param checkpointIdx The index of the checkpoint to start calculating from
         * @param positionTicket The position ticket to start calculating exited assets from
         * @param positionShares The number of shares to calculate assets for
         * @return burnedShares The number of shares burned
         * @return exitedAssets The number of assets exited
         */
        function calculateExitedAssets(
          History storage self,
          uint256 checkpointIdx,
          uint256 positionTicket,
          uint256 positionShares
        ) internal view returns (uint256 burnedShares, uint256 exitedAssets) {
          uint256 length = self.checkpoints.length;
          // there are no exited assets for such checkpoint index or no shares to burn
          if (checkpointIdx >= length || positionShares == 0) return (0, 0);
          // previous total tickets for calculating how much shares were burned for the period
          uint256 prevTotalTickets;
          unchecked {
            // cannot underflow as subtraction happens in case checkpointIdx > 0
            prevTotalTickets = checkpointIdx == 0
              ? 0
              : _unsafeAccess(self.checkpoints, checkpointIdx - 1).totalTickets;
          }
          // current total tickets for calculating assets per burned share
          // can be used with _unsafeAccess as checkpointIdx < length
          Checkpoint memory checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
          uint256 currTotalTickets = checkpoint.totalTickets;
          uint256 checkpointAssets = checkpoint.exitedAssets;
          // check whether position ticket is in [prevTotalTickets, currTotalTickets) range
          if (positionTicket < prevTotalTickets || currTotalTickets <= positionTicket) {
            revert Errors.InvalidCheckpointIndex();
          }
          // calculate amount of available shares that will be updated while iterating over checkpoints
          uint256 availableShares;
          unchecked {
            // cannot underflow as positionTicket < currTotalTickets
            availableShares = currTotalTickets - positionTicket;
          }
          // accumulate assets until the number of required shares is collected
          uint256 checkpointShares;
          uint256 sharesDelta;
          while (true) {
            unchecked {
              // cannot underflow as prevTotalTickets <= positionTicket
              checkpointShares = currTotalTickets - prevTotalTickets;
              // cannot underflow as positionShares > burnedShares while in the loop
              sharesDelta = Math.min(availableShares, positionShares - burnedShares);
              // cannot overflow as it is capped with underlying asset total supply
              burnedShares += sharesDelta;
              exitedAssets += Math.mulDiv(sharesDelta, checkpointAssets, checkpointShares);
              // cannot overflow as checkpoints are created max once per day
              checkpointIdx++;
            }
            // stop when required shares collected or reached end of checkpoints list
            if (positionShares <= burnedShares || checkpointIdx >= length) {
              return (burnedShares, exitedAssets);
            }
            // take next checkpoint
            prevTotalTickets = currTotalTickets;
            // can use _unsafeAccess as checkpointIdx < length is checked above
            checkpoint = _unsafeAccess(self.checkpoints, checkpointIdx);
            currTotalTickets = checkpoint.totalTickets;
            checkpointAssets = checkpoint.exitedAssets;
            unchecked {
              // cannot underflow as every next checkpoint total tickets is larger than previous
              availableShares = currTotalTickets - prevTotalTickets;
            }
          }
        }
        /**
         * @notice Pushes a new checkpoint onto a History
         * @param self An array containing checkpoints
         * @param shares The number of shares to add to the latest checkpoint
         * @param assets The number of assets that were exited for this checkpoint
         */
        function push(History storage self, uint256 shares, uint256 assets) internal {
          if (shares == 0 || assets == 0) revert Errors.InvalidCheckpointValue();
          Checkpoint memory checkpoint = Checkpoint({
            totalTickets: SafeCast.toUint160(getLatestTotalTickets(self) + shares),
            exitedAssets: SafeCast.toUint96(assets)
          });
          self.checkpoints.push(checkpoint);
        }
        function _unsafeAccess(
          Checkpoint[] storage self,
          uint256 pos
        ) private pure returns (Checkpoint storage result) {
          assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
          }
        }
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {IEthFoxVault} from '../../../interfaces/IEthFoxVault.sol';
      import {Multicall} from '../../../base/Multicall.sol';
      import {VaultValidators} from '../../modules/VaultValidators.sol';
      import {VaultAdmin} from '../../modules/VaultAdmin.sol';
      import {VaultFee} from '../../modules/VaultFee.sol';
      import {VaultVersion, IVaultVersion} from '../../modules/VaultVersion.sol';
      import {VaultImmutables} from '../../modules/VaultImmutables.sol';
      import {VaultState} from '../../modules/VaultState.sol';
      import {VaultEnterExit} from '../../modules/VaultEnterExit.sol';
      import {VaultEthStaking, IVaultEthStaking} from '../../modules/VaultEthStaking.sol';
      import {VaultMev} from '../../modules/VaultMev.sol';
      import {VaultBlocklist} from '../../modules/VaultBlocklist.sol';
      /**
       * @title EthFoxVault
       * @author StakeWise
       * @notice Custom Ethereum non-ERC20 vault with blocklist, own MEV and without osToken minting.
       */
      contract EthFoxVault is
        VaultImmutables,
        Initializable,
        VaultAdmin,
        VaultVersion,
        VaultFee,
        VaultState,
        VaultValidators,
        VaultEnterExit,
        VaultMev,
        VaultEthStaking,
        VaultBlocklist,
        Multicall,
        IEthFoxVault
      {
        /**
         * @dev Constructor
         * @dev Since the immutable variable value is stored in the bytecode,
         *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
         * @param _keeper The address of the Keeper contract
         * @param _vaultsRegistry The address of the VaultsRegistry contract
         * @param _validatorsRegistry The contract address used for registering validators in beacon chain
         * @param sharedMevEscrow The address of the shared MEV escrow
         * @param exitedAssetsClaimDelay The delay after which the assets can be claimed after exiting from staking
         */
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor(
          address _keeper,
          address _vaultsRegistry,
          address _validatorsRegistry,
          address sharedMevEscrow,
          uint256 exitedAssetsClaimDelay
        )
          VaultImmutables(_keeper, _vaultsRegistry, _validatorsRegistry)
          VaultEnterExit(exitedAssetsClaimDelay)
          VaultMev(sharedMevEscrow)
        {
          _disableInitializers();
        }
        /// @inheritdoc IEthFoxVault
        function initialize(bytes calldata params) external payable virtual override initializer {
          EthFoxVaultInitParams memory initParams = abi.decode(params, (EthFoxVaultInitParams));
          __EthFoxVault_init(initParams);
          emit EthFoxVaultCreated(
            initParams.admin,
            initParams.ownMevEscrow,
            initParams.capacity,
            initParams.feePercent,
            initParams.metadataIpfsHash
          );
        }
        /// @inheritdoc IVaultEthStaking
        function deposit(
          address receiver,
          address referrer
        ) public payable virtual override(IVaultEthStaking, VaultEthStaking) returns (uint256 shares) {
          _checkBlocklist(msg.sender);
          _checkBlocklist(receiver);
          return super.deposit(receiver, referrer);
        }
        /// @inheritdoc IEthFoxVault
        function ejectUser(address user) external override {
          // add user to blocklist
          updateBlocklist(user, true);
          // fetch shares of the user
          uint256 userShares = _balances[user];
          if (userShares == 0) return;
          if (_isCollateralized()) {
            // send user shares to exit queue
            _enterExitQueue(user, userShares, user);
          } else {
            // redeem user shares
            _redeem(user, userShares, user);
          }
          emit UserEjected(user, userShares);
        }
        /**
         * @dev Function for depositing using fallback function
         */
        receive() external payable virtual override {
          _checkBlocklist(msg.sender);
          _deposit(msg.sender, msg.value, address(0));
        }
        /// @inheritdoc VaultVersion
        function vaultId() public pure virtual override(IVaultVersion, VaultVersion) returns (bytes32) {
          return keccak256('EthFoxVault');
        }
        /// @inheritdoc IVaultVersion
        function version() public pure virtual override(IVaultVersion, VaultVersion) returns (uint8) {
          return 1;
        }
        /**
         * @dev Initializes the EthFoxVault contract
         * @param params The decoded parameters for initializing the EthFoxVault contract
         */
        function __EthFoxVault_init(EthFoxVaultInitParams memory params) internal onlyInitializing {
          __VaultAdmin_init(params.admin, params.metadataIpfsHash);
          // fee recipient is initially set to admin address
          __VaultFee_init(params.admin, params.feePercent);
          __VaultState_init(params.capacity);
          __VaultValidators_init();
          __VaultMev_init(params.ownMevEscrow);
          // blocklist manager is initially set to admin address
          __VaultBlocklist_init(params.admin);
          __VaultEthStaking_init();
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {IVaultAdmin} from '../../interfaces/IVaultAdmin.sol';
      import {Errors} from '../../libraries/Errors.sol';
      /**
       * @title VaultAdmin
       * @author StakeWise
       * @notice Defines the admin functionality for the Vault
       */
      abstract contract VaultAdmin is Initializable, IVaultAdmin {
        /// @inheritdoc IVaultAdmin
        address public override admin;
        /// @inheritdoc IVaultAdmin
        function setMetadata(string calldata metadataIpfsHash) external override {
          _checkAdmin();
          emit MetadataUpdated(msg.sender, metadataIpfsHash);
        }
        /**
         * @dev Initializes the VaultAdmin contract
         * @param _admin The address of the Vault admin
         */
        function __VaultAdmin_init(
          address _admin,
          string memory metadataIpfsHash
        ) internal onlyInitializing {
          admin = _admin;
          emit MetadataUpdated(msg.sender, metadataIpfsHash);
        }
        /**
         * @dev Internal method for checking whether the caller is admin
         */
        function _checkAdmin() internal view {
          if (msg.sender != admin) revert Errors.AccessDenied();
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {IVaultBlocklist} from '../../interfaces/IVaultBlocklist.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultAdmin} from './VaultAdmin.sol';
      /**
       * @title VaultBlocklist
       * @author StakeWise
       * @notice Defines the functionality for blocking addresses for the Vault
       */
      abstract contract VaultBlocklist is Initializable, VaultAdmin, IVaultBlocklist {
        /// @inheritdoc IVaultBlocklist
        address public override blocklistManager;
        /// @inheritdoc IVaultBlocklist
        mapping(address => bool) public override blockedAccounts;
        /// @inheritdoc IVaultBlocklist
        function updateBlocklist(address account, bool isBlocked) public virtual override {
          if (msg.sender != blocklistManager) revert Errors.AccessDenied();
          if (blockedAccounts[account] == isBlocked) return;
          blockedAccounts[account] = isBlocked;
          emit BlocklistUpdated(msg.sender, account, isBlocked);
        }
        /// @inheritdoc IVaultBlocklist
        function setBlocklistManager(address _blocklistManager) external override {
          _checkAdmin();
          _setBlocklistManager(_blocklistManager);
        }
        /**
         * @notice Internal function for checking blocklist
         * @param account The address of the account to check
         */
        function _checkBlocklist(address account) internal view {
          if (blockedAccounts[account]) revert Errors.AccessDenied();
        }
        /**
         * @dev Internal function for updating the blocklist manager externally or from the initializer
         * @param _blocklistManager The address of the new blocklist manager
         */
        function _setBlocklistManager(address _blocklistManager) private {
          // update blocklist manager address
          blocklistManager = _blocklistManager;
          emit BlocklistManagerUpdated(msg.sender, _blocklistManager);
        }
        /**
         * @dev Initializes the VaultBlocklist contract
         * @param _blocklistManager The address of the blocklist manager
         */
        function __VaultBlocklist_init(address _blocklistManager) internal onlyInitializing {
          _setBlocklistManager(_blocklistManager);
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
      import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {IVaultEnterExit} from '../../interfaces/IVaultEnterExit.sol';
      import {ExitQueue} from '../../libraries/ExitQueue.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultImmutables} from './VaultImmutables.sol';
      import {VaultState} from './VaultState.sol';
      /**
       * @title VaultEnterExit
       * @author StakeWise
       * @notice Defines the functionality for entering and exiting the Vault
       */
      abstract contract VaultEnterExit is VaultImmutables, Initializable, VaultState, IVaultEnterExit {
        using ExitQueue for ExitQueue.History;
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        uint256 private immutable _exitingAssetsClaimDelay;
        /**
         * @dev Constructor
         * @dev Since the immutable variable value is stored in the bytecode,
         *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
         * @param exitingAssetsClaimDelay The minimum delay after which the assets can be claimed after joining the exit queue
         */
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor(uint256 exitingAssetsClaimDelay) {
          _exitingAssetsClaimDelay = exitingAssetsClaimDelay;
        }
        /// @inheritdoc IVaultEnterExit
        function getExitQueueIndex(uint256 positionTicket) external view override returns (int256) {
          uint256 checkpointIdx = _exitQueue.getCheckpointIndex(positionTicket);
          return checkpointIdx < _exitQueue.checkpoints.length ? int256(checkpointIdx) : -1;
        }
        /// @inheritdoc IVaultEnterExit
        function redeem(
          uint256 shares,
          address receiver
        ) public virtual override returns (uint256 assets) {
          return _redeem(msg.sender, shares, receiver);
        }
        /// @inheritdoc IVaultEnterExit
        function enterExitQueue(
          uint256 shares,
          address receiver
        ) public virtual override returns (uint256 positionTicket) {
          return _enterExitQueue(msg.sender, shares, receiver);
        }
        /// @inheritdoc IVaultEnterExit
        function calculateExitedAssets(
          address receiver,
          uint256 positionTicket,
          uint256 timestamp,
          uint256 exitQueueIndex
        )
          public
          view
          override
          returns (uint256 leftShares, uint256 claimedShares, uint256 claimedAssets)
        {
          uint256 requestedShares = _exitRequests[
            keccak256(abi.encode(receiver, timestamp, positionTicket))
          ];
          // calculate exited shares and assets
          (claimedShares, claimedAssets) = _exitQueue.calculateExitedAssets(
            exitQueueIndex,
            positionTicket,
            requestedShares
          );
          leftShares = requestedShares - claimedShares;
        }
        /// @inheritdoc IVaultEnterExit
        function claimExitedAssets(
          uint256 positionTicket,
          uint256 timestamp,
          uint256 exitQueueIndex
        )
          external
          override
          returns (uint256 newPositionTicket, uint256 claimedShares, uint256 claimedAssets)
        {
          if (block.timestamp < timestamp + _exitingAssetsClaimDelay) revert Errors.ClaimTooEarly();
          bytes32 queueId = keccak256(abi.encode(msg.sender, timestamp, positionTicket));
          // calculate exited shares and assets
          uint256 leftShares;
          (leftShares, claimedShares, claimedAssets) = calculateExitedAssets(
            msg.sender,
            positionTicket,
            timestamp,
            exitQueueIndex
          );
          // nothing to claim
          if (claimedShares == 0) return (positionTicket, claimedShares, claimedAssets);
          // clean up current exit request
          delete _exitRequests[queueId];
          // skip creating new position for the shares rounding error
          if (leftShares > 1) {
            // update user's queue position
            newPositionTicket = positionTicket + claimedShares;
            _exitRequests[keccak256(abi.encode(msg.sender, timestamp, newPositionTicket))] = leftShares;
          }
          // transfer assets to the receiver
          _unclaimedAssets -= SafeCast.toUint128(claimedAssets);
          _transferVaultAssets(msg.sender, claimedAssets);
          emit ExitedAssetsClaimed(msg.sender, positionTicket, newPositionTicket, claimedAssets);
        }
        /**
         * @dev Internal function that must be used to process user deposits
         * @param to The address to mint shares to
         * @param assets The number of assets deposited
         * @param referrer The address of the referrer. Set to zero address if not used.
         * @return shares The total amount of shares minted
         */
        function _deposit(
          address to,
          uint256 assets,
          address referrer
        ) internal virtual returns (uint256 shares) {
          _checkHarvested();
          if (to == address(0)) revert Errors.ZeroAddress();
          if (assets == 0) revert Errors.InvalidAssets();
          uint256 totalAssetsAfter;
          unchecked {
            // cannot overflow as it is capped with underlying asset total supply
            totalAssetsAfter = _totalAssets + assets;
          }
          if (totalAssetsAfter > capacity()) revert Errors.CapacityExceeded();
          // calculate amount of shares to mint
          shares = _convertToShares(assets, Math.Rounding.Ceil);
          // update state
          _totalAssets = SafeCast.toUint128(totalAssetsAfter);
          _mintShares(to, shares);
          emit Deposited(msg.sender, to, assets, shares, referrer);
        }
        /**
         * @dev Internal function that must be used to process user withdrawals before first validator registration
         * @param user The address of the user
         * @param shares The number of shares to redeem
         * @param receiver The address that will receive the assets
         * @return assets The total amount of assets withdrawn
         */
        function _redeem(
          address user,
          uint256 shares,
          address receiver
        ) internal returns (uint256 assets) {
          _checkNotCollateralized();
          if (shares == 0) revert Errors.InvalidShares();
          if (receiver == address(0)) revert Errors.ZeroAddress();
          // calculate amount of assets to burn
          assets = convertToAssets(shares);
          if (assets == 0) revert Errors.InvalidAssets();
          // reverts in case there are not enough withdrawable assets
          if (assets > withdrawableAssets()) revert Errors.InsufficientAssets();
          // update total assets
          _totalAssets -= SafeCast.toUint128(assets);
          // burn owner shares
          _burnShares(user, shares);
          // transfer assets to the receiver
          _transferVaultAssets(receiver, assets);
          emit Redeemed(user, receiver, assets, shares);
        }
        /**
         * @dev Internal function that must be used to process user withdrawals after first validator registration
         * @param user The address of the user
         * @param shares The number of shares to send to exit queue
         * @param receiver The address that will receive the assets
         * @return positionTicket The position ticket in the exit queue
         */
        function _enterExitQueue(
          address user,
          uint256 shares,
          address receiver
        ) internal virtual returns (uint256 positionTicket) {
          _checkCollateralized();
          if (shares == 0) revert Errors.InvalidShares();
          if (receiver == address(0)) revert Errors.ZeroAddress();
          // SLOAD to memory
          uint256 _queuedShares = queuedShares;
          // calculate position ticket
          positionTicket = _exitQueue.getLatestTotalTickets() + _queuedShares;
          // add to the exit requests
          _exitRequests[keccak256(abi.encode(receiver, block.timestamp, positionTicket))] = shares;
          // reverts if owner does not have enough shares
          _balances[user] -= shares;
          unchecked {
            // cannot overflow as it is capped with _totalShares
            queuedShares = SafeCast.toUint128(_queuedShares + shares);
          }
          emit ExitQueueEntered(user, receiver, positionTicket, shares);
        }
        /**
         * @dev Internal function for transferring assets from the Vault to the receiver
         * @dev IMPORTANT: because control is transferred to the receiver, care must be
         *    taken to not create reentrancy vulnerabilities. The Vault must follow the checks-effects-interactions pattern:
         *    https://docs.soliditylang.org/en/v0.8.22/security-considerations.html#use-the-checks-effects-interactions-pattern
         * @param receiver The address that will receive the assets
         * @param assets The number of assets to transfer
         */
        function _transferVaultAssets(address receiver, uint256 assets) internal virtual;
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {ReentrancyGuardUpgradeable} from '@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol';
      import {Address} from '@openzeppelin/contracts/utils/Address.sol';
      import {IEthValidatorsRegistry} from '../../interfaces/IEthValidatorsRegistry.sol';
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {IVaultEthStaking} from '../../interfaces/IVaultEthStaking.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultValidators} from './VaultValidators.sol';
      import {VaultState} from './VaultState.sol';
      import {VaultEnterExit} from './VaultEnterExit.sol';
      import {VaultMev} from './VaultMev.sol';
      /**
       * @title VaultEthStaking
       * @author StakeWise
       * @notice Defines the Ethereum staking functionality for the Vault
       */
      abstract contract VaultEthStaking is
        Initializable,
        ReentrancyGuardUpgradeable,
        VaultState,
        VaultValidators,
        VaultEnterExit,
        VaultMev,
        IVaultEthStaking
      {
        uint256 private constant _securityDeposit = 1e9;
        /// @inheritdoc IVaultEthStaking
        function deposit(
          address receiver,
          address referrer
        ) public payable virtual override returns (uint256 shares) {
          return _deposit(receiver, msg.value, referrer);
        }
        /// @inheritdoc IVaultEthStaking
        function updateStateAndDeposit(
          address receiver,
          address referrer,
          IKeeperRewards.HarvestParams calldata harvestParams
        ) public payable virtual override returns (uint256 shares) {
          updateState(harvestParams);
          return deposit(receiver, referrer);
        }
        /**
         * @dev Function for depositing using fallback function
         */
        receive() external payable virtual {
          _deposit(msg.sender, msg.value, address(0));
        }
        /// @inheritdoc IVaultEthStaking
        function receiveFromMevEscrow() external payable override {
          if (msg.sender != mevEscrow()) revert Errors.AccessDenied();
        }
        /// @inheritdoc VaultValidators
        function _registerSingleValidator(bytes calldata validator) internal virtual override {
          bytes calldata publicKey = validator[:48];
          IEthValidatorsRegistry(_validatorsRegistry).deposit{value: _validatorDeposit()}(
            publicKey,
            _withdrawalCredentials(),
            validator[48:144],
            bytes32(validator[144:_validatorLength])
          );
          emit ValidatorRegistered(publicKey);
        }
        /// @inheritdoc VaultValidators
        function _registerMultipleValidators(
          bytes calldata validators,
          uint256[] calldata indexes
        ) internal virtual override returns (bytes32[] memory leaves) {
          // SLOAD to memory
          uint256 currentValIndex = validatorIndex;
          uint256 startIndex;
          uint256 endIndex;
          bytes calldata validator;
          bytes calldata publicKey;
          uint256 validatorsCount = indexes.length;
          leaves = new bytes32[](validatorsCount);
          uint256 validatorDeposit = _validatorDeposit();
          bytes memory withdrawalCreds = _withdrawalCredentials();
          for (uint256 i = 0; i < validatorsCount; i++) {
            unchecked {
              // cannot realistically overflow
              endIndex += _validatorLength;
            }
            validator = validators[startIndex:endIndex];
            leaves[indexes[i]] = keccak256(
              bytes.concat(keccak256(abi.encode(validator, currentValIndex)))
            );
            publicKey = validator[:48];
            // slither-disable-next-line arbitrary-send-eth
            IEthValidatorsRegistry(_validatorsRegistry).deposit{value: validatorDeposit}(
              publicKey,
              withdrawalCreds,
              validator[48:144],
              bytes32(validator[144:_validatorLength])
            );
            startIndex = endIndex;
            unchecked {
              // cannot realistically overflow
              ++currentValIndex;
            }
            emit ValidatorRegistered(publicKey);
          }
        }
        /// @inheritdoc VaultState
        function _vaultAssets() internal view virtual override returns (uint256) {
          return address(this).balance;
        }
        /// @inheritdoc VaultEnterExit
        function _transferVaultAssets(
          address receiver,
          uint256 assets
        ) internal virtual override nonReentrant {
          return Address.sendValue(payable(receiver), assets);
        }
        /// @inheritdoc VaultValidators
        function _validatorDeposit() internal pure override returns (uint256) {
          return 32 ether;
        }
        /**
         * @dev Initializes the VaultEthStaking contract
         */
        function __VaultEthStaking_init() internal onlyInitializing {
          __ReentrancyGuard_init();
          // see https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706
          if (msg.value < _securityDeposit) revert Errors.InvalidSecurityDeposit();
          _deposit(address(this), msg.value, address(0));
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {IVaultFee} from '../../interfaces/IVaultFee.sol';
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultAdmin} from './VaultAdmin.sol';
      import {VaultImmutables} from './VaultImmutables.sol';
      /**
       * @title VaultFee
       * @author StakeWise
       * @notice Defines the fee functionality for the Vault
       */
      abstract contract VaultFee is VaultImmutables, Initializable, VaultAdmin, IVaultFee {
        uint256 internal constant _maxFeePercent = 10_000; // @dev 100.00 %
        /// @inheritdoc IVaultFee
        address public override feeRecipient;
        /// @inheritdoc IVaultFee
        uint16 public override feePercent;
        /// @inheritdoc IVaultFee
        function setFeeRecipient(address _feeRecipient) external override {
          _checkAdmin();
          _setFeeRecipient(_feeRecipient);
        }
        /**
         * @dev Internal function for updating the fee recipient externally or from the initializer
         * @param _feeRecipient The address of the new fee recipient
         */
        function _setFeeRecipient(address _feeRecipient) private {
          _checkHarvested();
          if (_feeRecipient == address(0)) revert Errors.InvalidFeeRecipient();
          // update fee recipient address
          feeRecipient = _feeRecipient;
          emit FeeRecipientUpdated(msg.sender, _feeRecipient);
        }
        /**
         * @dev Initializes the VaultFee contract
         * @param _feeRecipient The address of the fee recipient
         * @param _feePercent The fee percent that is charged by the Vault
         */
        function __VaultFee_init(address _feeRecipient, uint16 _feePercent) internal onlyInitializing {
          if (_feePercent > _maxFeePercent) revert Errors.InvalidFeePercent();
          _setFeeRecipient(_feeRecipient);
          feePercent = _feePercent;
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {Errors} from '../../libraries/Errors.sol';
      /**
       * @title VaultImmutables
       * @author StakeWise
       * @notice Defines the Vault common immutable variables
       */
      abstract contract VaultImmutables {
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        address internal immutable _keeper;
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        address internal immutable _vaultsRegistry;
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        address internal immutable _validatorsRegistry;
        /**
         * @dev Constructor
         * @dev Since the immutable variable value is stored in the bytecode,
         *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
         * @param keeper The address of the Keeper contract
         * @param vaultsRegistry The address of the VaultsRegistry contract
         * @param validatorsRegistry The contract address used for registering validators in beacon chain
         */
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor(address keeper, address vaultsRegistry, address validatorsRegistry) {
          _keeper = keeper;
          _vaultsRegistry = vaultsRegistry;
          _validatorsRegistry = validatorsRegistry;
        }
        /**
         * @dev Internal method for checking whether the vault is collateralized
         * @return true if the vault is collateralized, false otherwise
         */
        function _isCollateralized() internal view returns (bool) {
          return IKeeperRewards(_keeper).isCollateralized(address(this));
        }
        /**
         * @dev Internal method for checking whether the vault is harvested
         */
        function _checkHarvested() internal view {
          if (IKeeperRewards(_keeper).isHarvestRequired(address(this))) revert Errors.NotHarvested();
        }
        /**
         * @dev Internal method for checking whether the vault is collateralized
         */
        function _checkCollateralized() internal view {
          if (!_isCollateralized()) revert Errors.NotCollateralized();
        }
        /**
         * @dev Internal method for checking whether the vault is not collateralized
         */
        function _checkNotCollateralized() internal view {
          if (_isCollateralized()) revert Errors.Collateralized();
        }
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {ISharedMevEscrow} from '../../interfaces/ISharedMevEscrow.sol';
      import {IOwnMevEscrow} from '../../interfaces/IOwnMevEscrow.sol';
      import {IVaultMev} from '../../interfaces/IVaultMev.sol';
      import {VaultState} from './VaultState.sol';
      /**
       * @title VaultMev
       * @author StakeWise
       * @notice Defines the Vaults' MEV functionality
       */
      abstract contract VaultMev is Initializable, VaultState, IVaultMev {
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        address private immutable _sharedMevEscrow;
        address private _ownMevEscrow;
        /**
         * @dev Constructor
         * @dev Since the immutable variable value is stored in the bytecode,
         *      its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.
         * @param sharedMevEscrow The address of the shared MEV escrow
         */
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor(address sharedMevEscrow) {
          _sharedMevEscrow = sharedMevEscrow;
        }
        /// @inheritdoc IVaultMev
        function mevEscrow() public view override returns (address) {
          // SLOAD to memory
          address ownMevEscrow = _ownMevEscrow;
          return ownMevEscrow != address(0) ? ownMevEscrow : _sharedMevEscrow;
        }
        /// @inheritdoc VaultState
        function _harvestAssets(
          IKeeperRewards.HarvestParams calldata harvestParams
        ) internal override returns (int256, bool) {
          (int256 totalAssetsDelta, uint256 unlockedMevDelta, bool harvested) = IKeeperRewards(_keeper)
            .harvest(harvestParams);
          // harvest execution rewards only when consensus rewards were harvested
          if (!harvested) return (totalAssetsDelta, harvested);
          // SLOAD to memory
          address _mevEscrow = mevEscrow();
          if (_mevEscrow == _sharedMevEscrow) {
            if (unlockedMevDelta > 0) {
              // withdraw assets from shared escrow only in case reward is positive
              ISharedMevEscrow(_mevEscrow).harvest(unlockedMevDelta);
            }
            return (totalAssetsDelta, harvested);
          }
          // execution rewards are always equal to what was accumulated in own MEV escrow
          return (totalAssetsDelta + int256(IOwnMevEscrow(_mevEscrow).harvest()), harvested);
        }
        /**
         * @dev Initializes the VaultMev contract
         * @param ownMevEscrow The address of the own MEV escrow contract
         */
        function __VaultMev_init(address ownMevEscrow) internal onlyInitializing {
          if (ownMevEscrow != address(0)) _ownMevEscrow = ownMevEscrow;
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {SafeCast} from '@openzeppelin/contracts/utils/math/SafeCast.sol';
      import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
      import {IVaultState} from '../../interfaces/IVaultState.sol';
      import {IKeeperRewards} from '../../interfaces/IKeeperRewards.sol';
      import {ExitQueue} from '../../libraries/ExitQueue.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultImmutables} from './VaultImmutables.sol';
      import {VaultFee} from './VaultFee.sol';
      /**
       * @title VaultState
       * @author StakeWise
       * @notice Defines Vault's state manipulation
       */
      abstract contract VaultState is VaultImmutables, Initializable, VaultFee, IVaultState {
        using ExitQueue for ExitQueue.History;
        uint128 internal _totalShares;
        uint128 internal _totalAssets;
        /// @inheritdoc IVaultState
        uint128 public override queuedShares;
        uint128 internal _unclaimedAssets;
        ExitQueue.History internal _exitQueue;
        mapping(bytes32 => uint256) internal _exitRequests;
        mapping(address => uint256) internal _balances;
        uint256 private _capacity;
        /// @inheritdoc IVaultState
        function totalShares() external view override returns (uint256) {
          return _totalShares;
        }
        /// @inheritdoc IVaultState
        function totalAssets() external view override returns (uint256) {
          return _totalAssets;
        }
        /// @inheritdoc IVaultState
        function getShares(address account) external view override returns (uint256) {
          return _balances[account];
        }
        /// @inheritdoc IVaultState
        function convertToShares(uint256 assets) public view override returns (uint256 shares) {
          return _convertToShares(assets, Math.Rounding.Floor);
        }
        /// @inheritdoc IVaultState
        function convertToAssets(uint256 shares) public view override returns (uint256 assets) {
          uint256 totalShares_ = _totalShares;
          return (totalShares_ == 0) ? shares : Math.mulDiv(shares, _totalAssets, totalShares_);
        }
        /// @inheritdoc IVaultState
        function capacity() public view override returns (uint256) {
          // SLOAD to memory
          uint256 capacity_ = _capacity;
          // if capacity is not set, it is unlimited
          return capacity_ == 0 ? type(uint256).max : capacity_;
        }
        /// @inheritdoc IVaultState
        function withdrawableAssets() public view override returns (uint256) {
          uint256 vaultAssets = _vaultAssets();
          unchecked {
            // calculate assets that are reserved by users who queued for exit
            // cannot overflow as it is capped with underlying asset total supply
            uint256 reservedAssets = convertToAssets(queuedShares) + _unclaimedAssets;
            return vaultAssets > reservedAssets ? vaultAssets - reservedAssets : 0;
          }
        }
        /// @inheritdoc IVaultState
        function isStateUpdateRequired() external view override returns (bool) {
          return IKeeperRewards(_keeper).isHarvestRequired(address(this));
        }
        /// @inheritdoc IVaultState
        function updateState(
          IKeeperRewards.HarvestParams calldata harvestParams
        ) public virtual override {
          // process total assets delta  since last update
          (int256 totalAssetsDelta, bool harvested) = _harvestAssets(harvestParams);
          // process total assets delta if it has changed
          if (totalAssetsDelta != 0) _processTotalAssetsDelta(totalAssetsDelta);
          // update exit queue every time new update is harvested
          if (harvested) _updateExitQueue();
        }
        /**
         * @dev Internal function for processing rewards and penalties
         * @param totalAssetsDelta The number of assets earned or lost
         */
        function _processTotalAssetsDelta(int256 totalAssetsDelta) internal {
          // SLOAD to memory
          uint256 newTotalAssets = _totalAssets;
          if (totalAssetsDelta < 0) {
            // add penalty to total assets
            newTotalAssets -= uint256(-totalAssetsDelta);
            // update state
            _totalAssets = SafeCast.toUint128(newTotalAssets);
            return;
          }
          // convert assets delta as it is positive
          uint256 profitAssets = uint256(totalAssetsDelta);
          newTotalAssets += profitAssets;
          // update state
          _totalAssets = SafeCast.toUint128(newTotalAssets);
          // calculate admin fee recipient assets
          uint256 feeRecipientAssets = Math.mulDiv(profitAssets, feePercent, _maxFeePercent);
          if (feeRecipientAssets == 0) return;
          // SLOAD to memory
          uint256 totalShares_ = _totalShares;
          // calculate fee recipient's shares
          uint256 feeRecipientShares;
          if (totalShares_ == 0) {
            feeRecipientShares = feeRecipientAssets;
          } else {
            unchecked {
              feeRecipientShares = Math.mulDiv(
                feeRecipientAssets,
                totalShares_,
                newTotalAssets - feeRecipientAssets
              );
            }
          }
          // SLOAD to memory
          address _feeRecipient = feeRecipient;
          // mint shares to the fee recipient
          _mintShares(_feeRecipient, feeRecipientShares);
          emit FeeSharesMinted(_feeRecipient, feeRecipientShares, feeRecipientAssets);
        }
        /**
      \t * @dev Internal function that must be used to process exit queue
         * @dev Make sure that sufficient time passed between exit queue updates (at least 1 day).
                Currently it's restricted by the keeper's harvest interval
         * @return burnedShares The total amount of burned shares
         */
        function _updateExitQueue() internal virtual returns (uint256 burnedShares) {
          // SLOAD to memory
          uint256 _queuedShares = queuedShares;
          if (_queuedShares == 0) return 0;
          // calculate the amount of assets that can be exited
          uint256 unclaimedAssets = _unclaimedAssets;
          uint256 exitedAssets = Math.min(
            _vaultAssets() - unclaimedAssets,
            convertToAssets(_queuedShares)
          );
          if (exitedAssets == 0) return 0;
          // calculate the amount of shares that can be burned
          burnedShares = convertToShares(exitedAssets);
          if (burnedShares == 0) return 0;
          // update queued shares and unclaimed assets
          queuedShares = SafeCast.toUint128(_queuedShares - burnedShares);
          _unclaimedAssets = SafeCast.toUint128(unclaimedAssets + exitedAssets);
          // push checkpoint so that exited assets could be claimed
          _exitQueue.push(burnedShares, exitedAssets);
          emit CheckpointCreated(burnedShares, exitedAssets);
          // update state
          _totalShares -= SafeCast.toUint128(burnedShares);
          _totalAssets -= SafeCast.toUint128(exitedAssets);
        }
        /**
         * @dev Internal function for minting shares
         * @param owner The address of the owner to mint shares to
         * @param shares The number of shares to mint
         */
        function _mintShares(address owner, uint256 shares) internal virtual {
          // update total shares
          _totalShares += SafeCast.toUint128(shares);
          // mint shares
          unchecked {
            // cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value
            _balances[owner] += shares;
          }
        }
        /**
         * @dev Internal function for burning shares
         * @param owner The address of the owner to burn shares for
         * @param shares The number of shares to burn
         */
        function _burnShares(address owner, uint256 shares) internal virtual {
          // burn shares
          _balances[owner] -= shares;
          // update total shares
          unchecked {
            // cannot underflow because the sum of all shares can't exceed the _totalShares
            _totalShares -= SafeCast.toUint128(shares);
          }
        }
        /**
         * @dev Internal conversion function (from assets to shares) with support for rounding direction.
         */
        function _convertToShares(
          uint256 assets,
          Math.Rounding rounding
        ) internal view returns (uint256 shares) {
          uint256 totalShares_ = _totalShares;
          // Will revert if assets > 0, totalShares > 0 and _totalAssets = 0.
          // That corresponds to a case where any asset would represent an infinite amount of shares.
          return
            (assets == 0 || totalShares_ == 0)
              ? assets
              : Math.mulDiv(assets, totalShares_, _totalAssets, rounding);
        }
        /**
         * @dev Internal function for harvesting Vaults' new assets
         * @return The total assets delta after harvest
         * @return `true` when the rewards were harvested, `false` otherwise
         */
        function _harvestAssets(
          IKeeperRewards.HarvestParams calldata harvestParams
        ) internal virtual returns (int256, bool);
        /**
      \t * @dev Internal function for retrieving the total assets stored in the Vault.
                NB! Assets can be forcibly sent to the vault, the returned value must be used with caution
         * @return The total amount of assets stored in the Vault
         */
        function _vaultAssets() internal view virtual returns (uint256);
        /**
         * @dev Initializes the VaultState contract
         * @param capacity_ The amount after which the Vault stops accepting deposits
         */
        function __VaultState_init(uint256 capacity_) internal onlyInitializing {
          if (capacity_ == 0) revert Errors.InvalidCapacity();
          // skip setting capacity if it is unlimited
          if (capacity_ != type(uint256).max) _capacity = capacity_;
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {MerkleProof} from '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
      import {IKeeperValidators} from '../../interfaces/IKeeperValidators.sol';
      import {IVaultValidators} from '../../interfaces/IVaultValidators.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultImmutables} from './VaultImmutables.sol';
      import {VaultAdmin} from './VaultAdmin.sol';
      import {VaultState} from './VaultState.sol';
      /**
       * @title VaultValidators
       * @author StakeWise
       * @notice Defines the validators functionality for the Vault
       */
      abstract contract VaultValidators is
        VaultImmutables,
        Initializable,
        VaultAdmin,
        VaultState,
        IVaultValidators
      {
        uint256 internal constant _validatorLength = 176;
        /// @inheritdoc IVaultValidators
        bytes32 public override validatorsRoot;
        /// @inheritdoc IVaultValidators
        uint256 public override validatorIndex;
        address private _keysManager;
        /// @inheritdoc IVaultValidators
        function keysManager() public view override returns (address) {
          // SLOAD to memory
          address keysManager_ = _keysManager;
          // if keysManager is not set, use admin address
          return keysManager_ == address(0) ? admin : keysManager_;
        }
        /// @inheritdoc IVaultValidators
        function registerValidator(
          IKeeperValidators.ApprovalParams calldata keeperParams,
          bytes32[] calldata proof
        ) external override {
          _checkHarvested();
          // get approval from oracles
          IKeeperValidators(_keeper).approveValidators(keeperParams);
          // check enough withdrawable assets
          if (withdrawableAssets() < _validatorDeposit()) revert Errors.InsufficientAssets();
          // check validator length is valid
          if (keeperParams.validators.length != _validatorLength) revert Errors.InvalidValidator();
          // SLOAD to memory
          uint256 currentIndex = validatorIndex;
          // check matches merkle root and next validator index
          if (
            !MerkleProof.verifyCalldata(
              proof,
              validatorsRoot,
              keccak256(bytes.concat(keccak256(abi.encode(keeperParams.validators, currentIndex))))
            )
          ) {
            revert Errors.InvalidProof();
          }
          // register validator
          _registerSingleValidator(keeperParams.validators);
          // increment index for the next validator
          unchecked {
            // cannot realistically overflow
            validatorIndex = currentIndex + 1;
          }
        }
        /// @inheritdoc IVaultValidators
        function registerValidators(
          IKeeperValidators.ApprovalParams calldata keeperParams,
          uint256[] calldata indexes,
          bool[] calldata proofFlags,
          bytes32[] calldata proof
        ) external override {
          _checkHarvested();
          // get approval from oracles
          IKeeperValidators(_keeper).approveValidators(keeperParams);
          // check enough withdrawable assets
          uint256 validatorsCount = indexes.length;
          if (withdrawableAssets() < _validatorDeposit() * validatorsCount) {
            revert Errors.InsufficientAssets();
          }
          // check validators length is valid
          unchecked {
            if (
              validatorsCount == 0 || validatorsCount * _validatorLength != keeperParams.validators.length
            ) {
              revert Errors.InvalidValidators();
            }
          }
          // check matches merkle root and next validator index
          if (
            !MerkleProof.multiProofVerifyCalldata(
              proof,
              proofFlags,
              validatorsRoot,
              _registerMultipleValidators(keeperParams.validators, indexes)
            )
          ) {
            revert Errors.InvalidProof();
          }
          // increment index for the next validator
          unchecked {
            // cannot realistically overflow
            validatorIndex += validatorsCount;
          }
        }
        /// @inheritdoc IVaultValidators
        function setKeysManager(address keysManager_) external override {
          _checkAdmin();
          if (keysManager_ == address(0)) revert Errors.ZeroAddress();
          // update keysManager address
          _keysManager = keysManager_;
          emit KeysManagerUpdated(msg.sender, keysManager_);
        }
        /// @inheritdoc IVaultValidators
        function setValidatorsRoot(bytes32 _validatorsRoot) external override {
          if (msg.sender != keysManager()) revert Errors.AccessDenied();
          _setValidatorsRoot(_validatorsRoot);
        }
        /**
         * @dev Internal function for updating the validators root externally or from the initializer
         * @param _validatorsRoot The new validators merkle tree root
         */
        function _setValidatorsRoot(bytes32 _validatorsRoot) private {
          validatorsRoot = _validatorsRoot;
          // reset validator index on every root update
          validatorIndex = 0;
          emit ValidatorsRootUpdated(msg.sender, _validatorsRoot);
        }
        /**
         * @dev Internal function for calculating Vault withdrawal credentials
         * @return The credentials used for the validators withdrawals
         */
        function _withdrawalCredentials() internal view returns (bytes memory) {
          return abi.encodePacked(bytes1(0x01), bytes11(0x0), address(this));
        }
        /**
         * @dev Internal function for registering single validator. Must emit ValidatorRegistered event.
         * @param validator The concatenation of the validator public key, signature and deposit data root
         */
        function _registerSingleValidator(bytes calldata validator) internal virtual;
        /**
         * @dev Internal function for registering multiple validators. Must emit ValidatorRegistered event for every validator.
         * @param validators The concatenation of the validators' public key, signature and deposit data root
         * @param indexes The indexes of the leaves for the merkle tree multi proof verification
         * @return leaves The leaves used for the merkle tree multi proof verification
         */
        function _registerMultipleValidators(
          bytes calldata validators,
          uint256[] calldata indexes
        ) internal virtual returns (bytes32[] memory leaves);
        /**
         * @dev Internal function for fetching validator deposit amount
         */
        function _validatorDeposit() internal pure virtual returns (uint256);
        /**
         * @dev Initializes the VaultValidators contract
         * @dev NB! This initializer must be called after VaultState initializer
         */
        function __VaultValidators_init() internal view onlyInitializing {
          if (capacity() < _validatorDeposit()) revert Errors.InvalidCapacity();
        }
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BUSL-1.1
      pragma solidity =0.8.22;
      import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
      import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
      import {ERC1967Utils} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol';
      import {IVaultsRegistry} from '../../interfaces/IVaultsRegistry.sol';
      import {IVaultVersion} from '../../interfaces/IVaultVersion.sol';
      import {Errors} from '../../libraries/Errors.sol';
      import {VaultAdmin} from './VaultAdmin.sol';
      import {VaultImmutables} from './VaultImmutables.sol';
      /**
       * @title VaultVersion
       * @author StakeWise
       * @notice Defines the versioning functionality for the Vault
       */
      abstract contract VaultVersion is
        VaultImmutables,
        Initializable,
        UUPSUpgradeable,
        VaultAdmin,
        IVaultVersion
      {
        bytes4 private constant _initSelector = bytes4(keccak256('initialize(bytes)'));
        /// @inheritdoc IVaultVersion
        function implementation() external view override returns (address) {
          return ERC1967Utils.getImplementation();
        }
        /// @inheritdoc UUPSUpgradeable
        function upgradeToAndCall(
          address newImplementation,
          bytes memory data
        ) public payable override onlyProxy {
          super.upgradeToAndCall(newImplementation, abi.encodeWithSelector(_initSelector, data));
        }
        /// @inheritdoc UUPSUpgradeable
        function _authorizeUpgrade(address newImplementation) internal view override {
          _checkAdmin();
          if (
            newImplementation == address(0) ||
            ERC1967Utils.getImplementation() == newImplementation || // cannot reinit the same implementation
            IVaultVersion(newImplementation).vaultId() != vaultId() || // vault must be of the same type
            IVaultVersion(newImplementation).version() != version() + 1 || // vault cannot skip versions between
            !IVaultsRegistry(_vaultsRegistry).vaultImpls(newImplementation) // new implementation must be registered
          ) {
            revert Errors.UpgradeFailed();
          }
        }
        /// @inheritdoc IVaultVersion
        function vaultId() public pure virtual override returns (bytes32);
        /// @inheritdoc IVaultVersion
        function version() public pure virtual override returns (uint8);
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[50] private __gap;
      }