// File: @openzeppelin/contracts/interfaces/draft-IERC1822.sol



// 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);
}

// File: @openzeppelin/contracts/interfaces/IERC5267.sol



// 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
        );
}

// File: @openzeppelin/contracts/proxy/beacon/IBeacon.sol



// 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);
}

// File: @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol



// 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();
    }
}

// File: @openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol



// 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();
        }
    }
}

// File: @openzeppelin/contracts/proxy/Proxy.sol



// 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();
    }
}

// File: @openzeppelin/contracts/utils/Address.sol



// 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();
        }
    }
}

// File: @openzeppelin/contracts/utils/StorageSlot.sol



// 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: contracts/interfaces/IEthVault.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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 {IMulticall} from './IMulticall.sol';

/**
 * @title IEthVault
 * @author StakeWise
 * @notice Defines the interface for the EthVault contract
 */
interface IEthVault is
  IVaultAdmin,
  IVaultVersion,
  IVaultFee,
  IVaultState,
  IVaultValidators,
  IVaultEnterExit,
  IVaultOsToken,
  IVaultMev,
  IVaultEthStaking,
  IMulticall
{
  /**
   * @dev Struct for initializing the EthVault 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 EthVaultInitParams {
    uint256 capacity;
    uint16 feePercent;
    string metadataIpfsHash;
  }

  /**
   * @notice Initializes the EthVault contract. Must transfer security deposit together with a call.
   * @param params The encoded parameters for initializing the EthVault contract
   */
  function initialize(bytes calldata params) external payable;
}

// File: contracts/interfaces/IEthVaultFactory.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

/**
 * @title IEthVaultFactory
 * @author StakeWise
 * @notice Defines the interface for the ETH Vault Factory contract
 */
interface IEthVaultFactory {
  /**
   * @notice Event emitted on a Vault creation
   * @param admin The address of the Vault admin
   * @param vault The address of the created Vault
   * @param ownMevEscrow The address of the own MEV escrow contract. Zero address if shared MEV escrow is used.
   * @param params The encoded parameters for initializing the Vault contract
   */
  event VaultCreated(
    address indexed admin,
    address indexed vault,
    address ownMevEscrow,
    bytes params
  );

  /**
   * @notice The address of the Vault implementation contract used for proxy creation
   * @return The address of the Vault implementation contract
   */
  function implementation() external view returns (address);

  /**
   * @notice The address of the own MEV escrow contract used for Vault creation
   * @return The address of the MEV escrow contract
   */
  function ownMevEscrow() external view returns (address);

  /**
   * @notice The address of the Vault admin used for Vault creation
   * @return The address of the Vault admin
   */
  function vaultAdmin() external view returns (address);

  /**
   * @notice Create Vault. Must transfer security deposit together with a call.
   * @param params The encoded parameters for initializing the Vault contract
   * @param isOwnMevEscrow Whether to deploy own escrow contract or connect to a smoothing pool for priority fees and MEV rewards
   * @return vault The address of the created Vault
   */
  function createVault(
    bytes calldata params,
    bool isOwnMevEscrow
  ) external payable returns (address vault);
}

// File: contracts/interfaces/IKeeperOracles.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IKeeperRewards.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IKeeperValidators.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IMulticall.sol



// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity =0.8.20;

/**
 * @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);
}

// File: contracts/interfaces/IOwnMevEscrow.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

/**
 * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern
   * @return assets The amount of assets withdrawn
   */
  function harvest() external returns (uint256 assets);
}

// File: contracts/interfaces/IVaultAdmin.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

/**
 * @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;
}

// File: contracts/interfaces/IVaultEnterExit.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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);
}

// File: contracts/interfaces/IVaultEthStaking.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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);
}

// File: contracts/interfaces/IVaultFee.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IVaultMev.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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);
}

// File: contracts/interfaces/IVaultOsToken.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IVaultsRegistry.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

/**
 * @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;
}

// File: contracts/interfaces/IVaultState.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IVaultValidators.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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;
}

// File: contracts/interfaces/IVaultVersion.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

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);
}

// File: contracts/libraries/Errors.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

/**
 * @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();
}

// File: contracts/vaults/ethereum/EthVaultFactory.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
import {IEthVaultFactory} from '../../interfaces/IEthVaultFactory.sol';
import {IEthVault} from '../../interfaces/IEthVault.sol';
import {IVaultsRegistry} from '../../interfaces/IVaultsRegistry.sol';
import {OwnMevEscrow} from './mev/OwnMevEscrow.sol';

/**
 * @title EthVaultFactory
 * @author StakeWise
 * @notice Factory for deploying Ethereum staking Vaults
 */
contract EthVaultFactory is IEthVaultFactory {
  IVaultsRegistry internal immutable _vaultsRegistry;

  /// @inheritdoc IEthVaultFactory
  address public immutable override implementation;

  /// @inheritdoc IEthVaultFactory
  address public override ownMevEscrow;

  /// @inheritdoc IEthVaultFactory
  address public override vaultAdmin;

  /**
   * @dev Constructor
   * @param _implementation The implementation address of Vault
   * @param vaultsRegistry The address of the VaultsRegistry contract
   */
  constructor(address _implementation, IVaultsRegistry vaultsRegistry) {
    implementation = _implementation;
    _vaultsRegistry = vaultsRegistry;
  }

  /// @inheritdoc IEthVaultFactory
  function createVault(
    bytes calldata params,
    bool isOwnMevEscrow
  ) external payable override returns (address vault) {
    // create vault
    vault = address(new ERC1967Proxy(implementation, ''));

    // create MEV escrow contract if needed
    address _mevEscrow;
    if (isOwnMevEscrow) {
      _mevEscrow = address(new OwnMevEscrow(vault));
      // set MEV escrow contract so that it can be initialized in the Vault
      ownMevEscrow = _mevEscrow;
    }

    // set admin so that it can be initialized in the Vault
    vaultAdmin = msg.sender;

    // initialize Vault
    IEthVault(vault).initialize{value: msg.value}(params);

    // cleanup MEV escrow contract
    if (isOwnMevEscrow) delete ownMevEscrow;

    // cleanup admin
    delete vaultAdmin;

    // add vault to the registry
    _vaultsRegistry.addVault(vault);

    // emit event
    emit VaultCreated(msg.sender, vault, _mevEscrow, params);
  }
}

// File: contracts/vaults/ethereum/mev/OwnMevEscrow.sol



// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.20;

import {IOwnMevEscrow} from '../../../interfaces/IOwnMevEscrow.sol';
import {IVaultEthStaking} from '../../../interfaces/IVaultEthStaking.sol';
import {Errors} from '../../../libraries/Errors.sol';

/**
 * @title OwnMevEscrow
 * @author StakeWise
 * @notice Accumulates received MEV. The escrow is owned by the Vault.
 */
contract OwnMevEscrow is IOwnMevEscrow {
  address payable public immutable override vault;

  /// @dev Constructor
  constructor(address _vault) {
    vault = payable(_vault);
  }

  /// @inheritdoc IOwnMevEscrow
  function harvest() external returns (uint256 assets) {
    if (msg.sender != vault) revert Errors.HarvestFailed();

    assets = address(this).balance;
    if (assets == 0) return 0;

    emit Harvested(assets);
    // slither-disable-next-line arbitrary-send-eth
    IVaultEthStaking(msg.sender).receiveFromMevEscrow{value: assets}();
  }

  /**
   * @dev Function for receiving MEV
   */
  receive() external payable {
    emit MevReceived(msg.value);
  }
}