ETH Price: $4,003.57 (+2.91%)

Transaction Decoder

Block:
19461666 at Mar-18-2024 12:24:59 PM +UTC
Transaction Fee:
0.00682782 ETH $27.34
Gas Used:
113,797 Gas / 60 Gwei

Emitted Events:

99 UUPSProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000039dc57cf562736d62be25efc1b37ce43deaf51bc, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000000000000000000000000000000000000000178c )
100 UUPSProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000007d5706f6ef3f89b3951e23e557cdfbc3239d4e2c, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000179e4b28cb277a2 )
101 UUPSProxy.0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb( 0x9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb, 0x0000000000000000000000007d5706f6ef3f89b3951e23e557cdfbc3239d4e2c, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000016d71c2b2e5f2b1 )
102 UUPSProxy.0x4ed779dfda2dd4cb90349b61fba6c125f68e3246023e6109203ddfa8db61ce05( 0x4ed779dfda2dd4cb90349b61fba6c125f68e3246023e6109203ddfa8db61ce05, 0x000000000000000000000000000000000000000000000000000000000000178c, 0000000000000000000000000000000000000000000000000179e4b28cb277a2, 000000000000000000000000000000000000000000000000016d71c2b2e5f2b1, 00000000000000000000000039dc57cf562736d62be25efc1b37ce43deaf51bc, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x308861A4...Fc6DaF216
(ether.fi: Liquidity Pool)
106,037.899125873754224762 Eth106,037.79275835201848956 Eth0.106367521735735202
0x35fA1647...9B6118ac2
0x39DC57cf...3dEaf51Bc
0.018654580772803075 Eth
Nonce: 65
0.118194282508538277 Eth
Nonce: 66
0.099539701735735202
0x7d5706f6...3239D4E2c
(beaverbuild)
17.602403624641038964 Eth17.602471403759601515 Eth0.000067779118562551

Execution Trace

UUPSProxy.b13acedd( )
  • WithdrawRequestNFT.claimWithdraw( tokenId=6028 )
    • UUPSProxy.561bddf8( )
      • LiquidityPool.amountForShare( _share=102877160930320904 ) => ( 106381702278467830 )
        • UUPSProxy.STATICCALL( )
          • EETH.DELEGATECALL( )
          • UUPSProxy.f3fef3a3( )
            • LiquidityPool.withdraw( _recipient=0x39DC57cf562736D62bE25EFC1B37ce43dEaf51Bc, _amount=106367521735735202 ) => ( 102863447538397873 )
              • UUPSProxy.STATICCALL( )
                • EETH.DELEGATECALL( )
                • UUPSProxy.70a08231( )
                  • EETH.balanceOf( _user=0x7d5706f6ef3F89B3951E23e557CDFBC3239D4E2c ) => ( 7606922529369885474033 )
                    • UUPSProxy.51199700( )
                    • UUPSProxy.ee7a7c04( )
                      • EETH.burnShares( _user=0x7d5706f6ef3F89B3951E23e557CDFBC3239D4E2c, _share=102863447538397873 )
                        • UUPSProxy.561bddf8( )
                        • ETH 0.106367521735735202 0x39dc57cf562736d62be25efc1b37ce43deaf51bc.CALL( )
                          File 1 of 6: UUPSProxy
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                          contract UUPSProxy is ERC1967Proxy {
                              constructor(
                                  address _implementation,
                                  bytes memory _data
                              ) ERC1967Proxy(_implementation, _data) {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
                          pragma solidity ^0.8.0;
                          import "../Proxy.sol";
                          import "./ERC1967Upgrade.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, ERC1967Upgrade {
                              /**
                               * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                               *
                               * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                               * function call, and allows initializing the storage of the proxy like a Solidity constructor.
                               */
                              constructor(address _logic, bytes memory _data) payable {
                                  _upgradeToAndCall(_logic, _data, false);
                              }
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _implementation() internal view virtual override returns (address impl) {
                                  return ERC1967Upgrade._getImplementation();
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 {
                                  _beforeFallback();
                                  _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();
                              }
                              /**
                               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                               * is empty.
                               */
                              receive() external payable virtual {
                                  _fallback();
                              }
                              /**
                               * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                               * call, or as part of the Solidity `fallback` or `receive` functions.
                               *
                               * If overridden should call `super._beforeFallback()`.
                               */
                              function _beforeFallback() internal virtual {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeacon.sol";
                          import "../../interfaces/draft-IERC1822.sol";
                          import "../../utils/Address.sol";
                          import "../../utils/StorageSlot.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967Upgrade {
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @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 {
                                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              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 {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @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 {
                                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      Address.isContract(IBeacon(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library Address {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlot {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          

                          File 2 of 6: UUPSProxy
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                          contract UUPSProxy is ERC1967Proxy {
                              constructor(
                                  address _implementation,
                                  bytes memory _data
                              ) ERC1967Proxy(_implementation, _data) {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
                          pragma solidity ^0.8.0;
                          import "../Proxy.sol";
                          import "./ERC1967Upgrade.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, ERC1967Upgrade {
                              /**
                               * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                               *
                               * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                               * function call, and allows initializing the storage of the proxy like a Solidity constructor.
                               */
                              constructor(address _logic, bytes memory _data) payable {
                                  _upgradeToAndCall(_logic, _data, false);
                              }
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _implementation() internal view virtual override returns (address impl) {
                                  return ERC1967Upgrade._getImplementation();
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 {
                                  _beforeFallback();
                                  _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();
                              }
                              /**
                               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                               * is empty.
                               */
                              receive() external payable virtual {
                                  _fallback();
                              }
                              /**
                               * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                               * call, or as part of the Solidity `fallback` or `receive` functions.
                               *
                               * If overridden should call `super._beforeFallback()`.
                               */
                              function _beforeFallback() internal virtual {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeacon.sol";
                          import "../../interfaces/draft-IERC1822.sol";
                          import "../../utils/Address.sol";
                          import "../../utils/StorageSlot.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967Upgrade {
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @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 {
                                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              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 {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @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 {
                                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      Address.isContract(IBeacon(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library Address {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlot {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          

                          File 3 of 6: WithdrawRequestNFT
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin-upgradeable/contracts/token/ERC721/ERC721Upgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
                          import "./interfaces/IeETH.sol";
                          import "./interfaces/ILiquidityPool.sol";
                          import "./interfaces/IWithdrawRequestNFT.sol";
                          import "./interfaces/IMembershipManager.sol";
                          contract WithdrawRequestNFT is ERC721Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IWithdrawRequestNFT {
                              ILiquidityPool public liquidityPool;
                              IeETH public eETH; 
                              IMembershipManager public membershipManager;
                              mapping(uint256 => IWithdrawRequestNFT.WithdrawRequest) private _requests;
                              mapping(address => bool) public admins;
                              uint32 public nextRequestId;
                              uint32 public lastFinalizedRequestId;
                              uint96 public accumulatedDustEEthShares; // to be burned or used to cover the validator churn cost
                              event WithdrawRequestCreated(uint32 indexed requestId, uint256 amountOfEEth, uint256 shareOfEEth, address owner, uint256 fee);
                              event WithdrawRequestClaimed(uint32 indexed requestId, uint256 amountOfEEth, uint256 burntShareOfEEth, address owner, uint256 fee);
                              event WithdrawRequestInvalidated(uint32 indexed requestId);
                              event WithdrawRequestValidated(uint32 indexed requestId);
                              /// @custom:oz-upgrades-unsafe-allow constructor
                              constructor() {
                                  _disableInitializers();
                              }
                              function initialize(address _liquidityPoolAddress, address _eEthAddress, address _membershipManagerAddress) initializer external {
                                  require(_liquidityPoolAddress != address(0), "No zero addresses");
                                  require(_eEthAddress != address(0), "No zero addresses");
                                  __ERC721_init("Withdraw Request NFT", "WithdrawRequestNFT");
                                  __Ownable_init();
                                  __UUPSUpgradeable_init();
                                  liquidityPool = ILiquidityPool(_liquidityPoolAddress);
                                  eETH = IeETH(_eEthAddress);
                                  membershipManager = IMembershipManager(_membershipManagerAddress);
                                  nextRequestId = 1;
                              }
                              /// @notice creates a withdraw request and issues an associated NFT to the recipient
                              /// @dev liquidity pool contract will call this function when a user requests withdraw
                              /// @param amountOfEEth amount of eETH requested for withdrawal
                              /// @param shareOfEEth share of eETH requested for withdrawal
                              /// @param recipient address to recieve with WithdrawRequestNFT
                              /// @param fee fee to be subtracted from amount when recipient calls claimWithdraw
                              /// @return uint256 id of the withdraw request
                              function requestWithdraw(uint96 amountOfEEth, uint96 shareOfEEth, address recipient, uint256 fee) external payable onlyLiquidtyPool returns (uint256) {
                                  uint256 requestId = nextRequestId++;
                                  uint32 feeGwei = uint32(fee / 1 gwei);
                                  _requests[requestId] = IWithdrawRequestNFT.WithdrawRequest(amountOfEEth, shareOfEEth, true, feeGwei);
                                  _safeMint(recipient, requestId);
                                  emit WithdrawRequestCreated(uint32(requestId), amountOfEEth, shareOfEEth, recipient, fee);
                                  return requestId;
                              }
                              function getClaimableAmount(uint256 tokenId) public view returns (uint256) {
                                  require(tokenId < nextRequestId, "Request does not exist");
                                  require(tokenId <= lastFinalizedRequestId, "Request is not finalized");
                                  require(ownerOf(tokenId) != address(0), "Already Claimed");
                                  IWithdrawRequestNFT.WithdrawRequest memory request = _requests[tokenId];
                                  require(request.isValid, "Request is not valid");
                                  // send the lesser value of the originally requested amount of eEth or the current eEth value of the shares
                                  uint256 amountForShares = liquidityPool.amountForShare(request.shareOfEEth);
                                  uint256 amountToTransfer = (request.amountOfEEth < amountForShares) ? request.amountOfEEth : amountForShares;
                                  uint256 fee = uint256(request.feeGwei) * 1 gwei;
                                  return amountToTransfer - fee;
                              }
                              /// @notice called by the NFT owner to claim their ETH
                              /// @dev burns the NFT and transfers ETH from the liquidity pool to the owner minus any fee, withdraw request must be valid and finalized
                              /// @param tokenId the id of the withdraw request and associated NFT
                              function claimWithdraw(uint256 tokenId) public {
                                  require(ownerOf(tokenId) == msg.sender, "Not the owner of the NFT");
                                  IWithdrawRequestNFT.WithdrawRequest memory request = _requests[tokenId];
                                  uint256 fee = uint256(request.feeGwei) * 1 gwei;
                                  uint256 amountToWithdraw = getClaimableAmount(tokenId);
                                  // transfer eth to requester
                                  address recipient = ownerOf(tokenId);
                                  _burn(tokenId);
                                  delete _requests[tokenId];
                                  if (fee > 0) {
                                      // send fee to membership manager
                                      liquidityPool.withdraw(address(membershipManager), fee);
                                  }
                                  uint256 amountBurnedShare = liquidityPool.withdraw(recipient, amountToWithdraw);
                                  uint256 amountUnBurnedShare = request.shareOfEEth - amountBurnedShare;
                                  if (amountUnBurnedShare > 0) {
                                      accumulatedDustEEthShares += uint96(amountUnBurnedShare);
                                  }
                                  emit WithdrawRequestClaimed(uint32(tokenId), amountToWithdraw + fee, amountBurnedShare, recipient, fee);
                              }
                              function batchClaimWithdraw(uint256[] calldata tokenIds) external {
                                  for (uint256 i = 0; i < tokenIds.length; i++) {
                                      claimWithdraw(tokenIds[i]);
                                  }
                              }
                              // a function to transfer accumulated shares to admin
                              function burnAccumulatedDustEEthShares() external onlyAdmin {
                                  require(eETH.totalShares() > accumulatedDustEEthShares, "Inappropriate burn");
                                  uint256 amount = accumulatedDustEEthShares;
                                  accumulatedDustEEthShares = 0;
                                  eETH.burnShares(address(this), amount);
                              }
                              function getRequest(uint256 requestId) external view returns (IWithdrawRequestNFT.WithdrawRequest memory) {
                                  return _requests[requestId];
                              }
                              function isFinalized(uint256 requestId) external view returns (bool) {
                                  return requestId <= lastFinalizedRequestId;
                              }
                              function finalizeRequests(uint256 requestId) external onlyAdmin {
                                  lastFinalizedRequestId = uint32(requestId);
                              }
                              function invalidateRequest(uint256 requestId) external onlyAdmin {
                                  require(_requests[requestId].isValid, "Request is not valid");
                                  _requests[requestId].isValid = false;
                                  emit WithdrawRequestInvalidated(uint32(requestId));
                              }
                              function validateRequest(uint256 requestId) external onlyAdmin {
                                  require(!_requests[requestId].isValid, "Request is valid");
                                  _requests[requestId].isValid = true;
                                  emit WithdrawRequestValidated(uint32(requestId));
                              }
                              function updateAdmin(address _address, bool _isAdmin) external onlyOwner {
                                  require(_address != address(0), "Cannot be address zero");
                                  admins[_address] = _isAdmin;
                              }
                              function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
                              function getImplementation() external view returns (address) {
                                  return _getImplementation();
                              }
                              modifier onlyAdmin() {
                                  require(admins[msg.sender], "Caller is not the admin");
                                  _;
                              }
                              modifier onlyLiquidtyPool() {
                                  require(msg.sender == address(liquidityPool), "Caller is not the liquidity pool");
                                  _;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.2) (token/ERC721/ERC721.sol)
                          pragma solidity ^0.8.0;
                          import "./IERC721Upgradeable.sol";
                          import "./IERC721ReceiverUpgradeable.sol";
                          import "./extensions/IERC721MetadataUpgradeable.sol";
                          import "../../utils/AddressUpgradeable.sol";
                          import "../../utils/ContextUpgradeable.sol";
                          import "../../utils/StringsUpgradeable.sol";
                          import "../../utils/introspection/ERC165Upgradeable.sol";
                          import "../../proxy/utils/Initializable.sol";
                          /**
                           * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
                           * the Metadata extension, but not including the Enumerable extension, which is available separately as
                           * {ERC721Enumerable}.
                           */
                          contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
                              using AddressUpgradeable for address;
                              using StringsUpgradeable for uint256;
                              // Token name
                              string private _name;
                              // Token symbol
                              string private _symbol;
                              // Mapping from token ID to owner address
                              mapping(uint256 => address) private _owners;
                              // Mapping owner address to token count
                              mapping(address => uint256) private _balances;
                              // Mapping from token ID to approved address
                              mapping(uint256 => address) private _tokenApprovals;
                              // Mapping from owner to operator approvals
                              mapping(address => mapping(address => bool)) private _operatorApprovals;
                              /**
                               * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
                               */
                              function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
                                  __ERC721_init_unchained(name_, symbol_);
                              }
                              function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                                  _name = name_;
                                  _symbol = symbol_;
                              }
                              /**
                               * @dev See {IERC165-supportsInterface}.
                               */
                              function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                                  return
                                      interfaceId == type(IERC721Upgradeable).interfaceId ||
                                      interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                                      super.supportsInterface(interfaceId);
                              }
                              /**
                               * @dev See {IERC721-balanceOf}.
                               */
                              function balanceOf(address owner) public view virtual override returns (uint256) {
                                  require(owner != address(0), "ERC721: address zero is not a valid owner");
                                  return _balances[owner];
                              }
                              /**
                               * @dev See {IERC721-ownerOf}.
                               */
                              function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                                  address owner = _ownerOf(tokenId);
                                  require(owner != address(0), "ERC721: invalid token ID");
                                  return owner;
                              }
                              /**
                               * @dev See {IERC721Metadata-name}.
                               */
                              function name() public view virtual override returns (string memory) {
                                  return _name;
                              }
                              /**
                               * @dev See {IERC721Metadata-symbol}.
                               */
                              function symbol() public view virtual override returns (string memory) {
                                  return _symbol;
                              }
                              /**
                               * @dev See {IERC721Metadata-tokenURI}.
                               */
                              function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                                  _requireMinted(tokenId);
                                  string memory baseURI = _baseURI();
                                  return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                              }
                              /**
                               * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                               * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                               * by default, can be overridden in child contracts.
                               */
                              function _baseURI() internal view virtual returns (string memory) {
                                  return "";
                              }
                              /**
                               * @dev See {IERC721-approve}.
                               */
                              function approve(address to, uint256 tokenId) public virtual override {
                                  address owner = ERC721Upgradeable.ownerOf(tokenId);
                                  require(to != owner, "ERC721: approval to current owner");
                                  require(
                                      _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                                      "ERC721: approve caller is not token owner or approved for all"
                                  );
                                  _approve(to, tokenId);
                              }
                              /**
                               * @dev See {IERC721-getApproved}.
                               */
                              function getApproved(uint256 tokenId) public view virtual override returns (address) {
                                  _requireMinted(tokenId);
                                  return _tokenApprovals[tokenId];
                              }
                              /**
                               * @dev See {IERC721-setApprovalForAll}.
                               */
                              function setApprovalForAll(address operator, bool approved) public virtual override {
                                  _setApprovalForAll(_msgSender(), operator, approved);
                              }
                              /**
                               * @dev See {IERC721-isApprovedForAll}.
                               */
                              function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                                  return _operatorApprovals[owner][operator];
                              }
                              /**
                               * @dev See {IERC721-transferFrom}.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) public virtual override {
                                  //solhint-disable-next-line max-line-length
                                  require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                                  _transfer(from, to, tokenId);
                              }
                              /**
                               * @dev See {IERC721-safeTransferFrom}.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) public virtual override {
                                  safeTransferFrom(from, to, tokenId, "");
                              }
                              /**
                               * @dev See {IERC721-safeTransferFrom}.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId,
                                  bytes memory data
                              ) public virtual override {
                                  require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                                  _safeTransfer(from, to, tokenId, data);
                              }
                              /**
                               * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                               * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                               *
                               * `data` is additional data, it has no specified format and it is sent in call to `to`.
                               *
                               * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
                               * implement alternative mechanisms to perform token transfer, such as signature-based.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must exist and be owned by `from`.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function _safeTransfer(
                                  address from,
                                  address to,
                                  uint256 tokenId,
                                  bytes memory data
                              ) internal virtual {
                                  _transfer(from, to, tokenId);
                                  require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
                              }
                              /**
                               * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
                               */
                              function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
                                  return _owners[tokenId];
                              }
                              /**
                               * @dev Returns whether `tokenId` exists.
                               *
                               * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                               *
                               * Tokens start existing when they are minted (`_mint`),
                               * and stop existing when they are burned (`_burn`).
                               */
                              function _exists(uint256 tokenId) internal view virtual returns (bool) {
                                  return _ownerOf(tokenId) != address(0);
                              }
                              /**
                               * @dev Returns whether `spender` is allowed to manage `tokenId`.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               */
                              function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                                  address owner = ERC721Upgradeable.ownerOf(tokenId);
                                  return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
                              }
                              /**
                               * @dev Safely mints `tokenId` and transfers it to `to`.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must not exist.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function _safeMint(address to, uint256 tokenId) internal virtual {
                                  _safeMint(to, tokenId, "");
                              }
                              /**
                               * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                               * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
                               */
                              function _safeMint(
                                  address to,
                                  uint256 tokenId,
                                  bytes memory data
                              ) internal virtual {
                                  _mint(to, tokenId);
                                  require(
                                      _checkOnERC721Received(address(0), to, tokenId, data),
                                      "ERC721: transfer to non ERC721Receiver implementer"
                                  );
                              }
                              /**
                               * @dev Mints `tokenId` and transfers it to `to`.
                               *
                               * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
                               *
                               * Requirements:
                               *
                               * - `tokenId` must not exist.
                               * - `to` cannot be the zero address.
                               *
                               * Emits a {Transfer} event.
                               */
                              function _mint(address to, uint256 tokenId) internal virtual {
                                  require(to != address(0), "ERC721: mint to the zero address");
                                  require(!_exists(tokenId), "ERC721: token already minted");
                                  _beforeTokenTransfer(address(0), to, tokenId, 1);
                                  // Check that tokenId was not minted by `_beforeTokenTransfer` hook
                                  require(!_exists(tokenId), "ERC721: token already minted");
                                  unchecked {
                                      // Will not overflow unless all 2**256 token ids are minted to the same owner.
                                      // Given that tokens are minted one by one, it is impossible in practice that
                                      // this ever happens. Might change if we allow batch minting.
                                      // The ERC fails to describe this case.
                                      _balances[to] += 1;
                                  }
                                  _owners[tokenId] = to;
                                  emit Transfer(address(0), to, tokenId);
                                  _afterTokenTransfer(address(0), to, tokenId, 1);
                              }
                              /**
                               * @dev Destroys `tokenId`.
                               * The approval is cleared when the token is burned.
                               * This is an internal function that does not check if the sender is authorized to operate on the token.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               *
                               * Emits a {Transfer} event.
                               */
                              function _burn(uint256 tokenId) internal virtual {
                                  address owner = ERC721Upgradeable.ownerOf(tokenId);
                                  _beforeTokenTransfer(owner, address(0), tokenId, 1);
                                  // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
                                  owner = ERC721Upgradeable.ownerOf(tokenId);
                                  // Clear approvals
                                  delete _tokenApprovals[tokenId];
                                  unchecked {
                                      // Cannot overflow, as that would require more tokens to be burned/transferred
                                      // out than the owner initially received through minting and transferring in.
                                      _balances[owner] -= 1;
                                  }
                                  delete _owners[tokenId];
                                  emit Transfer(owner, address(0), tokenId);
                                  _afterTokenTransfer(owner, address(0), tokenId, 1);
                              }
                              /**
                               * @dev Transfers `tokenId` from `from` to `to`.
                               *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
                               *
                               * Requirements:
                               *
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must be owned by `from`.
                               *
                               * Emits a {Transfer} event.
                               */
                              function _transfer(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) internal virtual {
                                  require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                                  require(to != address(0), "ERC721: transfer to the zero address");
                                  _beforeTokenTransfer(from, to, tokenId, 1);
                                  // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
                                  require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                                  // Clear approvals from the previous owner
                                  delete _tokenApprovals[tokenId];
                                  unchecked {
                                      // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                                      // `from`'s balance is the number of token held, which is at least one before the current
                                      // transfer.
                                      // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                                      // all 2**256 token ids to be minted, which in practice is impossible.
                                      _balances[from] -= 1;
                                      _balances[to] += 1;
                                  }
                                  _owners[tokenId] = to;
                                  emit Transfer(from, to, tokenId);
                                  _afterTokenTransfer(from, to, tokenId, 1);
                              }
                              /**
                               * @dev Approve `to` to operate on `tokenId`
                               *
                               * Emits an {Approval} event.
                               */
                              function _approve(address to, uint256 tokenId) internal virtual {
                                  _tokenApprovals[tokenId] = to;
                                  emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
                              }
                              /**
                               * @dev Approve `operator` to operate on all of `owner` tokens
                               *
                               * Emits an {ApprovalForAll} event.
                               */
                              function _setApprovalForAll(
                                  address owner,
                                  address operator,
                                  bool approved
                              ) internal virtual {
                                  require(owner != operator, "ERC721: approve to caller");
                                  _operatorApprovals[owner][operator] = approved;
                                  emit ApprovalForAll(owner, operator, approved);
                              }
                              /**
                               * @dev Reverts if the `tokenId` has not been minted yet.
                               */
                              function _requireMinted(uint256 tokenId) internal view virtual {
                                  require(_exists(tokenId), "ERC721: invalid token ID");
                              }
                              /**
                               * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
                               * The call is not executed if the target address is not a contract.
                               *
                               * @param from address representing the previous owner of the given token ID
                               * @param to target address that will receive the tokens
                               * @param tokenId uint256 ID of the token to be transferred
                               * @param data bytes optional data to send along with the call
                               * @return bool whether the call correctly returned the expected magic value
                               */
                              function _checkOnERC721Received(
                                  address from,
                                  address to,
                                  uint256 tokenId,
                                  bytes memory data
                              ) private returns (bool) {
                                  if (to.isContract()) {
                                      try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                                          return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
                                      } catch (bytes memory reason) {
                                          if (reason.length == 0) {
                                              revert("ERC721: transfer to non ERC721Receiver implementer");
                                          } else {
                                              /// @solidity memory-safe-assembly
                                              assembly {
                                                  revert(add(32, reason), mload(reason))
                                              }
                                          }
                                      }
                                  } else {
                                      return true;
                                  }
                              }
                              /**
                               * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                               * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                               *
                               * Calling conditions:
                               *
                               * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
                               * - When `from` is zero, the tokens will be minted for `to`.
                               * - When `to` is zero, ``from``'s tokens will be burned.
                               * - `from` and `to` are never both zero.
                               * - `batchSize` is non-zero.
                               *
                               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                               */
                              function _beforeTokenTransfer(
                                  address from,
                                  address to,
                                  uint256 firstTokenId,
                                  uint256 batchSize
                              ) internal virtual {}
                              /**
                               * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                               * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                               *
                               * Calling conditions:
                               *
                               * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
                               * - When `from` is zero, the tokens were minted for `to`.
                               * - When `to` is zero, ``from``'s tokens were burned.
                               * - `from` and `to` are never both zero.
                               * - `batchSize` is non-zero.
                               *
                               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                               */
                              function _afterTokenTransfer(
                                  address from,
                                  address to,
                                  uint256 firstTokenId,
                                  uint256 batchSize
                              ) internal virtual {}
                              /**
                               * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
                               *
                               * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
                               * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
                               * that `ownerOf(tokenId)` is `a`.
                               */
                              // solhint-disable-next-line func-name-mixedcase
                              function __unsafe_increaseBalance(address account, uint256 amount) internal {
                                  _balances[account] += amount;
                              }
                              /**
                               * @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[44] private __gap;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
                          pragma solidity ^0.8.0;
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
                          import "./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.
                           *
                           * _Available since v4.1._
                           */
                          abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
                              function __UUPSUpgradeable_init() internal onlyInitializing {
                              }
                              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                              }
                              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
                              address private immutable __self = address(this);
                              /**
                               * @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() {
                                  require(address(this) != __self, "Function must be called through delegatecall");
                                  require(_getImplementation() == __self, "Function must be called through active proxy");
                                  _;
                              }
                              /**
                               * @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() {
                                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                                  _;
                              }
                              /**
                               * @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 override notDelegated returns (bytes32) {
                                  return _IMPLEMENTATION_SLOT;
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeTo(address newImplementation) external virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                               * encoded in `data`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, data, true);
                              }
                              /**
                               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                               * {upgradeTo} and {upgradeToAndCall}.
                               *
                               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                               *
                               * ```solidity
                               * function _authorizeUpgrade(address) internal override onlyOwner {}
                               * ```
                               */
                              function _authorizeUpgrade(address newImplementation) 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: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                          pragma solidity ^0.8.0;
                          import "../utils/ContextUpgradeable.sol";
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Contract module which provides a basic access control mechanism, where
                           * there is an account (an owner) that can be granted exclusive access to
                           * specific functions.
                           *
                           * By default, the owner account will be the one that deploys the contract. This
                           * can later be changed with {transferOwnership}.
                           *
                           * This module is used through inheritance. It will make available the modifier
                           * `onlyOwner`, which can be applied to your functions to restrict their use to
                           * the owner.
                           */
                          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                              address private _owner;
                              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                              /**
                               * @dev Initializes the contract setting the deployer as the initial owner.
                               */
                              function __Ownable_init() internal onlyInitializing {
                                  __Ownable_init_unchained();
                              }
                              function __Ownable_init_unchained() internal onlyInitializing {
                                  _transferOwnership(_msgSender());
                              }
                              /**
                               * @dev Throws if called by any account other than the owner.
                               */
                              modifier onlyOwner() {
                                  _checkOwner();
                                  _;
                              }
                              /**
                               * @dev Returns the address of the current owner.
                               */
                              function owner() public view virtual returns (address) {
                                  return _owner;
                              }
                              /**
                               * @dev Throws if the sender is not the owner.
                               */
                              function _checkOwner() internal view virtual {
                                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
                              }
                              /**
                               * @dev Leaves the contract without owner. It will not be possible to call
                               * `onlyOwner` functions anymore. Can only be called by the current owner.
                               *
                               * NOTE: Renouncing ownership will leave the contract without an owner,
                               * thereby removing any functionality that is only available to the owner.
                               */
                              function renounceOwnership() public virtual onlyOwner {
                                  _transferOwnership(address(0));
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Can only be called by the current owner.
                               */
                              function transferOwnership(address newOwner) public virtual onlyOwner {
                                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                                  _transferOwnership(newOwner);
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Internal function without access restriction.
                               */
                              function _transferOwnership(address newOwner) internal virtual {
                                  address oldOwner = _owner;
                                  _owner = newOwner;
                                  emit OwnershipTransferred(oldOwner, newOwner);
                              }
                              /**
                               * @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[49] private __gap;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IeETH {
                              function name() external pure returns (string memory);
                              function symbol() external pure returns (string memory);
                              function decimals() external pure returns (uint8);
                              function totalShares() external view returns (uint256);
                              function shares(address _user) external view returns (uint256);
                              function balanceOf(address _user) external view returns (uint256);
                              function initialize(address _liquidityPool) external;
                              function mintShares(address _user, uint256 _share) external;
                              function burnShares(address _user, uint256 _share) external;
                              function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool);
                              function transfer(address _recipient, uint256 _amount) external returns (bool);
                              function approve(address _spender, uint256 _amount) external returns (bool);
                              function increaseAllowance(address _spender, uint256 _increaseAmount) external returns (bool);
                              function decreaseAllowance(address _spender, uint256 _decreaseAmount) external returns (bool);
                              function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./IStakingManager.sol";
                          interface ILiquidityPool {
                              struct PermitInput {
                                  uint256 value;
                                  uint256 deadline;
                                  uint8 v;
                                  bytes32 r;
                                  bytes32 s;
                              } 
                              enum SourceOfFunds {
                                  UNDEFINED,
                                  EETH,
                                  ETHER_FAN,
                                  DELEGATED_STAKING
                              }
                              struct FundStatistics {
                                  uint32 numberOfValidators;
                                  uint32 targetWeight;
                              }
                              // Necessary to preserve "statelessness" of dutyForWeek().
                              // Handles case where new users join/leave holder list during an active slot
                              struct HoldersUpdate {
                                  uint32 timestamp;
                                  uint32 startOfSlotNumOwners;
                              }
                              struct BnftHolder {
                                  address holder;
                                  uint32 timestamp;
                              }
                              struct BnftHoldersIndex {
                                  bool registered;
                                  uint32 index;
                              }
                              function initialize(address _eEthAddress, address _stakingManagerAddress, address _nodesManagerAddress, address _membershipManagerAddress, address _tNftAddress) external;
                              function numPendingDeposits() external view returns (uint32);
                              function totalValueOutOfLp() external view returns (uint128);
                              function totalValueInLp() external view returns (uint128);
                              function getTotalEtherClaimOf(address _user) external view returns (uint256);
                              function getTotalPooledEther() external view returns (uint256);
                              function sharesForAmount(uint256 _amount) external view returns (uint256);
                              function sharesForWithdrawalAmount(uint256 _amount) external view returns (uint256);
                              function amountForShare(uint256 _share) external view returns (uint256);
                              function deposit() external payable returns (uint256);
                              function deposit(address _referral) external payable returns (uint256);
                              function deposit(address _user, address _referral) external payable returns (uint256);
                              function withdraw(address _recipient, uint256 _amount) external returns (uint256);
                              function requestWithdraw(address recipient, uint256 amount) external returns (uint256);
                              function requestWithdrawWithPermit(address _owner, uint256 _amount, PermitInput calldata _permit) external returns (uint256);
                              function requestMembershipNFTWithdraw(address recipient, uint256 amount, uint256 fee) external returns (uint256);
                              function batchDepositAsBnftHolder(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external payable returns (uint256[] memory);
                              function batchRegisterAsBnftHolder(bytes32 _depositRoot, uint256[] calldata _validatorIds, IStakingManager.DepositData[] calldata _registerValidatorDepositData, bytes32[] calldata _depositDataRootApproval, bytes[] calldata _signaturesForApprovalDeposit) external;
                              function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubKey, bytes[] calldata _signature) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function sendExitRequests(uint256[] calldata _validatorIds) external;
                              function rebase(int128 _accruedRewards) external;
                              function addEthAmountLockedForWithdrawal(uint128 _amount) external;
                              
                              function setStakingTargetWeights(uint32 _eEthWeight, uint32 _etherFanWeight) external;
                              function updateAdmin(address _newAdmin, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                              
                              function decreaseSourceOfFundsValidators(uint32 numberOfEethValidators, uint32 numberOfEtherFanValidators) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IWithdrawRequestNFT {
                              struct WithdrawRequest {
                                  uint96  amountOfEEth;
                                  uint96  shareOfEEth;
                                  bool    isValid;
                                  uint32  feeGwei;
                              }
                              function initialize(address _liquidityPoolAddress, address _eEthAddress, address _membershipManager) external;
                              function requestWithdraw(uint96 amountOfEEth, uint96 shareOfEEth, address requester, uint256 fee) external payable returns (uint256);
                              function claimWithdraw(uint256 requestId) external;
                              function getRequest(uint256 requestId) external view returns (WithdrawRequest memory);
                              function isFinalized(uint256 requestId) external view returns (bool);
                              function invalidateRequest(uint256 requestId) external;
                              function finalizeRequests(uint256 upperBound) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IMembershipManager {
                              struct TokenDeposit {
                                  uint128 amounts;
                                  uint128 shares;
                              }
                              struct TokenData {
                                  uint96 vaultShare;
                                  uint40 baseLoyaltyPoints;
                                  uint40 baseTierPoints;
                                  uint32 prevPointsAccrualTimestamp;
                                  uint32 prevTopUpTimestamp;
                                  uint8  tier;
                                  uint8  version;
                              }
                              // Used for V1
                              struct TierVault {
                                  uint128 totalPooledEEthShares; // total share of eEth in the tier vault
                                  uint128 totalVaultShares; // total share of the tier vault
                              }
                              // Used for V0
                              struct TierDeposit {
                                  uint128 amounts; // total pooled eth amount
                                  uint128 shares; // total pooled eEth shares
                              }
                              struct TierData {
                                  uint96 rewardsGlobalIndex;
                                  uint40 requiredTierPoints;
                                  uint24 weight;
                                  uint96  __gap;
                              }
                              // State-changing functions
                              function wrapEthForEap(uint256 _amount, uint256 _amountForPoint, uint32  _eapDepositBlockNumber, uint256 _snapshotEthAmount, uint256 _points, bytes32[] calldata _merkleProof) external payable returns (uint256);
                              function wrapEth(uint256 _amount, uint256 _amountForPoint) external payable returns (uint256);
                              function wrapEth(uint256 _amount, uint256 _amountForPoint, address _referral) external payable returns (uint256);
                              function topUpDepositWithEth(uint256 _tokenId, uint128 _amount, uint128 _amountForPoints) external payable;
                              function requestWithdraw(uint256 _tokenId, uint256 _amount) external returns (uint256);
                              function requestWithdrawAndBurn(uint256 _tokenId) external returns (uint256);
                              function claim(uint256 _tokenId) external;
                              function migrateFromV0ToV1(uint256 _tokenId) external;
                              // Getter functions
                              function tokenDeposits(uint256) external view returns (uint128, uint128);
                              function tokenData(uint256) external view returns (uint96, uint40, uint40, uint32, uint32, uint8, uint8);
                              function tierDeposits(uint256) external view returns (uint128, uint128);
                              function tierData(uint256) external view returns (uint96, uint40, uint24, uint96);
                              function rewardsGlobalIndex(uint8 _tier) external view returns (uint256);
                              function allTimeHighDepositAmount(uint256 _tokenId) external view returns (uint256);
                              function tierForPoints(uint40 _tierPoints) external view returns (uint8);
                              function canTopUp(uint256 _tokenId, uint256 _totalAmount, uint128 _amount, uint128 _amountForPoints) external view returns (bool);
                              function pointsBoostFactor() external view returns (uint16);
                              function pointsGrowthRate() external view returns (uint16);
                              function maxDepositTopUpPercent() external view returns (uint8);
                              function numberOfTiers() external view returns (uint8);
                              function getImplementation() external view returns (address);
                              function minimumAmountForMint() external view returns (uint256);
                              function eEthShareForVaultShare(uint8 _tier, uint256 _vaultShare) external view returns (uint256);
                              function vaultShareForEEthShare(uint8 _tier, uint256 _eEthShare) external view returns (uint256);
                              function ethAmountForVaultShare(uint8 _tier, uint256 _vaultShare) external view returns (uint256);
                              function vaultShareForEthAmount(uint8 _tier, uint256 _ethAmount) external view returns (uint256);
                              // only Owner
                              function initializeOnUpgrade(address _etherFiAdminAddress, uint256 _fanBoostThresholdAmount, uint16 _burnFeeWaiverPeriodInDays) external;
                              function setWithdrawalLockBlocks(uint32 _blocks) external;
                              function updatePointsParams(uint16 _newPointsBoostFactor, uint16 _newPointsGrowthRate) external;
                              function rebase(int128 _accruedRewards) external;
                              function addNewTier(uint40 _requiredTierPoints, uint24 _weight) external;
                              function updateTier(uint8 _tier, uint40 _requiredTierPoints, uint24 _weight) external;
                              function setPoints(uint256 _tokenId, uint40 _loyaltyPoints, uint40 _tierPoints) external;
                              function setDepositAmountParams(uint56 _minDepositGwei, uint8 _maxDepositTopUpPercent) external;
                              function setTopUpCooltimePeriod(uint32 _newWaitTime) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
                          pragma solidity ^0.8.0;
                          import "../../utils/introspection/IERC165Upgradeable.sol";
                          /**
                           * @dev Required interface of an ERC721 compliant contract.
                           */
                          interface IERC721Upgradeable is IERC165Upgradeable {
                              /**
                               * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                               */
                              event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                              /**
                               * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                               */
                              event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                              /**
                               * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                               */
                              event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                              /**
                               * @dev Returns the number of tokens in ``owner``'s account.
                               */
                              function balanceOf(address owner) external view returns (uint256 balance);
                              /**
                               * @dev Returns the owner of the `tokenId` token.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               */
                              function ownerOf(uint256 tokenId) external view returns (address owner);
                              /**
                               * @dev Safely transfers `tokenId` token from `from` to `to`.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must exist and be owned by `from`.
                               * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId,
                                  bytes calldata data
                              ) external;
                              /**
                               * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                               * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must exist and be owned by `from`.
                               * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) external;
                              /**
                               * @dev Transfers `tokenId` token from `from` to `to`.
                               *
                               * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                               * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                               * understand this adds an external call which potentially creates a reentrancy vulnerability.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must be owned by `from`.
                               * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) external;
                              /**
                               * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                               * The approval is cleared when the token is transferred.
                               *
                               * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                               *
                               * Requirements:
                               *
                               * - The caller must own the token or be an approved operator.
                               * - `tokenId` must exist.
                               *
                               * Emits an {Approval} event.
                               */
                              function approve(address to, uint256 tokenId) external;
                              /**
                               * @dev Approve or remove `operator` as an operator for the caller.
                               * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                               *
                               * Requirements:
                               *
                               * - The `operator` cannot be the caller.
                               *
                               * Emits an {ApprovalForAll} event.
                               */
                              function setApprovalForAll(address operator, bool _approved) external;
                              /**
                               * @dev Returns the account approved for `tokenId` token.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               */
                              function getApproved(uint256 tokenId) external view returns (address operator);
                              /**
                               * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                               *
                               * See {setApprovalForAll}
                               */
                              function isApprovedForAll(address owner, address operator) external view returns (bool);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @title ERC721 token receiver interface
                           * @dev Interface for any contract that wants to support safeTransfers
                           * from ERC721 asset contracts.
                           */
                          interface IERC721ReceiverUpgradeable {
                              /**
                               * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                               * by `operator` from `from`, this function is called.
                               *
                               * It must return its Solidity selector to confirm the token transfer.
                               * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                               *
                               * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                               */
                              function onERC721Received(
                                  address operator,
                                  address from,
                                  uint256 tokenId,
                                  bytes calldata data
                              ) external returns (bytes4);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
                          pragma solidity ^0.8.0;
                          import "../IERC721Upgradeable.sol";
                          /**
                           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                           * @dev See https://eips.ethereum.org/EIPS/eip-721
                           */
                          interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                              /**
                               * @dev Returns the token collection name.
                               */
                              function name() external view returns (string memory);
                              /**
                               * @dev Returns the token collection symbol.
                               */
                              function symbol() external view returns (string memory);
                              /**
                               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                               */
                              function tokenURI(uint256 tokenId) external view returns (string memory);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library AddressUpgradeable {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                          pragma solidity ^0.8.0;
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Provides information about the current execution context, including the
                           * sender of the transaction and its data. While these are generally available
                           * via msg.sender and msg.data, they should not be accessed in such a direct
                           * manner, since when dealing with meta-transactions the account sending and
                           * paying for execution may not be the actual sender (as far as an application
                           * is concerned).
                           *
                           * This contract is only required for intermediate, library-like contracts.
                           */
                          abstract contract ContextUpgradeable is Initializable {
                              function __Context_init() internal onlyInitializing {
                              }
                              function __Context_init_unchained() internal onlyInitializing {
                              }
                              function _msgSender() internal view virtual returns (address) {
                                  return msg.sender;
                              }
                              function _msgData() internal view virtual returns (bytes calldata) {
                                  return msg.data;
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
                          pragma solidity ^0.8.0;
                          import "./math/MathUpgradeable.sol";
                          /**
                           * @dev String operations.
                           */
                          library StringsUpgradeable {
                              bytes16 private constant _SYMBOLS = "0123456789abcdef";
                              uint8 private constant _ADDRESS_LENGTH = 20;
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                               */
                              function toString(uint256 value) internal pure returns (string memory) {
                                  unchecked {
                                      uint256 length = MathUpgradeable.log10(value) + 1;
                                      string memory buffer = new string(length);
                                      uint256 ptr;
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          ptr := add(buffer, add(32, length))
                                      }
                                      while (true) {
                                          ptr--;
                                          /// @solidity memory-safe-assembly
                                          assembly {
                                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                                          }
                                          value /= 10;
                                          if (value == 0) break;
                                      }
                                      return buffer;
                                  }
                              }
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                               */
                              function toHexString(uint256 value) internal pure returns (string memory) {
                                  unchecked {
                                      return toHexString(value, MathUpgradeable.log256(value) + 1);
                                  }
                              }
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                               */
                              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                  bytes memory buffer = new bytes(2 * length + 2);
                                  buffer[0] = "0";
                                  buffer[1] = "x";
                                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                                      buffer[i] = _SYMBOLS[value & 0xf];
                                      value >>= 4;
                                  }
                                  require(value == 0, "Strings: hex length insufficient");
                                  return string(buffer);
                              }
                              /**
                               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                               */
                              function toHexString(address addr) internal pure returns (string memory) {
                                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
                          pragma solidity ^0.8.0;
                          import "./IERC165Upgradeable.sol";
                          import "../../proxy/utils/Initializable.sol";
                          /**
                           * @dev Implementation of the {IERC165} interface.
                           *
                           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                           * for the additional interface id that will be supported. For example:
                           *
                           * ```solidity
                           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                           * }
                           * ```
                           *
                           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
                           */
                          abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                              function __ERC165_init() internal onlyInitializing {
                              }
                              function __ERC165_init_unchained() internal onlyInitializing {
                              }
                              /**
                               * @dev See {IERC165-supportsInterface}.
                               */
                              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                                  return interfaceId == type(IERC165Upgradeable).interfaceId;
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
                          pragma solidity ^0.8.2;
                          import "../../utils/AddressUpgradeable.sol";
                          /**
                           * @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]
                           * ```
                           * 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 Indicates that the contract has been initialized.
                               * @custom:oz-retyped-from bool
                               */
                              uint8 private _initialized;
                              /**
                               * @dev Indicates that the contract is in the process of being initialized.
                               */
                              bool private _initializing;
                              /**
                               * @dev Triggered when the contract has been initialized or reinitialized.
                               */
                              event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
                               * constructor.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier initializer() {
                                  bool isTopLevelCall = !_initializing;
                                  require(
                                      (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                      "Initializable: contract is already initialized"
                                  );
                                  _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 255 will prevent any future reinitialization.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier reinitializer(uint8 version) {
                                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                  _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() {
                                  require(_initializing, "Initializable: contract is not initializing");
                                  _;
                              }
                              /**
                               * @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 {
                                  require(!_initializing, "Initializable: contract is initializing");
                                  if (_initialized < type(uint8).max) {
                                      _initialized = type(uint8).max;
                                      emit Initialized(type(uint8).max);
                                  }
                              }
                              /**
                               * @dev Returns the highest version that has been initialized. See {reinitializer}.
                               */
                              function _getInitializedVersion() internal view returns (uint8) {
                                  return _initialized;
                              }
                              /**
                               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                               */
                              function _isInitializing() internal view returns (bool) {
                                  return _initializing;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 IERC1822ProxiableUpgradeable {
                              /**
                               * @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 v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeaconUpgradeable.sol";
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../../utils/AddressUpgradeable.sol";
                          import "../../utils/StorageSlotUpgradeable.sol";
                          import "../utils/Initializable.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967UpgradeUpgradeable is Initializable {
                              function __ERC1967Upgrade_init() internal onlyInitializing {
                              }
                              function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
                              }
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _getImplementation() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 implementation slot.
                               */
                              function _setImplementation(address newImplementation) private {
                                  require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              function _getAdmin() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 admin slot.
                               */
                              function _setAdmin(address newAdmin) private {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @dev Returns the current beacon.
                               */
                              function _getBeacon() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
                              }
                              /**
                               * @dev Stores a new beacon in the EIP1967 beacon slot.
                               */
                              function _setBeacon(address newBeacon) private {
                                  require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                                  }
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                                  require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                              }
                              /**
                               * @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: MIT
                          pragma solidity 0.8.13;
                          import "./ILiquidityPool.sol";
                          interface IStakingManager {
                              struct DepositData {
                                  bytes publicKey;
                                  bytes signature;
                                  bytes32 depositDataRoot;
                                  string ipfsHashForEncryptedValidatorKey;
                              }
                              struct StakerInfo {
                                  address staker;
                                  ILiquidityPool.SourceOfFunds sourceOfFund;
                              }
                              function bidIdToStaker(uint256 id) external view returns (address);
                              function getEtherFiNodeBeacon() external view returns (address);
                              function initialize(address _auctionAddress, address _depositContractAddress) external;
                              function setEtherFiNodesManagerAddress(address _managerAddress) external;
                              function setLiquidityPoolAddress(address _liquidityPoolAddress) external;
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, address _staker, ILiquidityPool.SourceOfFunds source, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, DepositData[] calldata _depositData) external;
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, address _bNftRecipient, address _tNftRecipient, DepositData[] calldata _depositData, address _user) external;
                              function batchApproveRegistration(uint256[] memory _validatorId, bytes[] calldata _pubKey, bytes[] calldata _signature, bytes32[] calldata _depositDataRootApproval) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function batchCancelDepositAsBnftHolder(uint256[] calldata _validatorIds, address _caller) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC165 standard, as defined in the
                           * https://eips.ethereum.org/EIPS/eip-165[EIP].
                           *
                           * Implementers can declare support of contract interfaces, which can then be
                           * queried by others ({ERC165Checker}).
                           *
                           * For an implementation, see {ERC165}.
                           */
                          interface IERC165Upgradeable {
                              /**
                               * @dev Returns true if this contract implements the interface defined by
                               * `interfaceId`. See the corresponding
                               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                               * to learn more about how these ids are created.
                               *
                               * This function call must use less than 30 000 gas.
                               */
                              function supportsInterface(bytes4 interfaceId) external view returns (bool);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Standard math utilities missing in the Solidity language.
                           */
                          library MathUpgradeable {
                              enum Rounding {
                                  Down, // Toward negative infinity
                                  Up, // Toward infinity
                                  Zero // Toward zero
                              }
                              /**
                               * @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 up instead
                               * of rounding down.
                               */
                              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                  // (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; // Least significant 256 bits of the product
                                      uint256 prod1; // Most significant 256 bits of the product
                                      assembly {
                                          let mm := mulmod(x, y, not(0))
                                          prod0 := mul(x, y)
                                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                      }
                                      // Handle non-overflow cases, 256 by 256 division.
                                      if (prod1 == 0) {
                                          return prod0 / denominator;
                                      }
                                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                      require(denominator > prod1);
                                      ///////////////////////////////////////////////
                                      // 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.
                                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                                      uint256 twos = denominator & (~denominator + 1);
                                      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 (rounding == Rounding.Up && 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 down.
                               *
                               * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 2, rounded down, of a positive value.
                               * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 10, rounded down, of a positive value.
                               * 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 + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 256, rounded down, of a positive value.
                               * 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 10, 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 + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev This is the interface that {BeaconProxy} expects of its beacon.
                           */
                          interface IBeaconUpgradeable {
                              /**
                               * @dev Must return an address that can be used as a delegate call target.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlotUpgradeable {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          

                          File 4 of 6: UUPSProxy
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                          contract UUPSProxy is ERC1967Proxy {
                              constructor(
                                  address _implementation,
                                  bytes memory _data
                              ) ERC1967Proxy(_implementation, _data) {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
                          pragma solidity ^0.8.0;
                          import "../Proxy.sol";
                          import "./ERC1967Upgrade.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, ERC1967Upgrade {
                              /**
                               * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                               *
                               * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                               * function call, and allows initializing the storage of the proxy like a Solidity constructor.
                               */
                              constructor(address _logic, bytes memory _data) payable {
                                  _upgradeToAndCall(_logic, _data, false);
                              }
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _implementation() internal view virtual override returns (address impl) {
                                  return ERC1967Upgrade._getImplementation();
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 {
                                  _beforeFallback();
                                  _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();
                              }
                              /**
                               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                               * is empty.
                               */
                              receive() external payable virtual {
                                  _fallback();
                              }
                              /**
                               * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                               * call, or as part of the Solidity `fallback` or `receive` functions.
                               *
                               * If overridden should call `super._beforeFallback()`.
                               */
                              function _beforeFallback() internal virtual {}
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeacon.sol";
                          import "../../interfaces/draft-IERC1822.sol";
                          import "../../utils/Address.sol";
                          import "../../utils/StorageSlot.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967Upgrade {
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @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 {
                                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              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 {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @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 {
                                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      Address.isContract(IBeacon(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library Address {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function functionDelegateCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlot {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          

                          File 5 of 6: LiquidityPool
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                          import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
                          import "@openzeppelin-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/token/ERC721/IERC721ReceiverUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
                          import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
                          import "./interfaces/IRegulationsManager.sol";
                          import "./interfaces/IStakingManager.sol";
                          import "./interfaces/IEtherFiNodesManager.sol";
                          import "./interfaces/IeETH.sol";
                          import "./interfaces/IStakingManager.sol";
                          import "./interfaces/IMembershipManager.sol";
                          import "./interfaces/ITNFT.sol";
                          import "./interfaces/IWithdrawRequestNFT.sol";
                          import "./interfaces/ILiquidityPool.sol";
                          import "./interfaces/IEtherFiAdmin.sol";
                          import "./interfaces/IAuctionManager.sol";
                          import "./interfaces/ILiquifier.sol";
                          contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, ILiquidityPool {
                              //--------------------------------------------------------------------------------------
                              //---------------------------------  STATE-VARIABLES  ----------------------------------
                              //--------------------------------------------------------------------------------------
                              IStakingManager public stakingManager;
                              IEtherFiNodesManager public nodesManager;
                              IRegulationsManager public DEPRECATED_regulationsManager;
                              IMembershipManager public membershipManager;
                              ITNFT public tNft;
                              IeETH public eETH; 
                              bool public DEPRECATED_eEthliquidStakingOpened;
                              uint128 public totalValueOutOfLp;
                              uint128 public totalValueInLp;
                              address public DEPRECATED_admin;
                              uint32 public numPendingDeposits; // number of deposits to the staking manager, which needs 'registerValidator'
                              address public DEPRECATED_bNftTreasury;
                              IWithdrawRequestNFT public withdrawRequestNFT;
                              BnftHolder[] public bnftHolders;
                              uint128 public maxValidatorsPerOwner;
                              uint128 public DEPRECATED_schedulingPeriodInSeconds;
                              HoldersUpdate public DEPRECATED_holdersUpdate;
                              mapping(address => bool) public admins;
                              mapping(SourceOfFunds => FundStatistics) public fundStatistics;
                              mapping(uint256 => bytes32) public depositDataRootForApprovalDeposits;
                              address public etherFiAdminContract;
                              bool public whitelistEnabled;
                              mapping(address => bool) public whitelisted;
                              mapping(address => BnftHoldersIndex) public bnftHoldersIndexes;
                              bool public restakeBnftDeposits;
                              uint128 public ethAmountLockedForWithdrawal;
                              bool public paused;
                              IAuctionManager public auctionManager;
                              ILiquifier public liquifier;
                              bool private isLpBnftHolder;
                              //--------------------------------------------------------------------------------------
                              //-------------------------------------  EVENTS  ---------------------------------------
                              //--------------------------------------------------------------------------------------
                              event Paused(address account);
                              event Unpaused(address account);
                              event Deposit(address indexed sender, uint256 amount, SourceOfFunds source, address referral);
                              event Withdraw(address indexed sender, address recipient, uint256 amount, SourceOfFunds source);
                              event UpdatedWhitelist(address userAddress, bool value);
                              event BnftHolderDeregistered(address user, uint256 index);
                              event BnftHolderRegistered(address user, uint256 index);
                              event UpdatedSchedulingPeriod(uint128 newPeriodInSeconds);
                              event ValidatorRegistered(uint256 indexed validatorId, bytes signature, bytes pubKey, bytes32 depositRoot);
                              event ValidatorApproved(uint256 indexed validatorId);
                              event ValidatorRegistrationCanceled(uint256 indexed validatorId);
                              event Rebase(uint256 totalEthLocked, uint256 totalEEthShares);
                              event WhitelistStatusUpdated(bool value);
                              error IncorrectCaller();
                              error InvalidAmount();
                              error InvalidParams();
                              error DataNotSet();
                              error InsufficientLiquidity();
                              error SendFail();
                              //--------------------------------------------------------------------------------------
                              //----------------------------  STATE-CHANGING FUNCTIONS  ------------------------------
                              //--------------------------------------------------------------------------------------
                              /// @custom:oz-upgrades-unsafe-allow constructor
                              constructor() {
                                  _disableInitializers();
                              }
                              receive() external payable {
                                  if (msg.value > type(uint128).max) revert InvalidAmount();
                                  totalValueOutOfLp -= uint128(msg.value);
                                  totalValueInLp += uint128(msg.value);
                              }
                              function initialize(address _eEthAddress, address _stakingManagerAddress, address _nodesManagerAddress, address _membershipManagerAddress, address _tNftAddress, address _etherFiAdminContract, address _withdrawRequestNFT) external initializer {
                                  if (_eEthAddress == address(0) || _stakingManagerAddress == address(0) || _nodesManagerAddress == address(0) || _membershipManagerAddress == address(0) || _tNftAddress == address(0)) revert DataNotSet();
                                  
                                  __Ownable_init();
                                  __UUPSUpgradeable_init();
                                  eETH = IeETH(_eEthAddress);
                                  stakingManager = IStakingManager(_stakingManagerAddress);
                                  nodesManager = IEtherFiNodesManager(_nodesManagerAddress);
                                  membershipManager = IMembershipManager(_membershipManagerAddress);
                                  tNft = ITNFT(_tNftAddress);
                                  paused = true;
                                  whitelistEnabled = true;
                                  restakeBnftDeposits = false;
                                  ethAmountLockedForWithdrawal = 0;
                                  maxValidatorsPerOwner = 30;
                                  etherFiAdminContract = _etherFiAdminContract;
                                  withdrawRequestNFT = IWithdrawRequestNFT(_withdrawRequestNFT);
                                  admins[_etherFiAdminContract] = true;
                                  fundStatistics[SourceOfFunds.EETH].numberOfValidators = 1;
                                  fundStatistics[SourceOfFunds.ETHER_FAN].numberOfValidators = 1;
                              }
                              function initializeOnUpgrade(address _auctionManager, address _liquifier) external onlyOwner { 
                                  require(_auctionManager != address(0) && _liquifier != address(0), "Invalid params");
                                  auctionManager = IAuctionManager(_auctionManager);
                                  liquifier = ILiquifier(_liquifier);
                              }
                              // Used by eETH staking flow
                              function deposit() external payable returns (uint256) {
                                  return deposit(address(0));
                              }
                              function deposit(address _referral) public payable whenNotPaused returns (uint256) {
                                  require(_isWhitelisted(msg.sender), "Invalid User");
                                  emit Deposit(msg.sender, msg.value, SourceOfFunds.EETH, _referral);
                                  return _deposit(msg.sender, msg.value, 0);
                              }
                              function depositToRecipient(address _recipient, uint256 _amount, address _referral) public whenNotPaused returns (uint256) {
                                  require(msg.sender == address(liquifier), "Incorrect Caller");
                                  emit Deposit(_recipient, _amount, SourceOfFunds.EETH, _referral);
                                  return _deposit(_recipient, 0, _amount);
                              }
                              // Used by ether.fan staking flow
                              function deposit(address _user, address _referral) external payable whenNotPaused returns (uint256) {
                                  require(msg.sender == address(membershipManager), "Incorrect Caller");
                                  require(_user == address(membershipManager) || _isWhitelisted(_user), "Invalid User");
                                  emit Deposit(msg.sender, msg.value, SourceOfFunds.ETHER_FAN, _referral);
                                  return _deposit(msg.sender, msg.value, 0);
                              }
                              /// @notice withdraw from pool
                              /// @dev Burns user balance from msg.senders account & Sends equal amount of ETH back to the recipient
                              /// @param _recipient the recipient who will receives the ETH
                              /// @param _amount the amount to withdraw from contract
                              /// it returns the amount of shares burned
                              function withdraw(address _recipient, uint256 _amount) external whenNotPaused returns (uint256) {
                                  uint256 share = sharesForWithdrawalAmount(_amount);
                                  require(msg.sender == address(withdrawRequestNFT) || msg.sender == address(membershipManager), "Incorrect Caller");
                                  if (totalValueInLp < _amount || (msg.sender == address(withdrawRequestNFT) && ethAmountLockedForWithdrawal < _amount) || eETH.balanceOf(msg.sender) < _amount) revert InsufficientLiquidity();
                                  if (_amount > type(uint128).max || _amount == 0 || share == 0) revert InvalidAmount();
                                  totalValueInLp -= uint128(_amount);
                                  if (msg.sender == address(withdrawRequestNFT)) {
                                      ethAmountLockedForWithdrawal -= uint128(_amount);
                                  }
                                  eETH.burnShares(msg.sender, share);
                                  (bool sent, ) = _recipient.call{value: _amount}("");
                                  if (!sent) revert SendFail();
                                  return share;
                              }
                              /// @notice request withdraw from pool and receive a WithdrawRequestNFT
                              /// @dev Transfers the amount of eETH from msg.senders account to the WithdrawRequestNFT contract & mints an NFT to the msg.sender
                              /// @param recipient address that will be issued the NFT
                              /// @param amount requested amount to withdraw from contract
                              /// @return uint256 requestId of the WithdrawRequestNFT
                              function requestWithdraw(address recipient, uint256 amount) public whenNotPaused returns (uint256) {
                                  uint256 share = sharesForAmount(amount);
                                  if (amount > type(uint96).max || amount == 0 || share == 0) revert InvalidAmount();
                                  // transfer shares to WithdrawRequestNFT contract from this contract
                                  eETH.transferFrom(msg.sender, address(withdrawRequestNFT), amount);
                                  uint256 requestId = withdrawRequestNFT.requestWithdraw(uint96(amount), uint96(share), recipient, 0);
                                 
                                  emit Withdraw(msg.sender, recipient, amount, SourceOfFunds.EETH);
                                  return requestId;
                              }
                              /// @notice request withdraw from pool with signed permit data and receive a WithdrawRequestNFT
                              /// @dev accepts PermitInput signed data to approve transfer of eETH (EIP-2612) so withdraw request can happen in 1 tx
                              /// @param _owner address that will be issued the NFT
                              /// @param _amount requested amount to withdraw from contract
                              /// @param _permit signed permit data to approve transfer of eETH
                              /// @return uint256 requestId of the WithdrawRequestNFT
                              function requestWithdrawWithPermit(address _owner, uint256 _amount, PermitInput calldata _permit)
                                  external
                                  whenNotPaused
                                  returns (uint256)
                              {
                                  eETH.permit(msg.sender, address(this), _permit.value, _permit.deadline, _permit.v, _permit.r, _permit.s);
                                  return requestWithdraw(_owner, _amount);
                              }
                              /// @notice request withdraw of some or all of the eETH backing a MembershipNFT and receive a WithdrawRequestNFT
                              /// @dev Transfers the amount of eETH from MembershipManager to the WithdrawRequestNFT contract & mints an NFT to the recipient
                              /// @param recipient address that will be issued the NFT
                              /// @param amount requested amount to withdraw from contract
                              /// @param fee the burn fee to be paid by the recipient when the withdrawal is claimed (WithdrawRequestNFT.claimWithdraw)
                              /// @return uint256 requestId of the WithdrawRequestNFT
                              function requestMembershipNFTWithdraw(address recipient, uint256 amount, uint256 fee) public whenNotPaused returns (uint256) {
                                  if (msg.sender != address(membershipManager)) revert IncorrectCaller();
                                  uint256 share = sharesForAmount(amount);
                                  if (amount > type(uint96).max || amount == 0 || share == 0) revert InvalidAmount();
                                  // transfer shares to WithdrawRequestNFT contract
                                  eETH.transferFrom(msg.sender, address(withdrawRequestNFT), amount);
                                  uint256 requestId = withdrawRequestNFT.requestWithdraw(uint96(amount), uint96(share), recipient, fee);
                                  emit Withdraw(msg.sender, recipient, amount, SourceOfFunds.ETHER_FAN);
                                  return requestId;
                              }
                              /// @notice Allows a BNFT player to deposit their 2 ETH and pair with 30 ETH from the LP
                              /// @dev This function has multiple dependencies that need to be followed before this function will succeed. 
                              /// @param _candidateBidIds validator IDs that have been matched with the BNFT holder on the FE
                              /// @param _numberOfValidators how many validators the user wants to spin up. This can be less than the candidateBidIds length. 
                              ///         we may have more Ids sent in than needed to spin up incase some ids fail.
                              /// @return Array of bids that were successfully processed.
                              function batchDepositAsBnftHolder(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external payable whenNotPaused returns (uint256[] memory) {
                                  require(!isLpBnftHolder, "IncorrectBnftMode");
                                  return _batchDeposit(_candidateBidIds, _numberOfValidators, 2 ether);
                              }
                              /// @notice BNFT players register validators they have deposited. This triggers a 1 ETH transaction to the beacon chain.
                              /// @dev This function can only be called by a BNFT player on IDs that have been deposited.  
                              /// @param _depositRoot This is the deposit root of the beacon chain. Can send in 0x00 to bypass this check in future
                              /// @param _validatorIds The ids of the validators to register
                              /// @param _registerValidatorDepositData As in the solo staking flow, the BNFT player must send in a deposit data object (see ILiquidityPool for struct data)
                              ///         to register the validators. However, the signature and deposit data root must be for a 1 ETH deposit
                              /// @param _depositDataRootApproval The deposit data roots for each validator for the 31 ETH transaction which will happen in the approval
                              ///         step. See the Staking Manager for details.
                              /// @param _signaturesForApprovalDeposit Much like the deposit data root. This is the signature for each validator for the 31 ETH 
                              ///         transaction which will happen in the approval step.
                              function batchRegisterAsBnftHolder(
                                  bytes32 _depositRoot,
                                  uint256[] calldata _validatorIds,
                                  IStakingManager.DepositData[] calldata _registerValidatorDepositData,
                                  bytes32[] calldata _depositDataRootApproval,
                                  bytes[] calldata _signaturesForApprovalDeposit
                              ) external whenNotPaused {
                                  require(!isLpBnftHolder, "IncorrectBnftMode");
                                  return _batchRegister(_depositRoot, _validatorIds, _registerValidatorDepositData, _depositDataRootApproval, _signaturesForApprovalDeposit, msg.sender);
                              }
                              function batchDepositWithLiquidityPoolAsBnftHolder(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external whenNotPaused returns (uint256[] memory) {
                                  require(isLpBnftHolder, "IncorrectBnftMode");
                                  return _batchDeposit(_candidateBidIds, _numberOfValidators, 0 ether);
                              }
                              function batchRegisterWithLiquidityPoolAsBnftHolder(
                                  bytes32 _depositRoot,
                                  uint256[] calldata _validatorIds,
                                  IStakingManager.DepositData[] calldata _registerValidatorDepositData,
                                  bytes32[] calldata _depositDataRootApproval,
                                  bytes[] calldata _signaturesForApprovalDeposit
                              ) external whenNotPaused {
                                  require(isLpBnftHolder, "IncorrectBnftMode");
                                  return _batchRegister(_depositRoot, _validatorIds, _registerValidatorDepositData, _depositDataRootApproval, _signaturesForApprovalDeposit, address(this));
                              }
                              function _batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators, uint256 _stakerDepositAmountPerValidator) internal returns (uint256[] memory) {
                                  uint32 index = bnftHoldersIndexes[msg.sender].index;
                                  require(bnftHoldersIndexes[msg.sender].registered && bnftHolders[index].holder == msg.sender, "Incorrect Caller");        
                                  require(msg.value == _numberOfValidators * _stakerDepositAmountPerValidator, "Not Enough Deposit");
                                  require(totalValueInLp + msg.value >= 32 ether * _numberOfValidators, "Not enough balance");
                                  require(_numberOfValidators <= maxValidatorsPerOwner, "Exceeded max validators per owner");
                              
                                  //Funds in the LP can come from our membership strategy or the eEth staking strategy. We select which source of funds will
                                  //be used for spinning up these deposited ids. See the function for more detail on how we do this.
                                  SourceOfFunds _source = allocateSourceOfFunds();
                                  fundStatistics[_source].numberOfValidators += uint32(_numberOfValidators);
                                  uint256 amountFromLp = (32 ether - _stakerDepositAmountPerValidator) * _numberOfValidators;
                                  if (amountFromLp > type(uint128).max) revert InvalidAmount();
                                  totalValueOutOfLp += uint128(amountFromLp);
                                  totalValueInLp -= uint128(amountFromLp);
                                  numPendingDeposits += uint32(_numberOfValidators);
                                  //We then call the Staking Manager contract which handles the rest of the logic
                                  uint256[] memory newValidators = stakingManager.batchDepositWithBidIds{value: 32 ether * _numberOfValidators}(_candidateBidIds, msg.sender, _source, restakeBnftDeposits);
                                  
                                  //Sometimes not all the validators get deposited successfully. We need to check if there were remaining IDs that were not successful
                                  //and refund the BNFT player their 2 ETH for each ID
                                  if (_numberOfValidators > newValidators.length) {
                                      uint256 returnAmount = _stakerDepositAmountPerValidator * (_numberOfValidators - newValidators.length);
                                      totalValueOutOfLp += uint128(returnAmount);
                                      totalValueInLp -= uint128(returnAmount);
                                      numPendingDeposits -= uint32(_numberOfValidators - newValidators.length);
                                      (bool sent, ) = msg.sender.call{value: returnAmount}("");
                                      if (!sent) revert SendFail();
                                  }
                                  
                                  return newValidators;
                              }
                              function _batchRegister(
                                  bytes32 _depositRoot,
                                  uint256[] calldata _validatorIds,
                                  IStakingManager.DepositData[] calldata _registerValidatorDepositData,
                                  bytes32[] calldata _depositDataRootApproval,
                                  bytes[] calldata _signaturesForApprovalDeposit,
                                  address _bnftRecipient
                              ) internal {
                                  require(_validatorIds.length == _registerValidatorDepositData.length && _validatorIds.length == _depositDataRootApproval.length && _validatorIds.length == _signaturesForApprovalDeposit.length, "lengths differ");
                                  stakingManager.batchRegisterValidators(_depositRoot, _validatorIds, _bnftRecipient, address(this), _registerValidatorDepositData, msg.sender);
                                  
                                  //For each validator, we need to store the deposit data root of the 31 ETH transaction so it is accessible in the approve function
                                  for(uint256 i; i < _validatorIds.length; i++) {
                                      depositDataRootForApprovalDeposits[_validatorIds[i]] = _depositDataRootApproval[i];
                                      emit ValidatorRegistered(_validatorIds[i], _signaturesForApprovalDeposit[i], _registerValidatorDepositData[i].publicKey, _depositDataRootApproval[i]);
                                  }
                              }
                              /// @notice Approves validators and triggers the 31 ETH transaction to the beacon chain (rest of the stake).
                              /// @dev This gets called by the Oracle and only when it has confirmed the withdraw credentials of the 1 ETH deposit in the registration
                              ///         phase match the withdraw credentials stored on the beacon chain. This prevents a front-running attack.
                              /// @param _validatorIds The IDs of the validators to be approved
                              /// @param _pubKey The pubKey for each validator being spun up.
                              /// @param _signature The signatures for each validator for the 31 ETH transaction that were emitted in the register phase
                              function batchApproveRegistration(
                                  uint256[] memory _validatorIds, 
                                  bytes[] calldata _pubKey,
                                  bytes[] calldata _signature
                              ) external onlyAdmin whenNotPaused {
                                  require(_validatorIds.length == _pubKey.length && _validatorIds.length == _signature.length, "lengths differ");
                                  //Fetches the deposit data root of each validator and uses it in the approval call to the Staking Manager
                                  bytes32[] memory depositDataRootApproval = new bytes32[](_validatorIds.length);
                                  for(uint256 i; i < _validatorIds.length; i++) {
                                      depositDataRootApproval[i] = depositDataRootForApprovalDeposits[_validatorIds[i]];
                                      delete depositDataRootForApprovalDeposits[_validatorIds[i]];        
                                      emit ValidatorApproved(_validatorIds[i]);
                                  }
                                  numPendingDeposits -= uint32(_validatorIds.length);
                                  stakingManager.batchApproveRegistration(_validatorIds, _pubKey, _signature, depositDataRootApproval);
                              }
                              /// @notice Cancels a BNFT players deposits (whether validator is registered or deposited. Just not live on beacon chain)
                              /// @dev This is called only in the BNFT player flow
                              /// @param _validatorIds The IDs to be cancelled
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external whenNotPaused {
                                  _batchCancelDeposit(_validatorIds, msg.sender);
                              }
                              function batchCancelDepositByAdmin(uint256[] calldata _validatorIds, address _bnftStaker) external whenNotPaused onlyAdmin {
                                  _batchCancelDeposit(_validatorIds, _bnftStaker);
                              }
                              function _batchCancelDeposit(uint256[] calldata _validatorIds, address _bnftStaker) internal {
                                  uint256 returnAmount = 0;
                                  if (!isLpBnftHolder) {
                                      //Due to the way we handle our totalValueOutOfLP calculations, we need to update the data before we call the Staking Manager
                                      //For this reason, we first need to check which phase each validator is in. Because if a bNFT cancels a validator that has 
                                      //already been registered, they only receive 1 ETH back because the other 1 ETH is in the beacon chain. Those funds will be lost
                                      for (uint256 i = 0; i < _validatorIds.length; i++) {
                                          if(nodesManager.phase(_validatorIds[i]) == IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL) {
                                              returnAmount += 1 ether;
                                              emit ValidatorRegistrationCanceled(_validatorIds[i]);
                                          } else {
                                              returnAmount += 2 ether;
                                          }
                                      }
                                  }
                                  totalValueOutOfLp += uint128(returnAmount);
                                  numPendingDeposits -= uint32(_validatorIds.length);
                                  stakingManager.batchCancelDepositAsBnftHolder(_validatorIds, _bnftStaker);
                                  totalValueInLp -= uint128(returnAmount);
                                  (bool sent, ) = address(_bnftStaker).call{value: returnAmount}("");
                                  if (!sent) revert SendFail();
                              }
                              /// @notice The admin can register an address to become a BNFT holder. This adds them to the bnftHolders array
                              /// @dev BNFT players reach out to Etherfi externally and then Etherfi will register them
                              /// @param _user The address of the BNFT player to register
                              function registerAsBnftHolder(address _user) public onlyAdmin {      
                                  require(!bnftHoldersIndexes[_user].registered, "Already registered");  
                                  //We hold the users address and latest deposit timestamp in an object to make sure a user doesnt deposit twice in one scheduling period
                                  BnftHolder memory bnftHolder = BnftHolder({
                                      holder: _user,
                                      timestamp: 0
                                  });
                                  uint256 index = bnftHolders.length;
                                  bnftHolders.push(bnftHolder);
                                  bnftHoldersIndexes[_user] = BnftHoldersIndex({
                                      registered: true,
                                      index: uint32(index)
                                  });
                                  emit BnftHolderRegistered(_user, index);
                              }
                              /// @notice Removes a BNFT player from the bnftHolders array and means they are no longer eligible to be selected
                              /// @dev We allow either the user themselves or admins to remove BNFT players
                              /// @param _bNftHolder Address of the BNFT player to remove
                              function deRegisterBnftHolder(address _bNftHolder) external {
                                  require(bnftHoldersIndexes[_bNftHolder].registered, "Not registered");
                                  uint256 index = bnftHoldersIndexes[_bNftHolder].index;
                                  require(admins[msg.sender] || msg.sender == bnftHolders[index].holder, "Incorrect Caller");
                                  
                                  uint256 endIndex = bnftHolders.length - 1;
                                  address endUser = bnftHolders[endIndex].holder;
                                  //Swap the end BNFT player with the BNFT player being removed
                                  bnftHolders[index] = bnftHolders[endIndex];
                                  bnftHoldersIndexes[endUser].index = uint32(index);
                                  
                                  //Pop the last user as we have swapped them around
                                  bnftHolders.pop();
                                  delete bnftHoldersIndexes[_bNftHolder];
                                  emit BnftHolderDeregistered(_bNftHolder, index);
                              }
                              /// @notice Send the exit requests as the T-NFT holder
                              function sendExitRequests(uint256[] calldata _validatorIds) external onlyAdmin {
                                  for (uint256 i = 0; i < _validatorIds.length; i++) {
                                      uint256 validatorId = _validatorIds[i];
                                      nodesManager.sendExitRequest(validatorId);
                                  }
                              }
                              /// @notice Rebase by ether.fi
                              function rebase(int128 _accruedRewards) public {
                                  if (msg.sender != address(membershipManager)) revert IncorrectCaller();
                                  totalValueOutOfLp = uint128(int128(totalValueOutOfLp) + _accruedRewards);
                                  emit Rebase(getTotalPooledEther(), eETH.totalShares());
                              }
                              /// @notice Whether or not nodes created via bNFT deposits should be restaked
                              function setRestakeBnftDeposits(bool _restake) external onlyAdmin {
                                  restakeBnftDeposits = _restake;
                              }
                              /// @notice Updates the address of the admin
                              /// @param _address the new address to set as admin
                              function updateAdmin(address _address, bool _isAdmin) external onlyOwner {
                                  admins[_address] = _isAdmin;
                              }
                              function pauseContract() external onlyAdmin {
                                  paused = true;
                                  emit Paused(_msgSender());
                              }
                              function unPauseContract() external onlyAdmin {
                                  paused = false;
                                  emit Unpaused(_msgSender());
                              }
                              /// @notice Sets the max number of validators a BNFT can spin up in a batch
                              /// @param _newSize the number to set it to
                              function setNumValidatorsToSpinUpInBatch(uint128 _newSize) external onlyAdmin {
                                  maxValidatorsPerOwner = _newSize;
                              }
                              /// @notice Sets our targeted ratio of validators for each of the fund sources
                              /// @dev Fund sources are different ways where the LP receives funds. Currently, there is just through EETH staking and ETHER_FAN (membership manager)
                              /// @param _eEthWeight The target weight for eEth
                              /// @param _etherFanWeight The target weight for EtherFan
                              function setStakingTargetWeights(uint32 _eEthWeight, uint32 _etherFanWeight) external onlyAdmin {
                                  if (_eEthWeight + _etherFanWeight != 100) revert InvalidParams();
                                  fundStatistics[SourceOfFunds.EETH].targetWeight = _eEthWeight;
                                  fundStatistics[SourceOfFunds.ETHER_FAN].targetWeight = _etherFanWeight;
                              }
                              function updateWhitelistedAddresses(address[] calldata _users, bool _value) external onlyAdmin {
                                  for (uint256 i = 0; i < _users.length; i++) {
                                      whitelisted[_users[i]] = _value;
                                      emit UpdatedWhitelist(_users[i], _value);
                                  }
                              }
                              function updateWhitelistStatus(bool _value) external onlyAdmin {
                                  whitelistEnabled = _value;
                                  emit WhitelistStatusUpdated(_value);
                              }
                              function updateBnftMode(bool _isLpBnftHolder) external onlyAdmin {
                                  // Never toggle it in the process of deposit-regiration
                                  isLpBnftHolder = _isLpBnftHolder;
                              }
                              /// @notice Decreases the number of validators for a certain source of fund
                              /// @dev When a user deposits, we increment the number of validators in the allocated source object. However, when a BNFT player cancels 
                              ///         their deposits, we need to decrease this again.
                              /// @param numberOfEethValidators How many eEth validators to decrease
                              /// @param numberOfEtherFanValidators How many etherFan validators to decrease
                              function decreaseSourceOfFundsValidators(uint32 numberOfEethValidators, uint32 numberOfEtherFanValidators) external {
                                  if (msg.sender != address(stakingManager)) revert IncorrectCaller();
                                  fundStatistics[SourceOfFunds.EETH].numberOfValidators -= numberOfEethValidators;
                                  fundStatistics[SourceOfFunds.ETHER_FAN].numberOfValidators -= numberOfEtherFanValidators;
                              }
                              function addEthAmountLockedForWithdrawal(uint128 _amount) external {
                                  if (msg.sender != address(etherFiAdminContract)) revert IncorrectCaller();
                                  ethAmountLockedForWithdrawal += _amount;
                              }
                              //--------------------------------------------------------------------------------------
                              //------------------------------  INTERNAL FUNCTIONS  ----------------------------------
                              //--------------------------------------------------------------------------------------
                              function _deposit(address _recipient, uint256 _amountInLp, uint256 _amountOutOfLp) internal returns (uint256) {
                                  totalValueInLp += uint128(_amountInLp);
                                  totalValueOutOfLp += uint128(_amountOutOfLp);
                                  uint256 amount = _amountInLp + _amountOutOfLp;
                                  uint256 share = _sharesForDepositAmount(amount);
                                  if (amount > type(uint128).max || amount == 0 || share == 0) revert InvalidAmount();
                                  eETH.mintShares(_recipient, share);
                                  return share;
                              }
                              function _isWhitelisted(address _user) internal view returns (bool) {
                                  return (!whitelistEnabled || whitelisted[_user]);
                              }
                              function _sharesForDepositAmount(uint256 _depositAmount) internal view returns (uint256) {
                                  uint256 totalPooledEther = getTotalPooledEther() - _depositAmount;
                                  if (totalPooledEther == 0) {
                                      return _depositAmount;
                                  }
                                  return (_depositAmount * eETH.totalShares()) / totalPooledEther;
                              }
                              function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
                              //--------------------------------------------------------------------------------------
                              //------------------------------------  GETTERS  ---------------------------------------
                              //--------------------------------------------------------------------------------------
                              /// @notice Selects a source of funds to be used for the deposits
                              /// @dev The LP has two ways of accumulating funds, through eEth staking and through the ether fan page (membership manager).
                              ///         We want to manipulate which funds we use per deposit. Example, if someone is making 2 deposits, we want to select where the 60 ETH
                              ///         should come from. The funds will all be held in the LP but we are storing how many validators are spun up per source on the contract.
                              ///         We simply check which of the sources is below their target allocation and allocate the deposits to it.
                              /// @return The chosen source of funds (EETH or ETHER_FAN)
                              function allocateSourceOfFunds() public view returns (SourceOfFunds) {
                                  uint256 validatorRatio = (fundStatistics[SourceOfFunds.EETH].numberOfValidators * 10_000) / fundStatistics[SourceOfFunds.ETHER_FAN].numberOfValidators;
                                  uint256 weightRatio = (fundStatistics[SourceOfFunds.EETH].targetWeight * 10_000) / fundStatistics[SourceOfFunds.ETHER_FAN].targetWeight;
                                  return validatorRatio > weightRatio ? SourceOfFunds.ETHER_FAN : SourceOfFunds.EETH;
                              }
                              function getTotalEtherClaimOf(address _user) external view returns (uint256) {
                                  uint256 staked;
                                  uint256 totalShares = eETH.totalShares();
                                  if (totalShares > 0) {
                                      staked = (getTotalPooledEther() * eETH.shares(_user)) / totalShares;
                                  }
                                  return staked;
                              }
                              function getTotalPooledEther() public view returns (uint256) {
                                  return totalValueOutOfLp + totalValueInLp;
                              }
                              function sharesForAmount(uint256 _amount) public view returns (uint256) {
                                  uint256 totalPooledEther = getTotalPooledEther();
                                  if (totalPooledEther == 0) {
                                      return 0;
                                  }
                                  return (_amount * eETH.totalShares()) / totalPooledEther;
                              }
                              /// @dev withdrawal rounding errors favor the protocol by rounding up
                              function sharesForWithdrawalAmount(uint256 _amount) public view returns (uint256) {
                                  uint256 totalPooledEther = getTotalPooledEther();
                                  if (totalPooledEther == 0) {
                                      return 0;
                                  }
                                  // ceiling division so rounding errors favor the protocol
                                  uint256 numerator = _amount * eETH.totalShares();
                                  return (numerator + totalPooledEther - 1) / totalPooledEther;
                              }
                              function amountForShare(uint256 _share) public view returns (uint256) {
                                  uint256 totalShares = eETH.totalShares();
                                  if (totalShares == 0) {
                                      return 0;
                                  }
                                  return (_share * getTotalPooledEther()) / totalShares;
                              }
                              function getImplementation() external view returns (address) {return _getImplementation();}
                              function _requireAdmin() internal view virtual {
                                  require(admins[msg.sender], "Not admin");
                              }
                              function _requireNotPaused() internal view virtual {
                                  require(!paused, "Pausable: paused");
                              }
                              //--------------------------------------------------------------------------------------
                              //-----------------------------------  MODIFIERS  --------------------------------------
                              //--------------------------------------------------------------------------------------
                              modifier onlyAdmin() {
                                  _requireAdmin();
                                  _;
                              }
                              modifier whenNotPaused() {
                                  _requireNotPaused();
                                  _;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC20 standard as defined in the EIP.
                           */
                          interface IERC20 {
                              /**
                               * @dev Emitted when `value` tokens are moved from one account (`from`) to
                               * another (`to`).
                               *
                               * Note that `value` may be zero.
                               */
                              event Transfer(address indexed from, address indexed to, uint256 value);
                              /**
                               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                               * a call to {approve}. `value` is the new allowance.
                               */
                              event Approval(address indexed owner, address indexed spender, uint256 value);
                              /**
                               * @dev Returns the amount of tokens in existence.
                               */
                              function totalSupply() external view returns (uint256);
                              /**
                               * @dev Returns the amount of tokens owned by `account`.
                               */
                              function balanceOf(address account) external view returns (uint256);
                              /**
                               * @dev Moves `amount` tokens from the caller's account to `to`.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transfer(address to, uint256 amount) external returns (bool);
                              /**
                               * @dev Returns the remaining number of tokens that `spender` will be
                               * allowed to spend on behalf of `owner` through {transferFrom}. This is
                               * zero by default.
                               *
                               * This value changes when {approve} or {transferFrom} are called.
                               */
                              function allowance(address owner, address spender) external view returns (uint256);
                              /**
                               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * IMPORTANT: Beware that changing an allowance with this method brings the risk
                               * that someone may use both the old and the new allowance by unfortunate
                               * transaction ordering. One possible solution to mitigate this race
                               * condition is to first reduce the spender's allowance to 0 and set the
                               * desired value afterwards:
                               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                               *
                               * Emits an {Approval} event.
                               */
                              function approve(address spender, uint256 amount) external returns (bool);
                              /**
                               * @dev Moves `amount` tokens from `from` to `to` using the
                               * allowance mechanism. `amount` is then deducted from the caller's
                               * allowance.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 amount
                              ) external returns (bool);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                           *
                           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                           * need to send a transaction, and thus is not required to hold Ether at all.
                           */
                          interface IERC20Permit {
                              /**
                               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                               * given ``owner``'s signed approval.
                               *
                               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                               * ordering also apply here.
                               *
                               * Emits an {Approval} event.
                               *
                               * Requirements:
                               *
                               * - `spender` cannot be the zero address.
                               * - `deadline` must be a timestamp in the future.
                               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                               * over the EIP712-formatted function arguments.
                               * - the signature must use ``owner``'s current nonce (see {nonces}).
                               *
                               * For more information on the signature format, see the
                               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                               * section].
                               */
                              function permit(
                                  address owner,
                                  address spender,
                                  uint256 value,
                                  uint256 deadline,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              ) external;
                              /**
                               * @dev Returns the current nonce for `owner`. This value must be
                               * included whenever a signature is generated for {permit}.
                               *
                               * Every successful call to {permit} increases ``owner``'s nonce by one. This
                               * prevents a signature from being used multiple times.
                               */
                              function nonces(address owner) external view returns (uint256);
                              /**
                               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                               */
                              // solhint-disable-next-line func-name-mixedcase
                              function DOMAIN_SEPARATOR() external view returns (bytes32);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC20 standard as defined in the EIP.
                           */
                          interface IERC20Upgradeable {
                              /**
                               * @dev Emitted when `value` tokens are moved from one account (`from`) to
                               * another (`to`).
                               *
                               * Note that `value` may be zero.
                               */
                              event Transfer(address indexed from, address indexed to, uint256 value);
                              /**
                               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                               * a call to {approve}. `value` is the new allowance.
                               */
                              event Approval(address indexed owner, address indexed spender, uint256 value);
                              /**
                               * @dev Returns the amount of tokens in existence.
                               */
                              function totalSupply() external view returns (uint256);
                              /**
                               * @dev Returns the amount of tokens owned by `account`.
                               */
                              function balanceOf(address account) external view returns (uint256);
                              /**
                               * @dev Moves `amount` tokens from the caller's account to `to`.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transfer(address to, uint256 amount) external returns (bool);
                              /**
                               * @dev Returns the remaining number of tokens that `spender` will be
                               * allowed to spend on behalf of `owner` through {transferFrom}. This is
                               * zero by default.
                               *
                               * This value changes when {approve} or {transferFrom} are called.
                               */
                              function allowance(address owner, address spender) external view returns (uint256);
                              /**
                               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * IMPORTANT: Beware that changing an allowance with this method brings the risk
                               * that someone may use both the old and the new allowance by unfortunate
                               * transaction ordering. One possible solution to mitigate this race
                               * condition is to first reduce the spender's allowance to 0 and set the
                               * desired value afterwards:
                               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                               *
                               * Emits an {Approval} event.
                               */
                              function approve(address spender, uint256 amount) external returns (bool);
                              /**
                               * @dev Moves `amount` tokens from `from` to `to` using the
                               * allowance mechanism. `amount` is then deducted from the caller's
                               * allowance.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 amount
                              ) external returns (bool);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @title ERC721 token receiver interface
                           * @dev Interface for any contract that wants to support safeTransfers
                           * from ERC721 asset contracts.
                           */
                          interface IERC721ReceiverUpgradeable {
                              /**
                               * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                               * by `operator` from `from`, this function is called.
                               *
                               * It must return its Solidity selector to confirm the token transfer.
                               * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                               *
                               * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                               */
                              function onERC721Received(
                                  address operator,
                                  address from,
                                  uint256 tokenId,
                                  bytes calldata data
                              ) external returns (bytes4);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
                          pragma solidity ^0.8.2;
                          import "../../utils/AddressUpgradeable.sol";
                          /**
                           * @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]
                           * ```
                           * 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 Indicates that the contract has been initialized.
                               * @custom:oz-retyped-from bool
                               */
                              uint8 private _initialized;
                              /**
                               * @dev Indicates that the contract is in the process of being initialized.
                               */
                              bool private _initializing;
                              /**
                               * @dev Triggered when the contract has been initialized or reinitialized.
                               */
                              event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
                               * constructor.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier initializer() {
                                  bool isTopLevelCall = !_initializing;
                                  require(
                                      (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                      "Initializable: contract is already initialized"
                                  );
                                  _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 255 will prevent any future reinitialization.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier reinitializer(uint8 version) {
                                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                  _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() {
                                  require(_initializing, "Initializable: contract is not initializing");
                                  _;
                              }
                              /**
                               * @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 {
                                  require(!_initializing, "Initializable: contract is initializing");
                                  if (_initialized < type(uint8).max) {
                                      _initialized = type(uint8).max;
                                      emit Initialized(type(uint8).max);
                                  }
                              }
                              /**
                               * @dev Returns the highest version that has been initialized. See {reinitializer}.
                               */
                              function _getInitializedVersion() internal view returns (uint8) {
                                  return _initialized;
                              }
                              /**
                               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                               */
                              function _isInitializing() internal view returns (bool) {
                                  return _initializing;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
                          pragma solidity ^0.8.0;
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
                          import "./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.
                           *
                           * _Available since v4.1._
                           */
                          abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
                              function __UUPSUpgradeable_init() internal onlyInitializing {
                              }
                              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                              }
                              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
                              address private immutable __self = address(this);
                              /**
                               * @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() {
                                  require(address(this) != __self, "Function must be called through delegatecall");
                                  require(_getImplementation() == __self, "Function must be called through active proxy");
                                  _;
                              }
                              /**
                               * @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() {
                                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                                  _;
                              }
                              /**
                               * @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 override notDelegated returns (bytes32) {
                                  return _IMPLEMENTATION_SLOT;
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeTo(address newImplementation) external virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                               * encoded in `data`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, data, true);
                              }
                              /**
                               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                               * {upgradeTo} and {upgradeToAndCall}.
                               *
                               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                               *
                               * ```solidity
                               * function _authorizeUpgrade(address) internal override onlyOwner {}
                               * ```
                               */
                              function _authorizeUpgrade(address newImplementation) 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: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                          pragma solidity ^0.8.0;
                          import "../utils/ContextUpgradeable.sol";
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Contract module which provides a basic access control mechanism, where
                           * there is an account (an owner) that can be granted exclusive access to
                           * specific functions.
                           *
                           * By default, the owner account will be the one that deploys the contract. This
                           * can later be changed with {transferOwnership}.
                           *
                           * This module is used through inheritance. It will make available the modifier
                           * `onlyOwner`, which can be applied to your functions to restrict their use to
                           * the owner.
                           */
                          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                              address private _owner;
                              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                              /**
                               * @dev Initializes the contract setting the deployer as the initial owner.
                               */
                              function __Ownable_init() internal onlyInitializing {
                                  __Ownable_init_unchained();
                              }
                              function __Ownable_init_unchained() internal onlyInitializing {
                                  _transferOwnership(_msgSender());
                              }
                              /**
                               * @dev Throws if called by any account other than the owner.
                               */
                              modifier onlyOwner() {
                                  _checkOwner();
                                  _;
                              }
                              /**
                               * @dev Returns the address of the current owner.
                               */
                              function owner() public view virtual returns (address) {
                                  return _owner;
                              }
                              /**
                               * @dev Throws if the sender is not the owner.
                               */
                              function _checkOwner() internal view virtual {
                                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
                              }
                              /**
                               * @dev Leaves the contract without owner. It will not be possible to call
                               * `onlyOwner` functions anymore. Can only be called by the current owner.
                               *
                               * NOTE: Renouncing ownership will leave the contract without an owner,
                               * thereby removing any functionality that is only available to the owner.
                               */
                              function renounceOwnership() public virtual onlyOwner {
                                  _transferOwnership(address(0));
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Can only be called by the current owner.
                               */
                              function transferOwnership(address newOwner) public virtual onlyOwner {
                                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                                  _transferOwnership(newOwner);
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Internal function without access restriction.
                               */
                              function _transferOwnership(address newOwner) internal virtual {
                                  address oldOwner = _owner;
                                  _owner = newOwner;
                                  emit OwnershipTransferred(oldOwner, newOwner);
                              }
                              /**
                               * @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[49] private __gap;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IRegulationsManager {
                              function initialize() external;
                              function confirmEligibility(bytes32 hash) external;
                              function removeFromWhitelist(address _user) external;
                              function initializeNewWhitelist(bytes32 _newVersionHash) external;
                              function isEligible(uint32 _whitelistVersion, address _user) external view returns (bool);
                              function whitelistVersion() external view returns (uint32);
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./ILiquidityPool.sol";
                          interface IStakingManager {
                              struct DepositData {
                                  bytes publicKey;
                                  bytes signature;
                                  bytes32 depositDataRoot;
                                  string ipfsHashForEncryptedValidatorKey;
                              }
                              struct StakerInfo {
                                  address staker;
                                  ILiquidityPool.SourceOfFunds sourceOfFund;
                              }
                              function bidIdToStaker(uint256 id) external view returns (address);
                              function getEtherFiNodeBeacon() external view returns (address);
                              function initialize(address _auctionAddress, address _depositContractAddress) external;
                              function setEtherFiNodesManagerAddress(address _managerAddress) external;
                              function setLiquidityPoolAddress(address _liquidityPoolAddress) external;
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, address _staker, ILiquidityPool.SourceOfFunds source, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, DepositData[] calldata _depositData) external;
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, address _bNftRecipient, address _tNftRecipient, DepositData[] calldata _depositData, address _user) external;
                              function batchApproveRegistration(uint256[] memory _validatorId, bytes[] calldata _pubKey, bytes[] calldata _signature, bytes32[] calldata _depositDataRootApproval) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function batchCancelDepositAsBnftHolder(uint256[] calldata _validatorIds, address _caller) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./IEtherFiNode.sol";
                          import "@eigenlayer/IEigenPodManager.sol";
                          import "@eigenlayer/IDelayedWithdrawalRouter.sol";
                          interface IEtherFiNodesManager {
                              struct RewardsSplit {
                                  uint64 treasury;
                                  uint64 nodeOperator;
                                  uint64 tnft;
                                  uint64 bnft;
                              }
                              enum ValidatorRecipientType {
                                  TNFTHOLDER,
                                  BNFTHOLDER,
                                  TREASURY,
                                  OPERATOR
                              }
                              // VIEW functions
                              function calculateTVL(uint256 _validatorId, uint256 _beaconBalance) external view returns (uint256, uint256, uint256, uint256);
                              function calculateWithdrawableTVL(uint256 _validatorId, uint256 _beaconBalance) external view returns (uint256, uint256, uint256, uint256);
                              function delayedWithdrawalRouter() external view returns (IDelayedWithdrawalRouter);
                              function eigenPodManager() external view returns (IEigenPodManager);
                              function generateWithdrawalCredentials(address _address) external view returns (bytes memory);
                              function getFullWithdrawalPayouts(uint256 _validatorId) external view returns (uint256, uint256, uint256, uint256);
                              function getNonExitPenalty(uint256 _validatorId) external view returns (uint256);
                              function getRewardsPayouts(uint256 _validatorId, uint256 _beaconBalance) external view returns (uint256, uint256, uint256, uint256);
                              function getWithdrawalCredentials(uint256 _validatorId) external view returns (bytes memory);
                              function ipfsHashForEncryptedValidatorKey(uint256 _validatorId) external view returns (string memory);
                              function nonExitPenaltyDailyRate() external view returns (uint64);
                              function nonExitPenaltyPrincipal() external view returns (uint64);
                              function numberOfValidators() external view returns (uint64);
                              function phase(uint256 _validatorId) external view returns (IEtherFiNode.VALIDATOR_PHASE phase);
                              // Non-VIEW functions
                              function initialize(
                                  address _treasuryContract,
                                  address _auctionContract,
                                  address _stakingManagerContract,
                                  address _tnftContract,
                                  address _bnftContract
                              ) external;
                              function batchQueueRestakedWithdrawal(uint256[] calldata _validatorIds) external;
                              function batchSendExitRequest(uint256[] calldata _validatorIds) external;
                              function fullWithdrawBatch(uint256[] calldata _validatorIds) external;
                              function fullWithdraw(uint256 _validatorId) external;
                              function getUnusedWithdrawalSafesLength() external view returns (uint256);
                              function incrementNumberOfValidators(uint64 _count) external;
                              function markBeingSlashed(uint256[] calldata _validatorIds) external;
                              function partialWithdrawBatch(uint256[] calldata _validatorIds) external;
                              function partialWithdraw(uint256 _validatorId) external;
                              function processNodeExit(uint256[] calldata _validatorIds, uint32[] calldata _exitTimestamp) external;
                              function registerEtherFiNode(uint256 _validatorId, bool _enableRestaking) external returns (address);
                              function sendExitRequest(uint256 _validatorId) external;
                              function setEtherFiNodeIpfsHashForEncryptedValidatorKey(uint256 _validatorId, string calldata _ipfs) external;
                              function setEtherFiNodePhase(uint256 _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase) external;
                              function setNonExitPenalty(uint64 _nonExitPenaltyDailyRate, uint64 _nonExitPenaltyPrincipal) external;
                              function setStakingRewardsSplit(uint64 _treasury, uint64 _nodeOperator, uint64 _tnft, uint64 _bnf) external;
                              function unregisterEtherFiNode(uint256 _validatorId) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function admins(address _address) external view returns (bool);
                              function pauseContract() external;
                              function unPauseContract() external;
                              function treasuryContract() external view returns (address);
                              function maxEigenlayerWithdrawals() external view returns (uint8);
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IeETH {
                              function name() external pure returns (string memory);
                              function symbol() external pure returns (string memory);
                              function decimals() external pure returns (uint8);
                              function totalShares() external view returns (uint256);
                              function shares(address _user) external view returns (uint256);
                              function balanceOf(address _user) external view returns (uint256);
                              function initialize(address _liquidityPool) external;
                              function mintShares(address _user, uint256 _share) external;
                              function burnShares(address _user, uint256 _share) external;
                              function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool);
                              function transfer(address _recipient, uint256 _amount) external returns (bool);
                              function approve(address _spender, uint256 _amount) external returns (bool);
                              function increaseAllowance(address _spender, uint256 _increaseAmount) external returns (bool);
                              function decreaseAllowance(address _spender, uint256 _decreaseAmount) external returns (bool);
                              function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IMembershipManager {
                              struct TokenDeposit {
                                  uint128 amounts;
                                  uint128 shares;
                              }
                              struct TokenData {
                                  uint96 vaultShare;
                                  uint40 baseLoyaltyPoints;
                                  uint40 baseTierPoints;
                                  uint32 prevPointsAccrualTimestamp;
                                  uint32 prevTopUpTimestamp;
                                  uint8  tier;
                                  uint8  version;
                              }
                              // Used for V1
                              struct TierVault {
                                  uint128 totalPooledEEthShares; // total share of eEth in the tier vault
                                  uint128 totalVaultShares; // total share of the tier vault
                              }
                              // Used for V0
                              struct TierDeposit {
                                  uint128 amounts; // total pooled eth amount
                                  uint128 shares; // total pooled eEth shares
                              }
                              struct TierData {
                                  uint96 rewardsGlobalIndex;
                                  uint40 requiredTierPoints;
                                  uint24 weight;
                                  uint96  __gap;
                              }
                              // State-changing functions
                              function wrapEthForEap(uint256 _amount, uint256 _amountForPoint, uint32  _eapDepositBlockNumber, uint256 _snapshotEthAmount, uint256 _points, bytes32[] calldata _merkleProof) external payable returns (uint256);
                              function wrapEth(uint256 _amount, uint256 _amountForPoint) external payable returns (uint256);
                              function wrapEth(uint256 _amount, uint256 _amountForPoint, address _referral) external payable returns (uint256);
                              function topUpDepositWithEth(uint256 _tokenId, uint128 _amount, uint128 _amountForPoints) external payable;
                              function requestWithdraw(uint256 _tokenId, uint256 _amount) external returns (uint256);
                              function requestWithdrawAndBurn(uint256 _tokenId) external returns (uint256);
                              function claim(uint256 _tokenId) external;
                              function migrateFromV0ToV1(uint256 _tokenId) external;
                              // Getter functions
                              function tokenDeposits(uint256) external view returns (uint128, uint128);
                              function tokenData(uint256) external view returns (uint96, uint40, uint40, uint32, uint32, uint8, uint8);
                              function tierDeposits(uint256) external view returns (uint128, uint128);
                              function tierData(uint256) external view returns (uint96, uint40, uint24, uint96);
                              function rewardsGlobalIndex(uint8 _tier) external view returns (uint256);
                              function allTimeHighDepositAmount(uint256 _tokenId) external view returns (uint256);
                              function tierForPoints(uint40 _tierPoints) external view returns (uint8);
                              function canTopUp(uint256 _tokenId, uint256 _totalAmount, uint128 _amount, uint128 _amountForPoints) external view returns (bool);
                              function pointsBoostFactor() external view returns (uint16);
                              function pointsGrowthRate() external view returns (uint16);
                              function maxDepositTopUpPercent() external view returns (uint8);
                              function numberOfTiers() external view returns (uint8);
                              function getImplementation() external view returns (address);
                              function minimumAmountForMint() external view returns (uint256);
                              function eEthShareForVaultShare(uint8 _tier, uint256 _vaultShare) external view returns (uint256);
                              function vaultShareForEEthShare(uint8 _tier, uint256 _eEthShare) external view returns (uint256);
                              function ethAmountForVaultShare(uint8 _tier, uint256 _vaultShare) external view returns (uint256);
                              function vaultShareForEthAmount(uint8 _tier, uint256 _ethAmount) external view returns (uint256);
                              // only Owner
                              function initializeOnUpgrade(address _etherFiAdminAddress, uint256 _fanBoostThresholdAmount, uint16 _burnFeeWaiverPeriodInDays) external;
                              function setWithdrawalLockBlocks(uint32 _blocks) external;
                              function updatePointsParams(uint16 _newPointsBoostFactor, uint16 _newPointsGrowthRate) external;
                              function rebase(int128 _accruedRewards) external;
                              function addNewTier(uint40 _requiredTierPoints, uint24 _weight) external;
                              function updateTier(uint8 _tier, uint40 _requiredTierPoints, uint24 _weight) external;
                              function setPoints(uint256 _tokenId, uint40 _loyaltyPoints, uint40 _tierPoints) external;
                              function setDepositAmountParams(uint56 _minDepositGwei, uint8 _maxDepositTopUpPercent) external;
                              function setTopUpCooltimePeriod(uint32 _newWaitTime) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin-upgradeable/contracts/token/ERC721/IERC721Upgradeable.sol";
                          interface ITNFT is IERC721Upgradeable {
                              function burnFromWithdrawal(uint256 _validatorId) external;
                              function initialize() external;
                              function initializeOnUpgrade(address _etherFiNodesManagerAddress) external;
                              function mint(address _receiver, uint256 _validatorId) external;
                              function burnFromCancelBNftFlow(uint256 _validatorId) external;
                              function upgradeTo(address _newImplementation) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IWithdrawRequestNFT {
                              struct WithdrawRequest {
                                  uint96  amountOfEEth;
                                  uint96  shareOfEEth;
                                  bool    isValid;
                                  uint32  feeGwei;
                              }
                              function initialize(address _liquidityPoolAddress, address _eEthAddress, address _membershipManager) external;
                              function requestWithdraw(uint96 amountOfEEth, uint96 shareOfEEth, address requester, uint256 fee) external payable returns (uint256);
                              function claimWithdraw(uint256 requestId) external;
                              function getRequest(uint256 requestId) external view returns (WithdrawRequest memory);
                              function isFinalized(uint256 requestId) external view returns (bool);
                              function invalidateRequest(uint256 requestId) external;
                              function finalizeRequests(uint256 upperBound) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./IStakingManager.sol";
                          interface ILiquidityPool {
                              struct PermitInput {
                                  uint256 value;
                                  uint256 deadline;
                                  uint8 v;
                                  bytes32 r;
                                  bytes32 s;
                              } 
                              enum SourceOfFunds {
                                  UNDEFINED,
                                  EETH,
                                  ETHER_FAN,
                                  DELEGATED_STAKING
                              }
                              struct FundStatistics {
                                  uint32 numberOfValidators;
                                  uint32 targetWeight;
                              }
                              // Necessary to preserve "statelessness" of dutyForWeek().
                              // Handles case where new users join/leave holder list during an active slot
                              struct HoldersUpdate {
                                  uint32 timestamp;
                                  uint32 startOfSlotNumOwners;
                              }
                              struct BnftHolder {
                                  address holder;
                                  uint32 timestamp;
                              }
                              struct BnftHoldersIndex {
                                  bool registered;
                                  uint32 index;
                              }
                              function numPendingDeposits() external view returns (uint32);
                              function totalValueOutOfLp() external view returns (uint128);
                              function totalValueInLp() external view returns (uint128);
                              function getTotalEtherClaimOf(address _user) external view returns (uint256);
                              function getTotalPooledEther() external view returns (uint256);
                              function sharesForAmount(uint256 _amount) external view returns (uint256);
                              function sharesForWithdrawalAmount(uint256 _amount) external view returns (uint256);
                              function amountForShare(uint256 _share) external view returns (uint256);
                              function deposit() external payable returns (uint256);
                              function deposit(address _referral) external payable returns (uint256);
                              function deposit(address _user, address _referral) external payable returns (uint256);
                              function depositToRecipient(address _recipient, uint256 _amount, address _referral) external returns (uint256);
                              function withdraw(address _recipient, uint256 _amount) external returns (uint256);
                              function requestWithdraw(address recipient, uint256 amount) external returns (uint256);
                              function requestWithdrawWithPermit(address _owner, uint256 _amount, PermitInput calldata _permit) external returns (uint256);
                              function requestMembershipNFTWithdraw(address recipient, uint256 amount, uint256 fee) external returns (uint256);
                              function batchDepositAsBnftHolder(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external payable returns (uint256[] memory);
                              function batchRegisterAsBnftHolder(bytes32 _depositRoot, uint256[] calldata _validatorIds, IStakingManager.DepositData[] calldata _registerValidatorDepositData, bytes32[] calldata _depositDataRootApproval, bytes[] calldata _signaturesForApprovalDeposit) external;
                              function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubKey, bytes[] calldata _signature) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function sendExitRequests(uint256[] calldata _validatorIds) external;
                              function rebase(int128 _accruedRewards) external;
                              function addEthAmountLockedForWithdrawal(uint128 _amount) external;
                              
                              function setStakingTargetWeights(uint32 _eEthWeight, uint32 _etherFanWeight) external;
                              function updateAdmin(address _newAdmin, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                              
                              function decreaseSourceOfFundsValidators(uint32 numberOfEethValidators, uint32 numberOfEtherFanValidators) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IEtherFiAdmin {
                              function lastHandledReportRefSlot() external view returns (uint32);
                              function lastHandledReportRefBlock() external view returns (uint32);
                              function numValidatorsToSpinUp() external view returns (uint32);
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IAuctionManager {
                              struct Bid {
                                  uint256 amount;
                                  uint64 bidderPubKeyIndex;
                                  address bidderAddress;
                                  bool isActive;
                              }
                              function initialize(address _nodeOperatorManagerContract) external;
                              function getBidOwner(uint256 _bidId) external view returns (address);
                              function numberOfActiveBids() external view returns (uint256);
                              function isBidActive(uint256 _bidId) external view returns (bool);
                              function createBid(
                                  uint256 _bidSize,
                                  uint256 _bidAmount
                              ) external payable returns (uint256[] memory);
                              function cancelBidBatch(uint256[] calldata _bidIds) external;
                              function cancelBid(uint256 _bidId) external;
                              function reEnterAuction(uint256 _bidId) external;
                              function updateSelectedBidInformation(uint256 _bidId) external;
                              function processAuctionFeeTransfer(uint256 _validatorId) external;
                              function setStakingManagerContractAddress(
                                  address _stakingManagerContractAddress
                              ) external;
                              function setAccumulatedRevenueThreshold(uint128 _newThreshold) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                              
                              function transferAccumulatedRevenue() external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@eigenlayer/IStrategyManager.sol";
                          import "@eigenlayer/IStrategy.sol";
                          import "@eigenlayer/IPauserRegistry.sol";
                          // cbETH-ETH mainnet: 0x5FAE7E604FC3e24fd43A72867ceBaC94c65b404A
                          // wBETH-ETH mainnet: 0xBfAb6FA95E0091ed66058ad493189D2cB29385E6
                          // stETH-ETH mainnet: 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022
                          interface ICurvePool {
                              function exchange_underlying(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external returns (uint256);
                              function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256);
                              function get_virtual_price() external view returns (uint256);
                          }
                          interface ICurvePoolQuoter1 {
                              function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256); // wBETH-ETH, stETH-ETH
                          }
                          interface ICurvePoolQuoter2 {
                              function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256); // cbETH-ETH
                          }
                          // mint forwarder: 0xfae23c30d383DF59D3E031C325a73d454e8721a6
                          // mainnet: 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704
                          interface IcbETH is IERC20 {
                              function mint(address _to, uint256 _amount) external;
                              function exchangeRate() external view returns (uint256 _exchangeRate);
                          }
                          // mainnet: 0xa2E3356610840701BDf5611a53974510Ae27E2e1
                          interface IwBETH is IERC20 {
                              function deposit(address referral) payable external;
                              function mint(address _to, uint256 _amount) external;
                              function exchangeRate() external view returns (uint256 _exchangeRate);
                          }
                          // mainnet: 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84
                          interface ILido is IERC20 {
                              function getTotalPooledEther() external view returns (uint256);
                              function getTotalShares() external view returns (uint256);
                              function submit(address _referral) external payable returns (uint256);
                              function nonces(address _user) external view returns (uint256);
                              function DOMAIN_SEPARATOR() external view returns (bytes32);
                          }
                          // mainnet: 0x858646372CC42E1A627fcE94aa7A7033e7CF075A
                          interface IEigenLayerStrategyManager is IStrategyManager {
                              function withdrawalRootPending(bytes32 _withdrawalRoot) external view returns (bool);
                              function numWithdrawalsQueued(address _user) external view returns (uint96);
                              function pauserRegistry() external returns (IPauserRegistry);
                              function paused(uint8 index) external view returns (bool);
                              function unpause(uint256 newPausedStatus) external;
                          }
                          interface IEigenLayerStrategyTVLLimits is IStrategy {
                              function getTVLLimits() external view returns (uint256, uint256);
                              function setTVLLimits(uint256 newMaxPerDeposit, uint256 newMaxTotalDeposits) external;
                              function pauserRegistry() external returns (IPauserRegistry);
                              function paused(uint8 index) external view returns (bool);
                              function unpause(uint256 newPausedStatus) external;
                          }
                          // mainnet: 0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1
                          interface ILidoWithdrawalQueue {
                              function FINALIZE_ROLE() external view returns (bytes32);
                              function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);
                              function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256);
                              function requestWithdrawals(uint256[] calldata _amount, address _depositor) external returns (uint256[] memory);
                              function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;
                              function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable;
                              function prefinalize(uint256[] calldata _batches, uint256 _maxShareRate) external view returns (uint256 ethToLock, uint256 sharesToBurn);
                              function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex) external view returns (uint256[] memory hintIds);
                              function getRoleMember(bytes32 _role, uint256 _index) external view returns (address);
                              function getLastRequestId() external view returns (uint256);
                              function getLastCheckpointIndex() external view returns (uint256);
                          }
                          interface ILiquifier {
                              
                              struct PermitInput {
                                  uint256 value;
                                  uint256 deadline;
                                  uint8 v;
                                  bytes32 r;
                                  bytes32 s;
                              } 
                              struct TokenInfo {
                                  uint128 strategyShare;
                                  uint128 ethAmountPendingForWithdrawals;
                                  IStrategy strategy;
                                  bool isWhitelisted;
                                  uint16 discountInBasisPoints;
                                  uint32 timeBoundCapClockStartTime;
                                  uint32 timeBoundCapInEther;
                                  uint32 totalCapInEther;
                                  uint96 totalDepositedThisPeriod;
                                  uint96 totalDeposited;
                              }
                          }// SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library AddressUpgradeable {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 IERC1822ProxiableUpgradeable {
                              /**
                               * @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 v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeaconUpgradeable.sol";
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../../utils/AddressUpgradeable.sol";
                          import "../../utils/StorageSlotUpgradeable.sol";
                          import "../utils/Initializable.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967UpgradeUpgradeable is Initializable {
                              function __ERC1967Upgrade_init() internal onlyInitializing {
                              }
                              function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
                              }
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _getImplementation() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 implementation slot.
                               */
                              function _setImplementation(address newImplementation) private {
                                  require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              function _getAdmin() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 admin slot.
                               */
                              function _setAdmin(address newAdmin) private {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @dev Returns the current beacon.
                               */
                              function _getBeacon() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
                              }
                              /**
                               * @dev Stores a new beacon in the EIP1967 beacon slot.
                               */
                              function _setBeacon(address newBeacon) private {
                                  require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                                  }
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                                  require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                          pragma solidity ^0.8.0;
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Provides information about the current execution context, including the
                           * sender of the transaction and its data. While these are generally available
                           * via msg.sender and msg.data, they should not be accessed in such a direct
                           * manner, since when dealing with meta-transactions the account sending and
                           * paying for execution may not be the actual sender (as far as an application
                           * is concerned).
                           *
                           * This contract is only required for intermediate, library-like contracts.
                           */
                          abstract contract ContextUpgradeable is Initializable {
                              function __Context_init() internal onlyInitializing {
                              }
                              function __Context_init_unchained() internal onlyInitializing {
                              }
                              function _msgSender() internal view virtual returns (address) {
                                  return msg.sender;
                              }
                              function _msgData() internal view virtual returns (bytes calldata) {
                                  return msg.data;
                              }
                              /**
                               * @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: MIT
                          pragma solidity 0.8.13;
                          import "./IEtherFiNodesManager.sol";
                          interface IEtherFiNode {
                              // State Transition Diagram for StateMachine contract:
                              //
                              //      NOT_INITIALIZED
                              //              |
                              //      READY_FOR_DEPOSIT
                              //              ↓
                              //      STAKE_DEPOSITED
                              //           /      \\
                              //          /        \\
                              //         ↓          ↓
                              //         LIVE    CANCELLED
                              //         |  \\ \\ 
                              //         |   \\ \\
                              //         |   ↓  --> EVICTED
                              //         |  BEING_SLASHED
                              //         |    /
                              //         |   /
                              //         ↓  ↓
                              //         EXITED
                              //           |
                              //           ↓
                              //      FULLY_WITHDRAWN
                              // Transitions are only allowed as directed above.
                              // For instance, a transition from STAKE_DEPOSITED to either LIVE or CANCELLED is allowed,
                              // but a transition from STAKE_DEPOSITED to NOT_INITIALIZED, BEING_SLASHED, or EXITED is not.
                              //
                              // All phase transitions should be made through the setPhase function,
                              // which validates transitions based on these rules.
                              //
                              // Fully_WITHDRAWN or CANCELLED nodes can be recycled via rese tWithdrawalSafe()
                              enum VALIDATOR_PHASE {
                                  NOT_INITIALIZED,
                                  STAKE_DEPOSITED,
                                  LIVE,
                                  EXITED,
                                  FULLY_WITHDRAWN,
                                  CANCELLED,
                                  BEING_SLASHED,
                                  EVICTED,
                                  WAITING_FOR_APPROVAL,
                                  READY_FOR_DEPOSIT
                              }
                              // VIEW functions
                              function calculateTVL(uint256 _beaconBalance, uint256 _executionBalance, IEtherFiNodesManager.RewardsSplit memory _SRsplits, uint256 _scale) external view returns (uint256, uint256, uint256, uint256);
                              function eigenPod() external view returns (address);
                              function exitRequestTimestamp() external view returns (uint32);
                              function exitTimestamp() external view returns (uint32);
                              function getNonExitPenalty(uint32 _tNftExitRequestTimestamp, uint32 _bNftExitRequestTimestamp) external view returns (uint256);
                              function getStakingRewardsPayouts(uint256 _beaconBalance, IEtherFiNodesManager.RewardsSplit memory _splits, uint256 _scale) external view returns (uint256, uint256, uint256, uint256);
                              function ipfsHashForEncryptedValidatorKey() external view returns (string memory);
                              function phase() external view returns (VALIDATOR_PHASE);
                              function stakingStartTimestamp() external view returns (uint32);
                              // Non-VIEW functions
                              function claimQueuedWithdrawals(uint256 maxNumWithdrawals) external;
                              function createEigenPod() external;
                              function hasOutstandingEigenLayerWithdrawals() external view returns (bool);
                              function isRestakingEnabled() external view returns (bool);
                              function markExited(uint32 _exitTimestamp) external;
                              function markBeingSlashed() external;
                              function moveRewardsToManager(uint256 _amount) external;
                              function queueRestakedWithdrawal() external;
                              function recordStakingStart(bool _enableRestaking) external;
                              function resetWithdrawalSafe() external;
                              function setExitRequestTimestamp(uint32 _timestamp) external;
                              function setIpfsHashForEncryptedValidatorKey(string calldata _ipfs) external;
                              function setIsRestakingEnabled(bool _enabled) external;
                              function setPhase(VALIDATOR_PHASE _phase) external;
                              function splitBalanceInExecutionLayer() external view returns (uint256 _withdrawalSafe, uint256 _eigenPod, uint256 _delayedWithdrawalRouter);
                              function totalBalanceInExecutionLayer() external view returns (uint256);
                              function withdrawableBalanceInExecutionLayer() external view returns (uint256);
                              function withdrawFunds(
                                  address _treasury,
                                  uint256 _treasuryAmount,
                                  address _operator,
                                  uint256 _operatorAmount,
                                  address _tnftHolder,
                                  uint256 _tnftAmount,
                                  address _bnftHolder,
                                  uint256 _bnftAmount
                              ) external;
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
                          import "./IETHPOSDeposit.sol";
                          import "./IStrategyManager.sol";
                          import "./IEigenPod.sol";
                          import "./IBeaconChainOracle.sol";
                          import "./IPausable.sol";
                          import "./ISlasher.sol";
                          import "./IStrategy.sol";
                          /**
                           * @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           */
                          interface IEigenPodManager is IPausable {
                              /// @notice Emitted to notify the update of the beaconChainOracle address
                              event BeaconOracleUpdated(address indexed newOracleAddress);
                              /// @notice Emitted to notify the deployment of an EigenPod
                              event PodDeployed(address indexed eigenPod, address indexed podOwner);
                              /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
                              event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);
                              /// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue`
                              event MaxPodsUpdated(uint256 previousValue, uint256 newValue);
                              /// @notice Emitted when a withdrawal of beacon chain ETH is completed
                              event BeaconChainETHWithdrawalCompleted(
                                  address indexed podOwner,
                                  uint256 shares,
                                  uint96 nonce,
                                  address delegatedAddress,
                                  address withdrawer,
                                  bytes32 withdrawalRoot
                              );
                              /**
                               * @notice Creates an EigenPod for the sender.
                               * @dev Function will revert if the `msg.sender` already has an EigenPod.
                               * @dev Returns EigenPod address 
                               */
                              function createPod() external returns (address);
                              /**
                               * @notice Stakes for a new beacon chain validator on the sender's EigenPod.
                               * Also creates an EigenPod for the sender if they don't have one already.
                               * @param pubkey The 48 bytes public key of the beacon chain validator.
                               * @param signature The validator's signature of the deposit data.
                               * @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
                               */
                              function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
                              /**
                               * @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager
                               * to ensure that delegated shares are also tracked correctly
                               * @param podOwner is the pod owner whose balance is being updated.
                               * @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
                               * @dev Callable only by the podOwner's EigenPod contract.
                               * @dev Reverts if `sharesDelta` is not a whole Gwei amount
                               */
                              function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
                              /**
                               * @notice Updates the oracle contract that provides the beacon chain state root
                               * @param newBeaconChainOracle is the new oracle contract being pointed to
                               * @dev Callable only by the owner of this contract (i.e. governance)
                               */
                              function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external;
                              /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
                              function ownerToPod(address podOwner) external view returns (IEigenPod);
                              /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
                              function getPod(address podOwner) external view returns (IEigenPod);
                              /// @notice The ETH2 Deposit Contract
                              function ethPOS() external view returns (IETHPOSDeposit);
                              /// @notice Beacon proxy to which the EigenPods point
                              function eigenPodBeacon() external view returns (IBeacon);
                              /// @notice Oracle contract that provides updates to the beacon chain's state
                              function beaconChainOracle() external view returns (IBeaconChainOracle);
                              /// @notice Returns the beacon block root at `timestamp`. Reverts if the Beacon block root at `timestamp` has not yet been finalized.
                              function getBlockRootAtTimestamp(uint64 timestamp) external view returns (bytes32);
                              /// @notice EigenLayer's StrategyManager contract
                              function strategyManager() external view returns (IStrategyManager);
                              /// @notice EigenLayer's Slasher contract
                              function slasher() external view returns (ISlasher);
                              /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
                              function hasPod(address podOwner) external view returns (bool);
                              /// @notice Returns the number of EigenPods that have been created
                              function numPods() external view returns (uint256);
                              /// @notice Returns the maximum number of EigenPods that can be created
                              function maxPods() external view returns (uint256);
                              /**
                               * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
                               * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
                               * decrease between the pod owner queuing and completing a withdrawal.
                               * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
                               * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
                               * as the withdrawal "paying off the deficit".
                               */
                              function podOwnerShares(address podOwner) external view returns (int256);
                              /// @notice returns canonical, virtual beaconChainETH strategy
                              function beaconChainETHStrategy() external view returns (IStrategy);
                              /**
                               * @notice Used by the DelegationManager to remove a pod owner's shares while they're in the withdrawal queue.
                               * Simply decreases the `podOwner`'s shares by `shares`, down to a minimum of zero.
                               * @dev This function reverts if it would result in `podOwnerShares[podOwner]` being less than zero, i.e. it is forbidden for this function to
                               * result in the `podOwner` incurring a "share deficit". This behavior prevents a Staker from queuing a withdrawal which improperly removes excessive
                               * shares from the operator to whom the staker is delegated.
                               * @dev Reverts if `shares` is not a whole Gwei amount
                               */
                              function removeShares(address podOwner, uint256 shares) external;
                              /**
                               * @notice Increases the `podOwner`'s shares by `shares`, paying off deficit if possible.
                               * Used by the DelegationManager to award a pod owner shares on exiting the withdrawal queue
                               * @dev Returns the number of shares added to `podOwnerShares[podOwner]` above zero, which will be less than the `shares` input
                               * in the event that the podOwner has an existing shares deficit (i.e. `podOwnerShares[podOwner]` starts below zero)
                               * @dev Reverts if `shares` is not a whole Gwei amount
                               */
                              function addShares(address podOwner, uint256 shares) external returns (uint256);
                              /**
                               * @notice Used by the DelegationManager to complete a withdrawal, sending tokens to some destination address
                               * @dev Prioritizes decreasing the podOwner's share deficit, if they have one
                               * @dev Reverts if `shares` is not a whole Gwei amount
                               */
                              function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          interface IDelayedWithdrawalRouter {
                              // struct used to pack data into a single storage slot
                              struct DelayedWithdrawal {
                                  uint224 amount;
                                  uint32 blockCreated;
                              }
                              // struct used to store a single users delayedWithdrawal data
                              struct UserDelayedWithdrawals {
                                  uint256 delayedWithdrawalsCompleted;
                                  DelayedWithdrawal[] delayedWithdrawals;
                              }
                               /// @notice event for delayedWithdrawal creation
                              event DelayedWithdrawalCreated(address podOwner, address recipient, uint256 amount, uint256 index);
                              /// @notice event for the claiming of delayedWithdrawals
                              event DelayedWithdrawalsClaimed(address recipient, uint256 amountClaimed, uint256 delayedWithdrawalsCompleted);
                              /// @notice Emitted when the `withdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                              event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
                              /**
                               * @notice Creates an delayed withdrawal for `msg.value` to the `recipient`.
                               * @dev Only callable by the `podOwner`'s EigenPod contract.
                               */
                              function createDelayedWithdrawal(address podOwner, address recipient) external payable;
                              /**
                               * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period.
                               * @param recipient The address to claim delayedWithdrawals for.
                               * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                               */
                              function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfWithdrawalsToClaim) external;
                              /**
                               * @notice Called in order to withdraw delayed withdrawals made to the caller that have passed the `withdrawalDelayBlocks` period.
                               * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                               */
                              function claimDelayedWithdrawals(uint256 maxNumberOfWithdrawalsToClaim) external;
                              /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable.
                              function setWithdrawalDelayBlocks(uint256 newValue) external;
                              /// @notice Getter function for the mapping `_userWithdrawals`
                              function userWithdrawals(address user) external view returns (UserDelayedWithdrawals memory);
                              /// @notice Getter function to get all delayedWithdrawals of the `user`
                              function getUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory);
                              /// @notice Getter function to get all delayedWithdrawals that are currently claimable by the `user`
                              function getClaimableUserDelayedWithdrawals(address user) external view returns (DelayedWithdrawal[] memory);
                              /// @notice Getter function for fetching the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array
                              function userDelayedWithdrawalByIndex(address user, uint256 index) external view returns (DelayedWithdrawal memory);
                              /// @notice Getter function for fetching the length of the delayedWithdrawals array of a specific user
                              function userWithdrawalsLength(address user) external view returns (uint256);
                              /// @notice Convenience function for checking whether or not the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array is currently claimable
                              function canClaimDelayedWithdrawal(address user, uint256 index) external view returns (bool);
                              /**
                               * @notice Delay enforced by this contract for completing any delayedWithdrawal. Measured in blocks, and adjustable by this contract's owner,
                               * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
                               */
                              function withdrawalDelayBlocks() external view returns (uint256);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
                          pragma solidity ^0.8.0;
                          import "../../utils/introspection/IERC165Upgradeable.sol";
                          /**
                           * @dev Required interface of an ERC721 compliant contract.
                           */
                          interface IERC721Upgradeable is IERC165Upgradeable {
                              /**
                               * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                               */
                              event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                              /**
                               * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                               */
                              event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                              /**
                               * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                               */
                              event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                              /**
                               * @dev Returns the number of tokens in ``owner``'s account.
                               */
                              function balanceOf(address owner) external view returns (uint256 balance);
                              /**
                               * @dev Returns the owner of the `tokenId` token.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               */
                              function ownerOf(uint256 tokenId) external view returns (address owner);
                              /**
                               * @dev Safely transfers `tokenId` token from `from` to `to`.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must exist and be owned by `from`.
                               * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId,
                                  bytes calldata data
                              ) external;
                              /**
                               * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                               * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must exist and be owned by `from`.
                               * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                               *
                               * Emits a {Transfer} event.
                               */
                              function safeTransferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) external;
                              /**
                               * @dev Transfers `tokenId` token from `from` to `to`.
                               *
                               * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                               * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                               * understand this adds an external call which potentially creates a reentrancy vulnerability.
                               *
                               * Requirements:
                               *
                               * - `from` cannot be the zero address.
                               * - `to` cannot be the zero address.
                               * - `tokenId` token must be owned by `from`.
                               * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 tokenId
                              ) external;
                              /**
                               * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                               * The approval is cleared when the token is transferred.
                               *
                               * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                               *
                               * Requirements:
                               *
                               * - The caller must own the token or be an approved operator.
                               * - `tokenId` must exist.
                               *
                               * Emits an {Approval} event.
                               */
                              function approve(address to, uint256 tokenId) external;
                              /**
                               * @dev Approve or remove `operator` as an operator for the caller.
                               * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                               *
                               * Requirements:
                               *
                               * - The `operator` cannot be the caller.
                               *
                               * Emits an {ApprovalForAll} event.
                               */
                              function setApprovalForAll(address operator, bool _approved) external;
                              /**
                               * @dev Returns the account approved for `tokenId` token.
                               *
                               * Requirements:
                               *
                               * - `tokenId` must exist.
                               */
                              function getApproved(uint256 tokenId) external view returns (address operator);
                              /**
                               * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                               *
                               * See {setApprovalForAll}
                               */
                              function isApprovedForAll(address owner, address operator) external view returns (bool);
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "./IStrategy.sol";
                          import "./ISlasher.sol";
                          import "./IDelegationManager.sol";
                          /**
                           * @title Interface for the primary entrypoint for funds into EigenLayer.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice See the `StrategyManager` contract itself for implementation details.
                           */
                          interface IStrategyManager {
                              // packed struct for queued withdrawals; helps deal with stack-too-deep errors
                              struct WithdrawerAndNonce {
                                  address withdrawer;
                                  uint96 nonce;
                              }
                              /**
                               * Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored.
                               * In functions that operate on existing queued withdrawals -- e.g. `startQueuedWithdrawalWaitingPeriod` or `completeQueuedWithdrawal`,
                               * the data is resubmitted and the hash of the submitted data is computed by `calculateWithdrawalRoot` and checked against the
                               * stored hash in order to confirm the integrity of the submitted data.
                               */
                              struct QueuedWithdrawal {
                                  IStrategy[] strategies;
                                  uint256[] shares;
                                  address depositor;
                                  WithdrawerAndNonce withdrawerAndNonce;
                                  uint32 withdrawalStartBlock;
                                  address delegatedAddress;
                              }
                              /**
                               * @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender`
                               * @param strategy is the specified strategy where deposit is to be made,
                               * @param token is the denomination in which the deposit is to be made,
                               * @param amount is the amount of token to be deposited in the strategy by the depositor
                               * @return shares The amount of new shares in the `strategy` created as part of the action.
                               * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
                               * @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen).
                               * 
                               * WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended.  This can lead to attack vectors
                               *          where the token balance and corresponding strategy shares are not in sync upon reentrancy.
                               */
                              function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount)
                                  external
                                  returns (uint256 shares);
                              /**
                               * @notice Deposits `amount` of beaconchain ETH into this contract on behalf of `staker`
                               * @param staker is the entity that is restaking in eigenlayer,
                               * @param amount is the amount of beaconchain ETH being restaked,
                               * @dev Only callable by EigenPodManager.
                               */
                              function depositBeaconChainETH(address staker, uint256 amount) external;
                              /**
                               * @notice Records an overcommitment event on behalf of a staker. The staker's beaconChainETH shares are decremented by `amount`.
                               * @param overcommittedPodOwner is the pod owner to be slashed
                               * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy in case it must be removed,
                               * @param amount is the amount to decrement the slashedAddress's beaconChainETHStrategy shares
                               * @dev Only callable by EigenPodManager.
                               */
                              function recordOvercommittedBeaconChainETH(address overcommittedPodOwner, uint256 beaconChainETHStrategyIndex, uint256 amount)
                                  external;
                              /**
                               * @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`,
                               * who must sign off on the action.
                               * Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed 
                               * purely to help one address deposit 'for' another.
                               * @param strategy is the specified strategy where deposit is to be made,
                               * @param token is the denomination in which the deposit is to be made,
                               * @param amount is the amount of token to be deposited in the strategy by the depositor
                               * @param staker the staker that the deposited assets will be credited to
                               * @param expiry the timestamp at which the signature expires
                               * @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward
                               * following EIP-1271 if the `staker` is a contract
                               * @return shares The amount of new shares in the `strategy` created as part of the action.
                               * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
                               * @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those
                               * targeting stakers who may be attempting to undelegate.
                               * @dev Cannot be called on behalf of a staker that is 'frozen' (this function will revert if the `staker` is frozen).
                               * 
                               *  WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended.  This can lead to attack vectors
                               *          where the token balance and corresponding strategy shares are not in sync upon reentrancy
                               */
                              function depositIntoStrategyWithSignature(
                                  IStrategy strategy,
                                  IERC20 token,
                                  uint256 amount,
                                  address staker,
                                  uint256 expiry,
                                  bytes memory signature
                              )
                                  external
                                  returns (uint256 shares);
                              /// @notice Returns the current shares of `user` in `strategy`
                              function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares);
                              /**
                               * @notice Get all details on the depositor's deposits and corresponding shares
                               * @return (depositor's strategies, shares in these strategies)
                               */
                              function getDeposits(address depositor) external view returns (IStrategy[] memory, uint256[] memory);
                              /// @notice Simple getter function that returns `stakerStrategyList[staker].length`.
                              function stakerStrategyListLength(address staker) external view returns (uint256);
                              /**
                               * @notice Called by a staker to queue a withdrawal of the given amount of `shares` from each of the respective given `strategies`.
                               * @dev Stakers will complete their withdrawal by calling the 'completeQueuedWithdrawal' function.
                               * User shares are decreased in this function, but the total number of shares in each strategy remains the same.
                               * The total number of shares is decremented in the 'completeQueuedWithdrawal' function instead, which is where
                               * the funds are actually sent to the user through use of the strategies' 'withdrawal' function. This ensures
                               * that the value per share reported by each strategy will remain consistent, and that the shares will continue
                               * to accrue gains during the enforced withdrawal waiting period.
                               * @param strategyIndexes is a list of the indices in `stakerStrategyList[msg.sender]` that correspond to the strategies
                               * for which `msg.sender` is withdrawing 100% of their shares
                               * @param strategies The Strategies to withdraw from
                               * @param shares The amount of shares to withdraw from each of the respective Strategies in the `strategies` array
                               * @param withdrawer The address that can complete the withdrawal and will receive any withdrawn funds or shares upon completing the withdrawal
                               * @param undelegateIfPossible If this param is marked as 'true' *and the withdrawal will result in `msg.sender` having no shares in any Strategy,*
                               * then this function will also make an internal call to `undelegate(msg.sender)` to undelegate the `msg.sender`.
                               * @return The 'withdrawalRoot' of the newly created Queued Withdrawal
                               * @dev Strategies are removed from `stakerStrategyList` by swapping the last entry with the entry to be removed, then
                               * popping off the last entry in `stakerStrategyList`. The simplest way to calculate the correct `strategyIndexes` to input
                               * is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
                               * `stakerStrategyList` to lowest index
                               * @dev Note that if the withdrawal includes shares in the enshrined 'beaconChainETH' strategy, then it must *only* include shares in this strategy, and
                               * `withdrawer` must match the caller's address. The first condition is because slashing of queued withdrawals cannot be guaranteed 
                               * for Beacon Chain ETH (since we cannot trigger a withdrawal from the beacon chain through a smart contract) and the second condition is because shares in
                               * the enshrined 'beaconChainETH' strategy technically represent non-fungible positions (deposits to the Beacon Chain, each pointed at a specific EigenPod).
                               */
                              function queueWithdrawal(
                                  uint256[] calldata strategyIndexes,
                                  IStrategy[] calldata strategies,
                                  uint256[] calldata shares,
                                  address withdrawer,
                                  bool undelegateIfPossible
                              )
                                  external returns(bytes32);
                                  
                              /**
                               * @notice Used to complete the specified `queuedWithdrawal`. The function caller must match `queuedWithdrawal.withdrawer`
                               * @param queuedWithdrawal The QueuedWithdrawal to complete.
                               * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `strategies` array
                               * of the `queuedWithdrawal`. This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused)
                               * @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
                               * @param receiveAsTokens If true, the shares specified in the queued withdrawal will be withdrawn from the specified strategies themselves
                               * and sent to the caller, through calls to `queuedWithdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
                               * will simply be transferred to the caller directly.
                               * @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw`
                               */
                              function completeQueuedWithdrawal(
                                  QueuedWithdrawal calldata queuedWithdrawal,
                                  IERC20[] calldata tokens,
                                  uint256 middlewareTimesIndex,
                                  bool receiveAsTokens
                              )
                                  external;
                              
                              /**
                               * @notice Used to complete the specified `queuedWithdrawals`. The function caller must match `queuedWithdrawals[...].withdrawer`
                               * @param queuedWithdrawals The QueuedWithdrawals to complete.
                               * @param tokens Array of tokens for each QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
                               * @param middlewareTimesIndexes One index to reference per QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
                               * @param receiveAsTokens If true, the shares specified in the queued withdrawal will be withdrawn from the specified strategies themselves
                               * and sent to the caller, through calls to `queuedWithdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
                               * will simply be transferred to the caller directly.
                               * @dev Array-ified version of `completeQueuedWithdrawal`
                               * @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw`
                               */
                              function completeQueuedWithdrawals(
                                  QueuedWithdrawal[] calldata queuedWithdrawals,
                                  IERC20[][] calldata tokens,
                                  uint256[] calldata middlewareTimesIndexes,
                                  bool[] calldata receiveAsTokens
                              )
                                  external;
                              /**
                               * @notice Slashes the shares of a 'frozen' operator (or a staker delegated to one)
                               * @param slashedAddress is the frozen address that is having its shares slashed
                               * @param recipient is the address that will receive the slashed funds, which could e.g. be a harmed party themself,
                               * or a MerkleDistributor-type contract that further sub-divides the slashed funds.
                               * @param strategies Strategies to slash
                               * @param shareAmounts The amount of shares to slash in each of the provided `strategies`
                               * @param tokens The tokens to use as input to the `withdraw` function of each of the provided `strategies`
                               * @param strategyIndexes is a list of the indices in `stakerStrategyList[msg.sender]` that correspond to the strategies
                               * for which `msg.sender` is withdrawing 100% of their shares
                               * @param recipient The slashed funds are withdrawn as tokens to this address.
                               * @dev strategies are removed from `stakerStrategyList` by swapping the last entry with the entry to be removed, then
                               * popping off the last entry in `stakerStrategyList`. The simplest way to calculate the correct `strategyIndexes` to input
                               * is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
                               * `stakerStrategyList` to lowest index
                               */
                              function slashShares(
                                  address slashedAddress,
                                  address recipient,
                                  IStrategy[] calldata strategies,
                                  IERC20[] calldata tokens,
                                  uint256[] calldata strategyIndexes,
                                  uint256[] calldata shareAmounts
                              )
                                  external;
                              /**
                               * @notice Slashes an existing queued withdrawal that was created by a 'frozen' operator (or a staker delegated to one)
                               * @param recipient The funds in the slashed withdrawal are withdrawn as tokens to this address.
                               * @param queuedWithdrawal The previously queued withdrawal to be slashed
                               * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `strategies`
                               * array of the `queuedWithdrawal`.
                               * @param indicesToSkip Optional input parameter -- indices in the `strategies` array to skip (i.e. not call the 'withdraw' function on). This input exists
                               * so that, e.g., if the slashed QueuedWithdrawal contains a malicious strategy in the `strategies` array which always reverts on calls to its 'withdraw' function,
                               * then the malicious strategy can be skipped (with the shares in effect "burned"), while the non-malicious strategies are still called as normal.
                               */
                              function slashQueuedWithdrawal(address recipient, QueuedWithdrawal calldata queuedWithdrawal, IERC20[] calldata tokens, uint256[] calldata indicesToSkip)
                                  external;
                              /// @notice Returns the keccak256 hash of `queuedWithdrawal`.
                              function calculateWithdrawalRoot(
                                  QueuedWithdrawal memory queuedWithdrawal
                              )
                                  external
                                  pure
                                  returns (bytes32);
                              /**
                               * @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into
                               * @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already)
                              */
                              function addStrategiesToDepositWhitelist(IStrategy[] calldata strategiesToWhitelist) external;
                              /**
                               * @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into
                               * @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it)
                              */
                              function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external;
                              /// @notice Returns the single, central Delegation contract of EigenLayer
                              function delegation() external view returns (IDelegationManager);
                              /// @notice Returns the single, central Slasher contract of EigenLayer
                              function slasher() external view returns (ISlasher);
                              /// @notice returns the enshrined, virtual 'beaconChainETH' Strategy
                              function beaconChainETHStrategy() external view returns (IStrategy);
                              /// @notice Returns the number of blocks that must pass between the time a withdrawal is queued and the time it can be completed
                              function withdrawalDelayBlocks() external view returns (uint256);
                          }// SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                          /**
                           * @title Minimal interface for an `Strategy` contract.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice Custom `Strategy` implementations may expand extensively on this interface.
                           */
                          interface IStrategy {
                              /**
                               * @notice Used to deposit tokens into this Strategy
                               * @param token is the ERC20 token being deposited
                               * @param amount is the amount of token being deposited
                               * @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
                               * `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
                               * @return newShares is the number of new shares issued at the current exchange ratio.
                               */
                              function deposit(IERC20 token, uint256 amount) external returns (uint256);
                              /**
                               * @notice Used to withdraw tokens from this Strategy, to the `depositor`'s address
                               * @param depositor is the address to receive the withdrawn funds
                               * @param token is the ERC20 token being transferred out
                               * @param amountShares is the amount of shares being withdrawn
                               * @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
                               * other functions, and individual share balances are recorded in the strategyManager as well.
                               */
                              function withdraw(address depositor, IERC20 token, uint256 amountShares) external;
                              /**
                               * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
                               * @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
                               * @param amountShares is the amount of shares to calculate its conversion into the underlying token
                               * @return The amount of underlying tokens corresponding to the input `amountShares`
                               * @dev Implementation for these functions in particular may vary significantly for different strategies
                               */
                              function sharesToUnderlying(uint256 amountShares) external returns (uint256);
                              /**
                               * @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
                               * @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
                               * @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
                               * @return The amount of underlying tokens corresponding to the input `amountShares`
                               * @dev Implementation for these functions in particular may vary significantly for different strategies
                               */
                              function underlyingToShares(uint256 amountUnderlying) external returns (uint256);
                              /**
                               * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
                               * this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
                               */
                              function userUnderlying(address user) external returns (uint256);
                               /**
                               * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
                               * @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
                               * @param amountShares is the amount of shares to calculate its conversion into the underlying token
                               * @return The amount of shares corresponding to the input `amountUnderlying`
                               * @dev Implementation for these functions in particular may vary significantly for different strategies
                               */
                              function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256);
                              /**
                               * @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
                               * @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
                               * @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
                               * @return The amount of shares corresponding to the input `amountUnderlying`
                               * @dev Implementation for these functions in particular may vary significantly for different strategies
                               */
                              function underlyingToSharesView(uint256 amountUnderlying) external view returns (uint256);
                              /**
                               * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
                               * this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
                               */
                              function userUnderlyingView(address user) external view returns (uint256);
                              /// @notice The underlying token for shares in this Strategy
                              function underlyingToken() external view returns (IERC20);
                              /// @notice The total number of extant shares in this Strategy
                              function totalShares() external view returns (uint256);
                              /// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
                              function explanation() external view returns (string memory);
                              function shares(address user) external view returns (uint256);
                          }// SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          /**
                           * @title Interface for the `PauserRegistry` contract.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           */
                          interface IPauserRegistry {
                              event PauserStatusChanged(address pauser, bool canPause);
                              event UnpauserChanged(address previousUnpauser, address newUnpauser);
                              
                              /// @notice Mapping of addresses to whether they hold the pauser role.
                              function isPauser(address pauser) external view returns (bool);
                              /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
                              function unpauser() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev This is the interface that {BeaconProxy} expects of its beacon.
                           */
                          interface IBeaconUpgradeable {
                              /**
                               * @dev Must return an address that can be used as a delegate call target.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlotUpgradeable {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                          // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                          // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                          // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                          // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                          // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                          // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                          // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                          // SPDX-License-Identifier: CC0-1.0
                          pragma solidity >=0.5.0;
                          // This interface is designed to be compatible with the Vyper version.
                          /// @notice This is the Ethereum 2.0 deposit contract interface.
                          /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
                          interface IETHPOSDeposit {
                              /// @notice A processed deposit event.
                              event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);
                              /// @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;
                              /// @notice Query the current deposit root hash.
                              /// @return The deposit root hash.
                              function get_deposit_root() external view returns (bytes32);
                              /// @notice Query the current deposit count.
                              /// @return The deposit count encoded as a little endian 64-bit number.
                              function get_deposit_count() external view returns (bytes memory);
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "../eigenlayer-libraries/BeaconChainProofs.sol";
                          import "./IEigenPodManager.sol";
                          import "./IBeaconChainOracle.sol";
                          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                          /**
                           * @title The implementation contract used for restaking beacon chain ETH on EigenLayer
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice The main functionalities are:
                           * - creating new ETH validators with their withdrawal credentials pointed to this contract
                           * - proving from beacon chain state roots that withdrawal credentials are pointed to this contract
                           * - proving from beacon chain state roots the balances of ETH validators with their withdrawal credentials
                           *   pointed to this contract
                           * - updating aggregate balances in the EigenPodManager
                           * - withdrawing eth when withdrawals are initiated
                           * @dev Note that all beacon chain balances are stored as gwei within the beacon chain datastructures. We choose
                           *   to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts
                           */
                          interface IEigenPod {
                              enum VALIDATOR_STATUS {
                                  INACTIVE, // doesnt exist
                                  ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
                                  WITHDRAWN // withdrawn from the Beacon Chain
                              }
                              struct ValidatorInfo {
                                  // index of the validator in the beacon chain
                                  uint64 validatorIndex;
                                  // amount of beacon chain ETH restaked on EigenLayer in gwei
                                  uint64 restakedBalanceGwei;
                                  //timestamp of the validator's most recent balance update
                                  uint64 mostRecentBalanceUpdateTimestamp;
                                  // status of the validator
                                  VALIDATOR_STATUS status;
                              }
                              /**
                               * @notice struct used to store amounts related to proven withdrawals in memory. Used to help
                               * manage stack depth and optimize the number of external calls, when batching withdrawal operations.
                               */
                              struct VerifiedWithdrawal {
                                  // amount to send to a podOwner from a proven withdrawal
                                  uint256 amountToSendGwei;
                                  // difference in shares to be recorded in the eigenPodManager, as a result of the withdrawal
                                  int256 sharesDeltaGwei;
                              }
                              enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
                                  REDEEMED,
                                  PENDING,
                                  FAILED
                              }
                              /// @notice Emitted when an ETH validator stakes via this eigenPod
                              event EigenPodStaked(bytes pubkey);
                              /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
                              event ValidatorRestaked(uint40 validatorIndex);
                              /// @notice Emitted when an ETH validator's  balance is proven to be updated.  Here newValidatorBalanceGwei
                              //  is the validator's balance that is credited on EigenLayer.
                              event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei);
                              /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
                              event FullWithdrawalRedeemed(
                                  uint40 validatorIndex,
                                  uint64 withdrawalTimestamp,
                                  address indexed recipient,
                                  uint64 withdrawalAmountGwei
                              );
                              /// @notice Emitted when a partial withdrawal claim is successfully redeemed
                              event PartialWithdrawalRedeemed(
                                  uint40 validatorIndex,
                                  uint64 withdrawalTimestamp,
                                  address indexed recipient,
                                  uint64 partialWithdrawalAmountGwei
                              );
                              /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
                              event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
                              /// @notice Emitted when podOwner enables restaking
                              event RestakingActivated(address indexed podOwner);
                              /// @notice Emitted when ETH is received via the `receive` fallback
                              event NonBeaconChainETHReceived(uint256 amountReceived);
                              /// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
                              event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);
                              /// @notice The max amount of eth, in gwei, that can be restaked per validator
                              function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);
                              /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
                              function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);
                              /// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
                              function nonBeaconChainETHBalanceWei() external view returns (uint256);
                              /// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
                              function initialize(address owner) external;
                              /// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
                              function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
                              /**
                               * @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address
                               * @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain.
                               * @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `withdrawableRestakedExecutionLayerGwei` exceeds the
                               * `amountWei` input (when converted to GWEI).
                               * @dev Reverts if `amountWei` is not a whole Gwei amount
                               */
                              function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;
                              /// @notice The single EigenPodManager for EigenLayer
                              function eigenPodManager() external view returns (IEigenPodManager);
                              /// @notice The owner of this EigenPod
                              function podOwner() external view returns (address);
                              /// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
                              function hasRestaked() external view returns (bool);
                              /**
                               * @notice The latest timestamp at which the pod owner withdrew the balance of the pod, via calling `withdrawBeforeRestaking`.
                               * @dev This variable is only updated when the `withdrawBeforeRestaking` function is called, which can only occur before `hasRestaked` is set to true for this pod.
                               * Proofs for this pod are only valid against Beacon Chain state roots corresponding to timestamps after the stored `mostRecentWithdrawalTimestamp`.
                               */
                              function mostRecentWithdrawalTimestamp() external view returns (uint64);
                              /// @notice Returns the validatorInfo struct for the provided pubkeyHash
                              function validatorPubkeyHashToInfo(bytes32 validatorPubkeyHash) external view returns (ValidatorInfo memory);
                              /// @notice Returns the validatorInfo struct for the provided pubkey
                              function validatorPubkeyToInfo(bytes calldata validatorPubkey) external view returns (ValidatorInfo memory);
                              ///@notice mapping that tracks proven withdrawals
                              function provenWithdrawal(bytes32 validatorPubkeyHash, uint64 slot) external view returns (bool);
                              /// @notice This returns the status of a given validator
                              function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS);
                              /// @notice This returns the status of a given validator pubkey
                              function validatorStatus(bytes calldata validatorPubkey) external view returns (VALIDATOR_STATUS);
                              /**
                               * @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to
                               * this contract. It also verifies the effective balance  of the validator.  It verifies the provided proof of the ETH validator against the beacon chain state
                               * root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer.
                               * @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against.
                               * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
                               * @param withdrawalCredentialProofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials
                               * against a beacon chain state root
                               * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                               * for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                               */
                              function verifyWithdrawalCredentials(
                                  uint64 oracleTimestamp,
                                  BeaconChainProofs.StateRootProof calldata stateRootProof,
                                  uint40[] calldata validatorIndices,
                                  bytes[] calldata withdrawalCredentialProofs,
                                  bytes32[][] calldata validatorFields
                              )
                                  external;
                              /**
                               * @notice This function records an update (either increase or decrease) in the pod's balance in the StrategyManager.  
                                         It also verifies a merkle proof of the validator's current beacon chain balance.  
                               * @param oracleTimestamp The oracleTimestamp whose state root the `proof` will be proven against.
                               *        Must be within `VERIFY_BALANCE_UPDATE_WINDOW_SECONDS` of the current block.
                               * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs 
                               * @param validatorFieldsProofs proofs against the `beaconStateRoot` for each validator in `validatorFields`
                               * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                               * @dev For more details on the Beacon Chain spec, see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                               */
                              function verifyBalanceUpdates(
                                  uint64 oracleTimestamp,
                                  uint40[] calldata validatorIndices,
                                  BeaconChainProofs.StateRootProof calldata stateRootProof,
                                  bytes[] calldata validatorFieldsProofs,
                                  bytes32[][] calldata validatorFields
                              ) external;
                              /**
                               * @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod
                               * @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against
                               * @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven
                               * @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree
                               * @param withdrawalFields are the fields of the withdrawals being proven
                               * @param validatorFields are the fields of the validators being proven
                               */
                              function verifyAndProcessWithdrawals(
                                  uint64 oracleTimestamp,
                                  BeaconChainProofs.StateRootProof calldata stateRootProof,
                                  BeaconChainProofs.WithdrawalProof[] calldata withdrawalProofs,
                                  bytes[] calldata validatorFieldsProofs,
                                  bytes32[][] calldata validatorFields,
                                  bytes32[][] calldata withdrawalFields
                              ) external;
                              /**
                               * @notice Called by the pod owner to activate restaking by withdrawing
                               * all existing ETH from the pod and preventing further withdrawals via
                               * "withdrawBeforeRestaking()"
                               */
                              function activateRestaking() external;
                              /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                              function withdrawBeforeRestaking() external;
                              /// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei
                              function withdrawNonBeaconChainETHBalanceWei(address recipient, uint256 amountToWithdraw) external;
                              /// @notice called by owner of a pod to remove any ERC20s deposited in the pod
                              function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external;
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          /**
                           * @title Interface for the BeaconStateOracle contract.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           */
                          interface IBeaconChainOracle {
                              /// @notice The block number to state root mapping.
                              function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32);
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "./IPauserRegistry.sol";
                          /**
                           * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
                           * These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
                           * @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
                           * Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
                           * For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
                           * you can only flip (any number of) switches to off/0 (aka "paused").
                           * If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
                           * 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
                           * 2) update the paused state to this new value
                           * @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
                           * indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
                           */
                          interface IPausable {
                              /// @notice Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
                              event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
                              /// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
                              event Paused(address indexed account, uint256 newPausedStatus);
                              /// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
                              event Unpaused(address indexed account, uint256 newPausedStatus);
                              
                              /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
                              function pauserRegistry() external view returns (IPauserRegistry);
                              /**
                               * @notice This function is used to pause an EigenLayer contract's functionality.
                               * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
                               * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                               * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
                               */
                              function pause(uint256 newPausedStatus) external;
                              /**
                               * @notice Alias for `pause(type(uint256).max)`.
                               */
                              function pauseAll() external;
                              /**
                               * @notice This function is used to unpause an EigenLayer contract's functionality.
                               * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
                               * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                               * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
                               */
                              function unpause(uint256 newPausedStatus) external;
                              /// @notice Returns the current paused status as a uint256.
                              function paused() external view returns (uint256);
                              /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
                              function paused(uint8 index) external view returns (bool);
                              /// @notice Allows the unpauser to set a new pauser registry
                              function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "./IStrategyManager.sol";
                          import "./IDelegationManager.sol";
                          /**
                           * @title Interface for the primary 'slashing' contract for EigenLayer.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice See the `Slasher` contract itself for implementation details.
                           */
                          interface ISlasher {
                              // struct used to store information about the current state of an operator's obligations to middlewares they are serving
                              struct MiddlewareTimes {
                                  // The update block for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
                                  uint32 stalestUpdateBlock;
                                  // The latest 'serveUntilBlock' from all of the middleware that the operator is serving
                                  uint32 latestServeUntilBlock;
                              }
                              // struct used to store details relevant to a single middleware that an operator has opted-in to serving
                              struct MiddlewareDetails {
                                  // the block at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
                                  uint32 registrationMayBeginAtBlock;
                                  // the block before which the contract is allowed to slash the user
                                  uint32 contractCanSlashOperatorUntilBlock;
                                  // the block at which the middleware's view of the operator's stake was most recently updated
                                  uint32 latestUpdateBlock;
                              }
                              /// @notice Emitted when a middleware times is added to `operator`'s array.
                              event MiddlewareTimesAdded(
                                  address operator,
                                  uint256 index,
                                  uint32 stalestUpdateBlock,
                                  uint32 latestServeUntilBlock
                              );
                              /// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
                              event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
                              /// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilBlock`.
                              event SlashingAbilityRevoked(
                                  address indexed operator,
                                  address indexed contractAddress,
                                  uint32 contractCanSlashOperatorUntilBlock
                              );
                              /**
                               * @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
                               * @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
                               */
                              event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
                              /// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within EigenLayer.
                              event FrozenStatusReset(address indexed previouslySlashedAddress);
                              /**
                               * @notice Gives the `contractAddress` permission to slash the funds of the caller.
                               * @dev Typically, this function must be called prior to registering for a middleware.
                               */
                              function optIntoSlashing(address contractAddress) external;
                              /**
                               * @notice Used for 'slashing' a certain operator.
                               * @param toBeFrozen The operator to be frozen.
                               * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
                               * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
                               */
                              function freezeOperator(address toBeFrozen) external;
                              /**
                               * @notice Removes the 'frozen' status from each of the `frozenAddresses`
                               * @dev Callable only by the contract owner (i.e. governance).
                               */
                              function resetFrozenStatus(address[] calldata frozenAddresses) external;
                              /**
                               * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
                               *         is slashable until serveUntil
                               * @param operator the operator whose stake update is being recorded
                               * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                               * @dev adds the middleware's slashing contract to the operator's linked list
                               */
                              function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) external;
                              /**
                               * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
                               *         to make sure the operator's stake at updateBlock is slashable until serveUntil
                               * @param operator the operator whose stake update is being recorded
                               * @param updateBlock the block for which the stake update is being recorded
                               * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable
                               * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
                               * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
                               *      but it is anticipated to be rare and not detrimental.
                               */
                              function recordStakeUpdate(
                                  address operator,
                                  uint32 updateBlock,
                                  uint32 serveUntilBlock,
                                  uint256 insertAfter
                              ) external;
                              /**
                               * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
                               *         is slashable until serveUntil
                               * @param operator the operator whose stake update is being recorded
                               * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                               * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
                               * slash `operator` once `serveUntil` is reached
                               */
                              function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) external;
                              /// @notice The StrategyManager contract of EigenLayer
                              function strategyManager() external view returns (IStrategyManager);
                              /// @notice The DelegationManager contract of EigenLayer
                              function delegation() external view returns (IDelegationManager);
                              /**
                               * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
                               * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
                               * and the staker's status is reset (to 'unfrozen').
                               * @param staker The staker of interest.
                               * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
                               * to an operator who has their status set to frozen. Otherwise returns 'false'.
                               */
                              function isFrozen(address staker) external view returns (bool);
                              /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
                              function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
                              /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`.
                              function contractCanSlashOperatorUntilBlock(
                                  address operator,
                                  address serviceContract
                              ) external view returns (uint32);
                              /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake
                              function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32);
                              /// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
                              function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) external view returns (uint256);
                              /**
                               * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used
                               * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
                               * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
                               * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event
                               * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
                               * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
                               * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`.
                               * @param withdrawalStartBlock The block number at which the withdrawal was initiated.
                               * @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
                               * @dev The correct `middlewareTimesIndex` input should be computable off-chain.
                               */
                              function canWithdraw(
                                  address operator,
                                  uint32 withdrawalStartBlock,
                                  uint256 middlewareTimesIndex
                              ) external returns (bool);
                              /**
                               * operator =>
                               *  [
                               *      (
                               *          the least recent update block of all of the middlewares it's serving/served,
                               *          latest time that the stake bonded at that update needed to serve until
                               *      )
                               *  ]
                               */
                              function operatorToMiddlewareTimes(
                                  address operator,
                                  uint256 arrayIndex
                              ) external view returns (MiddlewareTimes memory);
                              /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
                              function middlewareTimesLength(address operator) external view returns (uint256);
                              /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`.
                              function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32);
                              /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
                              function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32);
                              /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
                              function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256);
                              /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
                              function operatorWhitelistedContractsLinkedListEntry(
                                  address operator,
                                  address node
                              ) external view returns (bool, uint256, uint256);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC165 standard, as defined in the
                           * https://eips.ethereum.org/EIPS/eip-165[EIP].
                           *
                           * Implementers can declare support of contract interfaces, which can then be
                           * queried by others ({ERC165Checker}).
                           *
                           * For an implementation, see {ERC165}.
                           */
                          interface IERC165Upgradeable {
                              /**
                               * @dev Returns true if this contract implements the interface defined by
                               * `interfaceId`. See the corresponding
                               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                               * to learn more about how these ids are created.
                               *
                               * This function call must use less than 30 000 gas.
                               */
                              function supportsInterface(bytes4 interfaceId) external view returns (bool);
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "./IDelegationTerms.sol";
                          /**
                           * @title The interface for the primary delegation contract for EigenLayer.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice  This is the contract for delegation in EigenLayer. The main functionalities of this contract are
                           * - enabling anyone to register as an operator in EigenLayer
                           * - allowing new operators to provide a DelegationTerms-type contract, which may mediate their interactions with stakers who delegate to them
                           * - enabling any staker to delegate its stake to the operator of its choice
                           * - enabling a staker to undelegate its assets from an operator (performed as part of the withdrawal process, initiated through the StrategyManager)
                           */
                          interface IDelegationManager {
                              /**
                               * @notice This will be called by an operator to register itself as an operator that stakers can choose to delegate to.
                               * @param dt is the `DelegationTerms` contract that the operator has for those who delegate to them.
                               * @dev An operator can set `dt` equal to their own address (or another EOA address), in the event that they want to split payments
                               * in a more 'trustful' manner.
                               * @dev In the present design, once set, there is no way for an operator to ever modify the address of their DelegationTerms contract.
                               */
                              function registerAsOperator(IDelegationTerms dt) external;
                              /**
                               *  @notice This will be called by a staker to delegate its assets to some operator.
                               *  @param operator is the operator to whom staker (msg.sender) is delegating its assets
                               */
                              function delegateTo(address operator) external;
                              /**
                               * @notice Delegates from `staker` to `operator`.
                               * @dev requires that:
                               * 1) if `staker` is an EOA, then `signature` is valid ECDSA signature from `staker`, indicating their intention for this action
                               * 2) if `staker` is a contract, then `signature` must will be checked according to EIP-1271
                               */
                              function delegateToBySignature(address staker, address operator, uint256 expiry, bytes memory signature) external;
                              /**
                               * @notice Undelegates `staker` from the operator who they are delegated to.
                               * @notice Callable only by the StrategyManager
                               * @dev Should only ever be called in the event that the `staker` has no active deposits in EigenLayer.
                               */
                              function undelegate(address staker) external;
                              /// @notice returns the address of the operator that `staker` is delegated to.
                              function delegatedTo(address staker) external view returns (address);
                              /// @notice returns the DelegationTerms of the `operator`, which may mediate their interactions with stakers who delegate to them.
                              function delegationTerms(address operator) external view returns (IDelegationTerms);
                              /// @notice returns the total number of shares in `strategy` that are delegated to `operator`.
                              function operatorShares(address operator, IStrategy strategy) external view returns (uint256);
                              /**
                               * @notice Increases the `staker`'s delegated shares in `strategy` by `shares, typically called when the staker has further deposits into EigenLayer
                               * @dev Callable only by the StrategyManager
                               */
                              function increaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
                              /**
                               * @notice Decreases the `staker`'s delegated shares in each entry of `strategies` by its respective `shares[i]`, typically called when the staker withdraws from EigenLayer
                               * @dev Callable only by the StrategyManager
                               */
                              function decreaseDelegatedShares(
                                  address staker,
                                  IStrategy[] calldata strategies,
                                  uint256[] calldata shares
                              ) external;
                              /// @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
                              function isDelegated(address staker) external view returns (bool);
                              /// @notice Returns 'true' if `staker` is *not* actively delegated, and 'false' otherwise.
                              function isNotDelegated(address staker) external view returns (bool);
                              /// @notice Returns if an operator can be delegated to, i.e. it has called `registerAsOperator`.
                              function isOperator(address operator) external view returns (bool);
                          }// SPDX-License-Identifier: BUSL-1.1
                          pragma solidity ^0.8.0;
                          import "./Merkle.sol";
                          import "./Endian.sol";
                          //Utility library for parsing and PHASE0 beacon chain block headers
                          //SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
                          //BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
                          //BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate
                          library BeaconChainProofs {
                              // constants are the number of fields and the heights of the different merkle trees used in merkleizing beacon chain containers
                              uint256 internal constant NUM_BEACON_BLOCK_HEADER_FIELDS = 5;
                              uint256 internal constant BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT = 3;
                              uint256 internal constant NUM_BEACON_BLOCK_BODY_FIELDS = 11;
                              uint256 internal constant BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT = 4;
                              uint256 internal constant NUM_BEACON_STATE_FIELDS = 21;
                              uint256 internal constant BEACON_STATE_FIELD_TREE_HEIGHT = 5;
                              uint256 internal constant NUM_ETH1_DATA_FIELDS = 3;
                              uint256 internal constant ETH1_DATA_FIELD_TREE_HEIGHT = 2;
                              uint256 internal constant NUM_VALIDATOR_FIELDS = 8;
                              uint256 internal constant VALIDATOR_FIELD_TREE_HEIGHT = 3;
                              uint256 internal constant NUM_EXECUTION_PAYLOAD_HEADER_FIELDS = 15;
                              uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT = 4;
                              uint256 internal constant NUM_EXECUTION_PAYLOAD_FIELDS = 15;
                              uint256 internal constant EXECUTION_PAYLOAD_FIELD_TREE_HEIGHT = 4;
                              // HISTORICAL_ROOTS_LIMIT\t = 2**24, so tree height is 24
                              uint256 internal constant HISTORICAL_ROOTS_TREE_HEIGHT = 24;
                              // HISTORICAL_BATCH is root of state_roots and block_root, so number of leaves =  2^1
                              uint256 internal constant HISTORICAL_BATCH_TREE_HEIGHT = 1;
                              // SLOTS_PER_HISTORICAL_ROOT = 2**13, so tree height is 13
                              uint256 internal constant STATE_ROOTS_TREE_HEIGHT = 13;
                              uint256 internal constant BLOCK_ROOTS_TREE_HEIGHT = 13;
                              //HISTORICAL_ROOTS_LIMIT = 2**24, so tree height is 24
                              uint256 internal constant HISTORICAL_SUMMARIES_TREE_HEIGHT = 24;
                              //Index of block_summary_root in historical_summary container
                              uint256 internal constant BLOCK_SUMMARY_ROOT_INDEX = 0;
                              uint256 internal constant NUM_WITHDRAWAL_FIELDS = 4;
                              // tree height for hash tree of an individual withdrawal container
                              uint256 internal constant WITHDRAWAL_FIELD_TREE_HEIGHT = 2;
                              uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;
                              // MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
                              uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
                              //in beacon block body https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconblockbody
                              uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
                              // in beacon block header https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
                              uint256 internal constant SLOT_INDEX = 0;
                              uint256 internal constant PROPOSER_INDEX_INDEX = 1;
                              uint256 internal constant STATE_ROOT_INDEX = 3;
                              uint256 internal constant BODY_ROOT_INDEX = 4;
                              // in beacon state https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
                              uint256 internal constant HISTORICAL_BATCH_STATE_ROOT_INDEX = 1;
                              uint256 internal constant BEACON_STATE_SLOT_INDEX = 2;
                              uint256 internal constant LATEST_BLOCK_HEADER_ROOT_INDEX = 4;
                              uint256 internal constant BLOCK_ROOTS_INDEX = 5;
                              uint256 internal constant STATE_ROOTS_INDEX = 6;
                              uint256 internal constant HISTORICAL_ROOTS_INDEX = 7;
                              uint256 internal constant ETH_1_ROOT_INDEX = 8;
                              uint256 internal constant VALIDATOR_TREE_ROOT_INDEX = 11;
                              uint256 internal constant EXECUTION_PAYLOAD_HEADER_INDEX = 24;
                              uint256 internal constant HISTORICAL_SUMMARIES_INDEX = 27;
                              // in validator https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                              uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
                              uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
                              uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
                              uint256 internal constant VALIDATOR_SLASHED_INDEX = 3;
                              uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;
                              // in execution payload header
                              uint256 internal constant TIMESTAMP_INDEX = 9;
                              uint256 internal constant WITHDRAWALS_ROOT_INDEX = 14;
                              //in execution payload
                              uint256 internal constant WITHDRAWALS_INDEX = 14;
                              // in withdrawal
                              uint256 internal constant WITHDRAWAL_VALIDATOR_INDEX_INDEX = 1;
                              uint256 internal constant WITHDRAWAL_VALIDATOR_AMOUNT_INDEX = 3;
                              //In historicalBatch
                              uint256 internal constant HISTORICALBATCH_STATEROOTS_INDEX = 1;
                              //Misc Constants
                              /// @notice The number of slots each epoch in the beacon chain
                              uint64 internal constant SLOTS_PER_EPOCH = 32;
                              /// @notice The number of seconds in a slot in the beacon chain
                              uint64 internal constant SECONDS_PER_SLOT = 12;
                              /// @notice Number of seconds per epoch: 384 == 32 slots/epoch * 12 seconds/slot 
                              uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;
                              bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
                              /// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal
                              struct WithdrawalProof {
                                  bytes withdrawalProof;
                                  bytes slotProof;
                                  bytes executionPayloadProof;
                                  bytes timestampProof;
                                  bytes historicalSummaryBlockRootProof;
                                  uint64 blockRootIndex;
                                  uint64 historicalSummaryIndex;
                                  uint64 withdrawalIndex;
                                  bytes32 blockRoot;
                                  bytes32 slotRoot;
                                  bytes32 timestampRoot;
                                  bytes32 executionPayloadRoot;
                              }
                              /// @notice This struct contains the root and proof for verifying the state root against the oracle block root
                              struct StateRootProof {
                                  bytes32 beaconStateRoot;
                                  bytes proof;
                              }
                              /**
                               * @notice This function verifies merkle proofs of the fields of a certain validator against a beacon chain state root
                               * @param validatorIndex the index of the proven validator
                               * @param beaconStateRoot is the beacon chain state root to be proven against.
                               * @param validatorFieldsProof is the data used in proving the validator's fields
                               * @param validatorFields the claimed fields of the validator
                               */
                              function verifyValidatorFields(
                                  bytes32 beaconStateRoot,
                                  bytes32[] calldata validatorFields,
                                  bytes calldata validatorFieldsProof,
                                  uint40 validatorIndex
                              ) internal view {
                                  require(
                                      validatorFields.length == 2 ** VALIDATOR_FIELD_TREE_HEIGHT,
                                      "BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"
                                  );
                                  /**
                                   * Note: the length of the validator merkle proof is BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1.
                                   * There is an additional layer added by hashing the root with the length of the validator list
                                   */
                                  require(
                                      validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT),
                                      "BeaconChainProofs.verifyValidatorFields: Proof has incorrect length"
                                  );
                                  uint256 index = (VALIDATOR_TREE_ROOT_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex);
                                  // merkleize the validatorFields to get the leaf to prove
                                  bytes32 validatorRoot = EigenlayerMerkle.merkleizeSha256(validatorFields);
                                  // verify the proof of the validatorRoot against the beaconStateRoot
                                  require(
                                      EigenlayerMerkle.verifyInclusionSha256({
                                          proof: validatorFieldsProof,
                                          root: beaconStateRoot,
                                          leaf: validatorRoot,
                                          index: index
                                      }),
                                      "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof"
                                  );
                              }
                              /**
                               * @notice This function verifies the latestBlockHeader against the state root. the latestBlockHeader is
                               * a tracked in the beacon state.
                               * @param beaconStateRoot is the beacon chain state root to be proven against.
                               * @param stateRootProof is the provided merkle proof
                               * @param latestBlockRoot is hashtree root of the latest block header in the beacon state
                               */
                              function verifyStateRootAgainstLatestBlockRoot(
                                  bytes32 latestBlockRoot,
                                  bytes32 beaconStateRoot,
                                  bytes calldata stateRootProof
                              ) internal view {
                                  require(
                                      stateRootProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                      "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Proof has incorrect length"
                                  );
                                  //Next we verify the slot against the blockRoot
                                  require(
                                      EigenlayerMerkle.verifyInclusionSha256({
                                          proof: stateRootProof,
                                          root: latestBlockRoot,
                                          leaf: beaconStateRoot,
                                          index: STATE_ROOT_INDEX
                                      }),
                                      "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Invalid latest block header root merkle proof"
                                  );
                              }
                              /**
                               * @notice This function verifies the slot and the withdrawal fields for a given withdrawal
                               * @param withdrawalProof is the provided set of merkle proofs
                               * @param withdrawalFields is the serialized withdrawal container to be proven
                               */
                              function verifyWithdrawal(
                                  bytes32 beaconStateRoot,
                                  bytes32[] calldata withdrawalFields,
                                  WithdrawalProof calldata withdrawalProof
                              ) internal view {
                                  require(
                                      withdrawalFields.length == 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT,
                                      "BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length"
                                  );
                                  require(
                                      withdrawalProof.blockRootIndex < 2 ** BLOCK_ROOTS_TREE_HEIGHT,
                                      "BeaconChainProofs.verifyWithdrawal: blockRootIndex is too large"
                                  );
                                  require(
                                      withdrawalProof.withdrawalIndex < 2 ** WITHDRAWALS_TREE_HEIGHT,
                                      "BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
                                  );
                                  require(
                                      withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
                                      "BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
                                  );
                                  require(
                                      withdrawalProof.withdrawalProof.length ==
                                          32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT + WITHDRAWALS_TREE_HEIGHT + 1),
                                      "BeaconChainProofs.verifyWithdrawal: withdrawalProof has incorrect length"
                                  );
                                  require(
                                      withdrawalProof.executionPayloadProof.length ==
                                          32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
                                      "BeaconChainProofs.verifyWithdrawal: executionPayloadProof has incorrect length"
                                  );
                                  require(
                                      withdrawalProof.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                      "BeaconChainProofs.verifyWithdrawal: slotProof has incorrect length"
                                  );
                                  require(
                                      withdrawalProof.timestampProof.length == 32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT),
                                      "BeaconChainProofs.verifyWithdrawal: timestampProof has incorrect length"
                                  );
                                  require(
                                      withdrawalProof.historicalSummaryBlockRootProof.length ==
                                          32 *
                                              (BEACON_STATE_FIELD_TREE_HEIGHT +
                                                  (HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) +
                                                  1 +
                                                  (BLOCK_ROOTS_TREE_HEIGHT)),
                                      "BeaconChainProofs.verifyWithdrawal: historicalSummaryBlockRootProof has incorrect length"
                                  );
                                  /**
                                   * Note: Here, the "1" in "1 + (BLOCK_ROOTS_TREE_HEIGHT)" signifies that extra step of choosing the "block_root_summary" within the individual
                                   * "historical_summary". Everywhere else it signifies merkelize_with_mixin, where the length of an array is hashed with the root of the array,
                                   * but not here.
                                   */
                                  uint256 historicalBlockHeaderIndex = (HISTORICAL_SUMMARIES_INDEX <<
                                      ((HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) + 1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                      (uint256(withdrawalProof.historicalSummaryIndex) << (1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                      (BLOCK_SUMMARY_ROOT_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)) |
                                      uint256(withdrawalProof.blockRootIndex);
                                  require(
                                      EigenlayerMerkle.verifyInclusionSha256({
                                          proof: withdrawalProof.historicalSummaryBlockRootProof,
                                          root: beaconStateRoot,
                                          leaf: withdrawalProof.blockRoot,
                                          index: historicalBlockHeaderIndex
                                      }),
                                      "BeaconChainProofs.verifyWithdrawal: Invalid historicalsummary merkle proof"
                                  );
                                  //Next we verify the slot against the blockRoot
                                  require(
                                      EigenlayerMerkle.verifyInclusionSha256({
                                          proof: withdrawalProof.slotProof,
                                          root: withdrawalProof.blockRoot,
                                          leaf: withdrawalProof.slotRoot,
                                          index: SLOT_INDEX
                                      }),
                                      "BeaconChainProofs.verifyWithdrawal: Invalid slot merkle proof"
                                  );
                                  {
                                      // Next we verify the executionPayloadRoot against the blockRoot
                                      uint256 executionPayloadIndex = (BODY_ROOT_INDEX << (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)) |
                                          EXECUTION_PAYLOAD_INDEX;
                                      require(
                                          EigenlayerMerkle.verifyInclusionSha256({
                                              proof: withdrawalProof.executionPayloadProof,
                                              root: withdrawalProof.blockRoot,
                                              leaf: withdrawalProof.executionPayloadRoot,
                                              index: executionPayloadIndex
                                          }),
                                          "BeaconChainProofs.verifyWithdrawal: Invalid executionPayload merkle proof"
                                      );
                                  }
                                  // Next we verify the timestampRoot against the executionPayload root
                                  require(
                                      EigenlayerMerkle.verifyInclusionSha256({
                                          proof: withdrawalProof.timestampProof,
                                          root: withdrawalProof.executionPayloadRoot,
                                          leaf: withdrawalProof.timestampRoot,
                                          index: TIMESTAMP_INDEX
                                      }),
                                      "BeaconChainProofs.verifyWithdrawal: Invalid blockNumber merkle proof"
                                  );
                                  {
                                      /**
                                       * Next we verify the withdrawal fields against the blockRoot:
                                       * First we compute the withdrawal_index relative to the blockRoot by concatenating the indexes of all the
                                       * intermediate root indexes from the bottom of the sub trees (the withdrawal container) to the top, the blockRoot.
                                       * Then we calculate merkleize the withdrawalFields container to calculate the the withdrawalRoot.
                                       * Finally we verify the withdrawalRoot against the executionPayloadRoot.
                                       *
                                       *
                                       * Note: EigenlayerMerkleization of the withdrawals root tree uses EigenlayerMerkleizeWithMixin, i.e., the length of the array is hashed with the root of
                                       * the array.  Thus we shift the WITHDRAWALS_INDEX over by WITHDRAWALS_TREE_HEIGHT + 1 and not just WITHDRAWALS_TREE_HEIGHT.
                                       */
                                      uint256 withdrawalIndex = (WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1)) |
                                          uint256(withdrawalProof.withdrawalIndex);
                                      bytes32 withdrawalRoot = EigenlayerMerkle.merkleizeSha256(withdrawalFields);
                                      require(
                                          EigenlayerMerkle.verifyInclusionSha256({
                                              proof: withdrawalProof.withdrawalProof,
                                              root: withdrawalProof.executionPayloadRoot,
                                              leaf: withdrawalRoot,
                                              index: withdrawalIndex
                                          }),
                                          "BeaconChainProofs.verifyWithdrawal: Invalid withdrawal merkle proof"
                                      );
                                  }
                              }
                              /**
                               * @notice This function replicates the ssz hashing of a validator's pubkey, outlined below:
                               *  hh := ssz.NewHasher()
                               *  hh.PutBytes(validatorPubkey[:])
                               *  validatorPubkeyHash := hh.Hash()
                               *  hh.Reset()
                               */
                              function hashValidatorBLSPubkey(bytes memory validatorPubkey) internal pure returns (bytes32 pubkeyHash) {
                                  require(validatorPubkey.length == 48, "Input should be 48 bytes in length");
                                  return sha256(abi.encodePacked(validatorPubkey, bytes16(0)));
                              }
                              /**
                               * @dev Retrieve the withdrawal timestamp
                               */
                              function getWithdrawalTimestamp(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
                                  return
                                      Endian.fromLittleEndianUint64(withdrawalProof.timestampRoot);
                              }
                              /**
                               * @dev Converts the withdrawal's slot to an epoch
                               */
                              function getWithdrawalEpoch(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
                                  return
                                      Endian.fromLittleEndianUint64(withdrawalProof.slotRoot) / SLOTS_PER_EPOCH;
                              }
                              /**
                               * Indices for validator fields (refer to consensus specs):
                               * 0: pubkey
                               * 1: withdrawal credentials
                               * 2: effective balance
                               * 3: slashed?
                               * 4: activation elligibility epoch
                               * 5: activation epoch
                               * 6: exit epoch
                               * 7: withdrawable epoch
                               */
                              /**
                               * @dev Retrieves a validator's pubkey hash
                               */
                              function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) {
                                  return 
                                      validatorFields[VALIDATOR_PUBKEY_INDEX];
                              }
                              function getWithdrawalCredentials(bytes32[] memory validatorFields) internal pure returns (bytes32) {
                                  return
                                      validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
                              }
                              /**
                               * @dev Retrieves a validator's effective balance (in gwei)
                               */
                              function getEffectiveBalanceGwei(bytes32[] memory validatorFields) internal pure returns (uint64) {
                                  return 
                                      Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
                              }
                              /**
                               * @dev Retrieves a validator's withdrawable epoch
                               */
                              function getWithdrawableEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) {
                                  return 
                                      Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
                              }
                              /**
                               * Indices for withdrawal fields (refer to consensus specs):
                               * 0: withdrawal index
                               * 1: validator index
                               * 2: execution address
                               * 3: withdrawal amount
                               */
                              /**
                               * @dev Retrieves a withdrawal's validator index
                               */
                              function getValidatorIndex(bytes32[] memory withdrawalFields) internal pure returns (uint40) {
                                  return 
                                      uint40(Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_INDEX_INDEX]));
                              }
                              /**
                               * @dev Retrieves a withdrawal's withdrawal amount (in gwei)
                               */
                              function getWithdrawalAmountGwei(bytes32[] memory withdrawalFields) internal pure returns (uint64) {
                                  return
                                      Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
                              }
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity >=0.5.0;
                          import "./IStrategy.sol";
                          /**
                           * @title Abstract interface for a contract that helps structure the delegation relationship.
                           * @author Layr Labs, Inc.
                           * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                           * @notice The gas budget provided to this contract in calls from EigenLayer contracts is limited.
                           */
                          interface IDelegationTerms {
                              function payForService(IERC20 token, uint256 amount) external payable;
                              function onDelegationWithdrawn(
                                  address delegator,
                                  IStrategy[] memory stakerStrategyList,
                                  uint256[] memory stakerShares
                              ) external returns(bytes memory);
                              function onDelegationReceived(
                                  address delegator,
                                  IStrategy[] memory stakerStrategyList,
                                  uint256[] memory stakerShares
                              ) external returns(bytes memory);
                          }// SPDX-License-Identifier: BUSL-1.1
                          // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 EigenlayerMerkle {
                              /**
                               * @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. The tree is built assuming `leaf` is
                               * the 0 indexed `index`'th leaf from the bottom left of the tree.
                               *
                               * Note this is for a Merkle tree using the keccak/sha3 hash function
                               */
                              function verifyInclusionKeccak(
                                  bytes memory proof,
                                  bytes32 root,
                                  bytes32 leaf,
                                  uint256 index
                              ) internal pure returns (bool) {
                                  return processInclusionProofKeccak(proof, leaf, index) == 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. The tree is built assuming `leaf` is
                               * the 0 indexed `index`'th leaf from the bottom left of the tree.
                               *
                               * _Available since v4.4._
                               *
                               * Note this is for a Merkle tree using the keccak/sha3 hash function
                               */
                              function processInclusionProofKeccak(
                                  bytes memory proof,
                                  bytes32 leaf,
                                  uint256 index
                              ) internal pure returns (bytes32) {
                                  require(
                                      proof.length != 0 && proof.length % 32 == 0,
                                      "Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32"
                                  );
                                  bytes32 computedHash = leaf;
                                  for (uint256 i = 32; i <= proof.length; i += 32) {
                                      if (index % 2 == 0) {
                                          // if ith bit of index is 0, then computedHash is a left sibling
                                          assembly {
                                              mstore(0x00, computedHash)
                                              mstore(0x20, mload(add(proof, i)))
                                              computedHash := keccak256(0x00, 0x40)
                                              index := div(index, 2)
                                          }
                                      } else {
                                          // if ith bit of index is 1, then computedHash is a right sibling
                                          assembly {
                                              mstore(0x00, mload(add(proof, i)))
                                              mstore(0x20, computedHash)
                                              computedHash := keccak256(0x00, 0x40)
                                              index := div(index, 2)
                                          }
                                      }
                                  }
                                  return computedHash;
                              }
                              /**
                               * @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. The tree is built assuming `leaf` is
                               * the 0 indexed `index`'th leaf from the bottom left of the tree.
                               *
                               * Note this is for a Merkle tree using the sha256 hash function
                               */
                              function verifyInclusionSha256(
                                  bytes memory proof,
                                  bytes32 root,
                                  bytes32 leaf,
                                  uint256 index
                              ) internal view returns (bool) {
                                  return processInclusionProofSha256(proof, leaf, index) == 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. The tree is built assuming `leaf` is
                               * the 0 indexed `index`'th leaf from the bottom left of the tree.
                               *
                               * _Available since v4.4._
                               *
                               * Note this is for a Merkle tree using the sha256 hash function
                               */
                              function processInclusionProofSha256(
                                  bytes memory proof,
                                  bytes32 leaf,
                                  uint256 index
                              ) internal view returns (bytes32) {
                                  require(
                                      proof.length != 0 && proof.length % 32 == 0,
                                      "Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32"
                                  );
                                  bytes32[1] memory computedHash = [leaf];
                                  for (uint256 i = 32; i <= proof.length; i += 32) {
                                      if (index % 2 == 0) {
                                          // if ith bit of index is 0, then computedHash is a left sibling
                                          assembly {
                                              mstore(0x00, mload(computedHash))
                                              mstore(0x20, mload(add(proof, i)))
                                              if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
                                                  revert(0, 0)
                                              }
                                              index := div(index, 2)
                                          }
                                      } else {
                                          // if ith bit of index is 1, then computedHash is a right sibling
                                          assembly {
                                              mstore(0x00, mload(add(proof, i)))
                                              mstore(0x20, mload(computedHash))
                                              if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
                                                  revert(0, 0)
                                              }
                                              index := div(index, 2)
                                          }
                                      }
                                  }
                                  return computedHash[0];
                              }
                              /**
                               @notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function
                               @param leaves the leaves of the merkle tree
                               @return The computed Merkle root of the tree.
                               @dev A pre-condition to this function is that leaves.length is a power of two.  If not, the function will merkleize the inputs incorrectly.
                               */
                              function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) {
                                  //there are half as many nodes in the layer above the leaves
                                  uint256 numNodesInLayer = leaves.length / 2;
                                  //create a layer to store the internal nodes
                                  bytes32[] memory layer = new bytes32[](numNodesInLayer);
                                  //fill the layer with the pairwise hashes of the leaves
                                  for (uint256 i = 0; i < numNodesInLayer; i++) {
                                      layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
                                  }
                                  //the next layer above has half as many nodes
                                  numNodesInLayer /= 2;
                                  //while we haven't computed the root
                                  while (numNodesInLayer != 0) {
                                      //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
                                      for (uint256 i = 0; i < numNodesInLayer; i++) {
                                          layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
                                      }
                                      //the next layer above has half as many nodes
                                      numNodesInLayer /= 2;
                                  }
                                  //the first node in the layer is the root
                                  return layer[0];
                              }
                          }
                          // SPDX-License-Identifier: BUSL-1.1
                          pragma solidity ^0.8.0;
                          library Endian {
                              /**
                               * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
                               * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
                               * @return n The big endian-formatted uint64
                               * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
                               * through a right-shift/shr operation.
                               */
                              function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) {
                                  // the number needs to be stored in little-endian encoding (ie in bytes 0-8)
                                  n = uint64(uint256(lenum >> 192));
                                  return
                                      (n >> 56) |
                                      ((0x00FF000000000000 & n) >> 40) |
                                      ((0x0000FF0000000000 & n) >> 24) |
                                      ((0x000000FF00000000 & n) >> 8) |
                                      ((0x00000000FF000000 & n) << 8) |
                                      ((0x0000000000FF0000 & n) << 24) |
                                      ((0x000000000000FF00 & n) << 40) |
                                      ((0x00000000000000FF & n) << 56);
                              }
                          }
                          

                          File 6 of 6: EETH
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "@openzeppelin-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/utils/CountersUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol";
                          import "@openzeppelin-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol";
                          import "./interfaces/IeETH.sol";
                          import "./interfaces/ILiquidityPool.sol";
                          contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20PermitUpgradeable, IeETH {
                              using CountersUpgradeable for CountersUpgradeable.Counter;
                              ILiquidityPool public liquidityPool;
                              uint256 public totalShares;
                              mapping (address => uint256) public shares;
                              mapping (address => mapping (address => uint256)) public allowances;
                              mapping (address => CountersUpgradeable.Counter) private _nonces;
                              bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                              // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                              // invalidate the cached domain separator if the chain id changes.
                              bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
                              uint256 private immutable _CACHED_CHAIN_ID;
                              address private immutable _CACHED_THIS;
                              bytes32 private immutable _HASHED_NAME;
                              bytes32 private immutable _HASHED_VERSION;
                              bytes32 private immutable _TYPE_HASH;
                              event TransferShares( address indexed from, address indexed to, uint256 sharesValue);
                              // TODO: Figure our what `name` and `version` are for
                              constructor() { 
                                  bytes32 hashedName = keccak256("EETH");
                                  bytes32 hashedVersion = keccak256("1");
                                  bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                                  _HASHED_NAME = hashedName;
                                  _HASHED_VERSION = hashedVersion;
                                  _CACHED_CHAIN_ID = block.chainid;
                                  _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
                                  _CACHED_THIS = address(this);
                                  _TYPE_HASH = typeHash;
                                  _disableInitializers(); 
                              }
                              function initialize(address _liquidityPool) external initializer {
                                  require(_liquidityPool != address(0), "No zero addresses");
                                  
                                  __UUPSUpgradeable_init();
                                  __Ownable_init();
                                  liquidityPool = ILiquidityPool(_liquidityPool);
                              }
                              function mintShares(address _user, uint256 _share) external onlyPoolContract {
                                  shares[_user] += _share;
                                  totalShares += _share;
                                  emit Transfer(address(0), _user, liquidityPool.amountForShare(_share));
                                  emit TransferShares(address(0), _user, _share);
                              }
                              function burnShares(address _user, uint256 _share) external {
                                  require(msg.sender == address(liquidityPool) || msg.sender == _user, "Incorrect Caller");
                                  require(shares[_user] >= _share, "BURN_AMOUNT_EXCEEDS_BALANCE");
                                  shares[_user] -= _share;
                                  totalShares -= _share;
                                  emit Transfer(_user, address(0), liquidityPool.amountForShare(_share));
                                  emit TransferShares(_user, address(0), _share);
                              }
                              function transfer(address _recipient, uint256 _amount) external override(IeETH, IERC20Upgradeable) returns (bool) {
                                  _transfer(msg.sender, _recipient, _amount);
                                  return true;
                              }
                              function allowance(address _owner, address _spender) public view returns (uint256) {
                                  return allowances[_owner][_spender];
                              }
                              function approve(address _spender, uint256 _amount) external override(IeETH, IERC20Upgradeable) returns (bool) {
                                  _approve(msg.sender, _spender, _amount);
                                  return true;
                              }
                              function increaseAllowance(address _spender, uint256 _increaseAmount) external returns (bool) {
                                  address owner = msg.sender;
                                  uint256 currentAllowance = allowance(owner, _spender);
                                  _approve(owner, _spender,currentAllowance + _increaseAmount);
                                  return true;
                              }
                              function decreaseAllowance(address _spender, uint256 _decreaseAmount) external returns (bool) {
                                  address owner = msg.sender;
                                  uint256 currentAllowance = allowance(owner, _spender);
                                  require(currentAllowance >= _decreaseAmount, "ERC20: decreased allowance below zero");
                                  unchecked {
                                      _approve(owner, _spender, currentAllowance - _decreaseAmount);
                                  }
                                  return true;
                              }
                              function transferFrom(address _sender, address _recipient, uint256 _amount) external override(IeETH, IERC20Upgradeable) returns (bool) {
                                  uint256 currentAllowance = allowances[_sender][msg.sender];
                                  require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");
                                  unchecked {
                                      _approve(_sender, msg.sender, currentAllowance - _amount);
                                  }
                                  _transfer(_sender, _recipient, _amount);
                                  return true;
                              }
                              function permit(
                                  address owner,
                                  address spender,
                                  uint256 value,
                                  uint256 deadline,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              ) public virtual override(IeETH, IERC20PermitUpgradeable) {
                                  require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                                  bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
                                  bytes32 hash = _hashTypedDataV4(structHash);
                                  address signer = ECDSAUpgradeable.recover(hash, v, r, s);
                                  require(signer == owner, "ERC20Permit: invalid signature");
                                  _approve(owner, spender, value);
                              }
                              // [INTERNAL FUNCTIONS] 
                              function _transfer(address _sender, address _recipient, uint256 _amount) internal {
                                  uint256 _sharesToTransfer = liquidityPool.sharesForAmount(_amount);
                                  _transferShares(_sender, _recipient, _sharesToTransfer);
                                  emit Transfer(_sender, _recipient, _amount);
                              }
                              function _approve(address _owner, address _spender, uint256 _amount) internal {
                                  require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
                                  require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");
                                  allowances[_owner][_spender] = _amount;
                                  emit Approval(_owner, _spender, _amount);
                              }
                              function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
                                  require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
                                  require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");
                                  require(_sharesAmount <= shares[_sender], "TRANSFER_AMOUNT_EXCEEDS_BALANCE");
                                  shares[_sender] -= _sharesAmount;
                                  shares[_recipient] += _sharesAmount;
                                  emit TransferShares(_sender, _recipient, _sharesAmount);
                              }
                              function _authorizeUpgrade(
                                  address newImplementation
                              ) internal override onlyOwner {}
                              function _useNonce(address owner) internal virtual returns (uint256 current) {
                                  CountersUpgradeable.Counter storage nonce = _nonces[owner];
                                  current = nonce.current();
                                  nonce.increment();
                              }
                              // [GETTERS]
                              function name() public pure returns (string memory) { return "ether.fi ETH"; }
                              function symbol() public pure returns (string memory) { return "eETH"; }
                              function decimals() public pure returns (uint8) { return 18; }
                              function totalSupply() public view returns (uint256) {
                                  return liquidityPool.getTotalPooledEther();
                              }
                              function balanceOf(address _user) public view override(IeETH, IERC20Upgradeable) returns (uint256) {
                                  return liquidityPool.getTotalEtherClaimOf(_user);
                              }
                              function getImplementation() external view returns (address) {
                                  return _getImplementation();
                              }
                              function nonces(address owner) public view virtual override returns (uint256) {
                                  return _nonces[owner].current();
                              }
                              function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                                  return _domainSeparatorV4();
                              }
                              function _domainSeparatorV4() internal view returns (bytes32) {
                                  if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                                      return _CACHED_DOMAIN_SEPARATOR;
                                  } else {
                                      return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
                                  }
                              }
                              function _buildDomainSeparator(
                                  bytes32 typeHash,
                                  bytes32 nameHash,
                                  bytes32 versionHash
                              ) private view returns (bytes32) {
                                  return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                              }
                              function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                                  return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
                              }
                              // [MODIFIERS]
                              modifier onlyPoolContract() {
                                  require(msg.sender == address(liquidityPool), "Only pool contract function");
                                  _;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC20 standard as defined in the EIP.
                           */
                          interface IERC20Upgradeable {
                              /**
                               * @dev Emitted when `value` tokens are moved from one account (`from`) to
                               * another (`to`).
                               *
                               * Note that `value` may be zero.
                               */
                              event Transfer(address indexed from, address indexed to, uint256 value);
                              /**
                               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                               * a call to {approve}. `value` is the new allowance.
                               */
                              event Approval(address indexed owner, address indexed spender, uint256 value);
                              /**
                               * @dev Returns the amount of tokens in existence.
                               */
                              function totalSupply() external view returns (uint256);
                              /**
                               * @dev Returns the amount of tokens owned by `account`.
                               */
                              function balanceOf(address account) external view returns (uint256);
                              /**
                               * @dev Moves `amount` tokens from the caller's account to `to`.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transfer(address to, uint256 amount) external returns (bool);
                              /**
                               * @dev Returns the remaining number of tokens that `spender` will be
                               * allowed to spend on behalf of `owner` through {transferFrom}. This is
                               * zero by default.
                               *
                               * This value changes when {approve} or {transferFrom} are called.
                               */
                              function allowance(address owner, address spender) external view returns (uint256);
                              /**
                               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * IMPORTANT: Beware that changing an allowance with this method brings the risk
                               * that someone may use both the old and the new allowance by unfortunate
                               * transaction ordering. One possible solution to mitigate this race
                               * condition is to first reduce the spender's allowance to 0 and set the
                               * desired value afterwards:
                               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                               *
                               * Emits an {Approval} event.
                               */
                              function approve(address spender, uint256 amount) external returns (bool);
                              /**
                               * @dev Moves `amount` tokens from `from` to `to` using the
                               * allowance mechanism. `amount` is then deducted from the caller's
                               * allowance.
                               *
                               * Returns a boolean value indicating whether the operation succeeded.
                               *
                               * Emits a {Transfer} event.
                               */
                              function transferFrom(
                                  address from,
                                  address to,
                                  uint256 amount
                              ) external returns (bool);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
                          pragma solidity ^0.8.0;
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
                          import "./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.
                           *
                           * _Available since v4.1._
                           */
                          abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
                              function __UUPSUpgradeable_init() internal onlyInitializing {
                              }
                              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                              }
                              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
                              address private immutable __self = address(this);
                              /**
                               * @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() {
                                  require(address(this) != __self, "Function must be called through delegatecall");
                                  require(_getImplementation() == __self, "Function must be called through active proxy");
                                  _;
                              }
                              /**
                               * @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() {
                                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                                  _;
                              }
                              /**
                               * @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 override notDelegated returns (bytes32) {
                                  return _IMPLEMENTATION_SLOT;
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeTo(address newImplementation) external virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
                              }
                              /**
                               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                               * encoded in `data`.
                               *
                               * Calls {_authorizeUpgrade}.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                                  _authorizeUpgrade(newImplementation);
                                  _upgradeToAndCallUUPS(newImplementation, data, true);
                              }
                              /**
                               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                               * {upgradeTo} and {upgradeToAndCall}.
                               *
                               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                               *
                               * ```solidity
                               * function _authorizeUpgrade(address) internal override onlyOwner {}
                               * ```
                               */
                              function _authorizeUpgrade(address newImplementation) 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: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                          pragma solidity ^0.8.0;
                          import "../utils/ContextUpgradeable.sol";
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Contract module which provides a basic access control mechanism, where
                           * there is an account (an owner) that can be granted exclusive access to
                           * specific functions.
                           *
                           * By default, the owner account will be the one that deploys the contract. This
                           * can later be changed with {transferOwnership}.
                           *
                           * This module is used through inheritance. It will make available the modifier
                           * `onlyOwner`, which can be applied to your functions to restrict their use to
                           * the owner.
                           */
                          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                              address private _owner;
                              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                              /**
                               * @dev Initializes the contract setting the deployer as the initial owner.
                               */
                              function __Ownable_init() internal onlyInitializing {
                                  __Ownable_init_unchained();
                              }
                              function __Ownable_init_unchained() internal onlyInitializing {
                                  _transferOwnership(_msgSender());
                              }
                              /**
                               * @dev Throws if called by any account other than the owner.
                               */
                              modifier onlyOwner() {
                                  _checkOwner();
                                  _;
                              }
                              /**
                               * @dev Returns the address of the current owner.
                               */
                              function owner() public view virtual returns (address) {
                                  return _owner;
                              }
                              /**
                               * @dev Throws if the sender is not the owner.
                               */
                              function _checkOwner() internal view virtual {
                                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
                              }
                              /**
                               * @dev Leaves the contract without owner. It will not be possible to call
                               * `onlyOwner` functions anymore. Can only be called by the current owner.
                               *
                               * NOTE: Renouncing ownership will leave the contract without an owner,
                               * thereby removing any functionality that is only available to the owner.
                               */
                              function renounceOwnership() public virtual onlyOwner {
                                  _transferOwnership(address(0));
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Can only be called by the current owner.
                               */
                              function transferOwnership(address newOwner) public virtual onlyOwner {
                                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                                  _transferOwnership(newOwner);
                              }
                              /**
                               * @dev Transfers ownership of the contract to a new account (`newOwner`).
                               * Internal function without access restriction.
                               */
                              function _transferOwnership(address newOwner) internal virtual {
                                  address oldOwner = _owner;
                                  _owner = newOwner;
                                  emit OwnershipTransferred(oldOwner, newOwner);
                              }
                              /**
                               * @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[49] private __gap;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
                          pragma solidity ^0.8.0;
                          import "./ECDSAUpgradeable.sol";
                          import "../../proxy/utils/Initializable.sol";
                          /**
                           * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
                           *
                           * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
                           * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
                           * they need in their contracts using a combination of `abi.encode` and `keccak256`.
                           *
                           * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
                           * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
                           * ({_hashTypedDataV4}).
                           *
                           * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
                           * the chain id to protect against replay attacks on an eventual fork of the chain.
                           *
                           * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
                           * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
                           *
                           * _Available since v3.4._
                           *
                           * @custom:storage-size 52
                           */
                          abstract contract EIP712Upgradeable is Initializable {
                              /* solhint-disable var-name-mixedcase */
                              bytes32 private _HASHED_NAME;
                              bytes32 private _HASHED_VERSION;
                              bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                              /* solhint-enable var-name-mixedcase */
                              /**
                               * @dev Initializes the domain separator and parameter caches.
                               *
                               * The meaning of `name` and `version` is specified in
                               * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                               *
                               * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                               * - `version`: the current major version of the signing domain.
                               *
                               * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                               * contract upgrade].
                               */
                              function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                                  __EIP712_init_unchained(name, version);
                              }
                              function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                                  bytes32 hashedName = keccak256(bytes(name));
                                  bytes32 hashedVersion = keccak256(bytes(version));
                                  _HASHED_NAME = hashedName;
                                  _HASHED_VERSION = hashedVersion;
                              }
                              /**
                               * @dev Returns the domain separator for the current chain.
                               */
                              function _domainSeparatorV4() internal view returns (bytes32) {
                                  return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                              }
                              function _buildDomainSeparator(
                                  bytes32 typeHash,
                                  bytes32 nameHash,
                                  bytes32 versionHash
                              ) private view returns (bytes32) {
                                  return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                              }
                              /**
                               * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                               * function returns the hash of the fully encoded EIP712 message for this domain.
                               *
                               * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                               *
                               * ```solidity
                               * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                               *     keccak256("Mail(address to,string contents)"),
                               *     mailTo,
                               *     keccak256(bytes(mailContents))
                               * )));
                               * address signer = ECDSA.recover(digest, signature);
                               * ```
                               */
                              function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                                  return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
                              }
                              /**
                               * @dev The hash of the name parameter for the EIP712 domain.
                               *
                               * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                               * are a concern.
                               */
                              function _EIP712NameHash() internal virtual view returns (bytes32) {
                                  return _HASHED_NAME;
                              }
                              /**
                               * @dev The hash of the version parameter for the EIP712 domain.
                               *
                               * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                               * are a concern.
                               */
                              function _EIP712VersionHash() internal virtual view returns (bytes32) {
                                  return _HASHED_VERSION;
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @title Counters
                           * @author Matt Condon (@shrugs)
                           * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
                           * of elements in a mapping, issuing ERC721 ids, or counting request ids.
                           *
                           * Include with `using Counters for Counters.Counter;`
                           */
                          library CountersUpgradeable {
                              struct Counter {
                                  // This variable should never be directly accessed by users of the library: interactions must be restricted to
                                  // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                                  // this feature: see https://github.com/ethereum/solidity/issues/4637
                                  uint256 _value; // default: 0
                              }
                              function current(Counter storage counter) internal view returns (uint256) {
                                  return counter._value;
                              }
                              function increment(Counter storage counter) internal {
                                  unchecked {
                                      counter._value += 1;
                                  }
                              }
                              function decrement(Counter storage counter) internal {
                                  uint256 value = counter._value;
                                  require(value > 0, "Counter: decrement overflow");
                                  unchecked {
                                      counter._value = value - 1;
                                  }
                              }
                              function reset(Counter storage counter) internal {
                                  counter._value = 0;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                           *
                           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                           * need to send a transaction, and thus is not required to hold Ether at all.
                           */
                          interface IERC20PermitUpgradeable {
                              /**
                               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                               * given ``owner``'s signed approval.
                               *
                               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                               * ordering also apply here.
                               *
                               * Emits an {Approval} event.
                               *
                               * Requirements:
                               *
                               * - `spender` cannot be the zero address.
                               * - `deadline` must be a timestamp in the future.
                               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                               * over the EIP712-formatted function arguments.
                               * - the signature must use ``owner``'s current nonce (see {nonces}).
                               *
                               * For more information on the signature format, see the
                               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                               * section].
                               */
                              function permit(
                                  address owner,
                                  address spender,
                                  uint256 value,
                                  uint256 deadline,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              ) external;
                              /**
                               * @dev Returns the current nonce for `owner`. This value must be
                               * included whenever a signature is generated for {permit}.
                               *
                               * Every successful call to {permit} increases ``owner``'s nonce by one. This
                               * prevents a signature from being used multiple times.
                               */
                              function nonces(address owner) external view returns (uint256);
                              /**
                               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                               */
                              // solhint-disable-next-line func-name-mixedcase
                              function DOMAIN_SEPARATOR() external view returns (bytes32);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
                          pragma solidity ^0.8.0;
                          import "../StringsUpgradeable.sol";
                          /**
                           * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
                           *
                           * These functions can be used to verify that a message was signed by the holder
                           * of the private keys of a given address.
                           */
                          library ECDSAUpgradeable {
                              enum RecoverError {
                                  NoError,
                                  InvalidSignature,
                                  InvalidSignatureLength,
                                  InvalidSignatureS,
                                  InvalidSignatureV // Deprecated in v4.8
                              }
                              function _throwError(RecoverError error) private pure {
                                  if (error == RecoverError.NoError) {
                                      return; // no error: do nothing
                                  } else if (error == RecoverError.InvalidSignature) {
                                      revert("ECDSA: invalid signature");
                                  } else if (error == RecoverError.InvalidSignatureLength) {
                                      revert("ECDSA: invalid signature length");
                                  } else if (error == RecoverError.InvalidSignatureS) {
                                      revert("ECDSA: invalid signature 's' value");
                                  }
                              }
                              /**
                               * @dev Returns the address that signed a hashed message (`hash`) with
                               * `signature` or error string. This address can then be used for verification purposes.
                               *
                               * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                               * this function rejects them by requiring the `s` value to be in the lower
                               * half order, and the `v` value to be either 27 or 28.
                               *
                               * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                               * verification to be secure: it is possible to craft signatures that
                               * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                               * this is by receiving a hash of the original message (which may otherwise
                               * be too long), and then calling {toEthSignedMessageHash} on it.
                               *
                               * Documentation for signature generation:
                               * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                               * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                               *
                               * _Available since v4.3._
                               */
                              function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                                  if (signature.length == 65) {
                                      bytes32 r;
                                      bytes32 s;
                                      uint8 v;
                                      // ecrecover takes the signature parameters, and the only way to get them
                                      // currently is to use assembly.
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          r := mload(add(signature, 0x20))
                                          s := mload(add(signature, 0x40))
                                          v := byte(0, mload(add(signature, 0x60)))
                                      }
                                      return tryRecover(hash, v, r, s);
                                  } else {
                                      return (address(0), RecoverError.InvalidSignatureLength);
                                  }
                              }
                              /**
                               * @dev Returns the address that signed a hashed message (`hash`) with
                               * `signature`. This address can then be used for verification purposes.
                               *
                               * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                               * this function rejects them by requiring the `s` value to be in the lower
                               * half order, and the `v` value to be either 27 or 28.
                               *
                               * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                               * verification to be secure: it is possible to craft signatures that
                               * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                               * this is by receiving a hash of the original message (which may otherwise
                               * be too long), and then calling {toEthSignedMessageHash} on it.
                               */
                              function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                                  (address recovered, RecoverError error) = tryRecover(hash, signature);
                                  _throwError(error);
                                  return recovered;
                              }
                              /**
                               * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                               *
                               * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                               *
                               * _Available since v4.3._
                               */
                              function tryRecover(
                                  bytes32 hash,
                                  bytes32 r,
                                  bytes32 vs
                              ) internal pure returns (address, RecoverError) {
                                  bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                                  uint8 v = uint8((uint256(vs) >> 255) + 27);
                                  return tryRecover(hash, v, r, s);
                              }
                              /**
                               * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                               *
                               * _Available since v4.2._
                               */
                              function recover(
                                  bytes32 hash,
                                  bytes32 r,
                                  bytes32 vs
                              ) internal pure returns (address) {
                                  (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                                  _throwError(error);
                                  return recovered;
                              }
                              /**
                               * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                               * `r` and `s` signature fields separately.
                               *
                               * _Available since v4.3._
                               */
                              function tryRecover(
                                  bytes32 hash,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              ) internal pure returns (address, RecoverError) {
                                  // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                                  // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                                  // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                                  // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                                  //
                                  // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                                  // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                                  // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                                  // these malleable signatures as well.
                                  if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                                      return (address(0), RecoverError.InvalidSignatureS);
                                  }
                                  // If the signature is valid (and not malleable), return the signer address
                                  address signer = ecrecover(hash, v, r, s);
                                  if (signer == address(0)) {
                                      return (address(0), RecoverError.InvalidSignature);
                                  }
                                  return (signer, RecoverError.NoError);
                              }
                              /**
                               * @dev Overload of {ECDSA-recover} that receives the `v`,
                               * `r` and `s` signature fields separately.
                               */
                              function recover(
                                  bytes32 hash,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              ) internal pure returns (address) {
                                  (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                                  _throwError(error);
                                  return recovered;
                              }
                              /**
                               * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                               * produces hash corresponding to the one signed with the
                               * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                               * JSON-RPC method as part of EIP-191.
                               *
                               * See {recover}.
                               */
                              function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                                  // 32 is the length in bytes of hash,
                                  // enforced by the type signature above
                                  return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
                          32", hash));
                              }
                              /**
                               * @dev Returns an Ethereum Signed Message, created from `s`. This
                               * produces hash corresponding to the one signed with the
                               * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                               * JSON-RPC method as part of EIP-191.
                               *
                               * See {recover}.
                               */
                              function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                                  return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
                          ", StringsUpgradeable.toString(s.length), s));
                              }
                              /**
                               * @dev Returns an Ethereum Signed Typed Data, created from a
                               * `domainSeparator` and a `structHash`. This produces hash corresponding
                               * to the one signed with the
                               * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                               * JSON-RPC method as part of EIP-712.
                               *
                               * See {recover}.
                               */
                              function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                                  return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          interface IeETH {
                              function name() external pure returns (string memory);
                              function symbol() external pure returns (string memory);
                              function decimals() external pure returns (uint8);
                              function totalShares() external view returns (uint256);
                              function shares(address _user) external view returns (uint256);
                              function balanceOf(address _user) external view returns (uint256);
                              function initialize(address _liquidityPool) external;
                              function mintShares(address _user, uint256 _share) external;
                              function burnShares(address _user, uint256 _share) external;
                              function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool);
                              function transfer(address _recipient, uint256 _amount) external returns (bool);
                              function approve(address _spender, uint256 _amount) external returns (bool);
                              function increaseAllowance(address _spender, uint256 _increaseAmount) external returns (bool);
                              function decreaseAllowance(address _spender, uint256 _decreaseAmount) external returns (bool);
                              function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./IStakingManager.sol";
                          interface ILiquidityPool {
                              struct PermitInput {
                                  uint256 value;
                                  uint256 deadline;
                                  uint8 v;
                                  bytes32 r;
                                  bytes32 s;
                              } 
                              enum SourceOfFunds {
                                  UNDEFINED,
                                  EETH,
                                  ETHER_FAN,
                                  DELEGATED_STAKING
                              }
                              struct FundStatistics {
                                  uint32 numberOfValidators;
                                  uint32 targetWeight;
                              }
                              // Necessary to preserve "statelessness" of dutyForWeek().
                              // Handles case where new users join/leave holder list during an active slot
                              struct HoldersUpdate {
                                  uint32 timestamp;
                                  uint32 startOfSlotNumOwners;
                              }
                              struct BnftHolder {
                                  address holder;
                                  uint32 timestamp;
                              }
                              struct BnftHoldersIndex {
                                  bool registered;
                                  uint32 index;
                              }
                              function initialize(address _eEthAddress, address _stakingManagerAddress, address _nodesManagerAddress, address _membershipManagerAddress, address _tNftAddress) external;
                              function numPendingDeposits() external view returns (uint32);
                              function totalValueOutOfLp() external view returns (uint128);
                              function totalValueInLp() external view returns (uint128);
                              function getTotalEtherClaimOf(address _user) external view returns (uint256);
                              function getTotalPooledEther() external view returns (uint256);
                              function sharesForAmount(uint256 _amount) external view returns (uint256);
                              function sharesForWithdrawalAmount(uint256 _amount) external view returns (uint256);
                              function amountForShare(uint256 _share) external view returns (uint256);
                              function deposit() external payable returns (uint256);
                              function deposit(address _referral) external payable returns (uint256);
                              function deposit(address _user, address _referral) external payable returns (uint256);
                              function withdraw(address _recipient, uint256 _amount) external returns (uint256);
                              function requestWithdraw(address recipient, uint256 amount) external returns (uint256);
                              function requestWithdrawWithPermit(address _owner, uint256 _amount, PermitInput calldata _permit) external returns (uint256);
                              function requestMembershipNFTWithdraw(address recipient, uint256 amount, uint256 fee) external returns (uint256);
                              function batchDepositAsBnftHolder(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external payable returns (uint256[] memory);
                              function batchRegisterAsBnftHolder(bytes32 _depositRoot, uint256[] calldata _validatorIds, IStakingManager.DepositData[] calldata _registerValidatorDepositData, bytes32[] calldata _depositDataRootApproval, bytes[] calldata _signaturesForApprovalDeposit) external;
                              function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubKey, bytes[] calldata _signature) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function sendExitRequests(uint256[] calldata _validatorIds) external;
                              function rebase(int128 _accruedRewards) external;
                              function addEthAmountLockedForWithdrawal(uint128 _amount) external;
                              
                              function setStakingTargetWeights(uint32 _eEthWeight, uint32 _etherFanWeight) external;
                              function updateAdmin(address _newAdmin, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                              
                              function decreaseSourceOfFundsValidators(uint32 numberOfEethValidators, uint32 numberOfEtherFanValidators) external;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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 IERC1822ProxiableUpgradeable {
                              /**
                               * @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 v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                          pragma solidity ^0.8.2;
                          import "../beacon/IBeaconUpgradeable.sol";
                          import "../../interfaces/draft-IERC1822Upgradeable.sol";
                          import "../../utils/AddressUpgradeable.sol";
                          import "../../utils/StorageSlotUpgradeable.sol";
                          import "../utils/Initializable.sol";
                          /**
                           * @dev This abstract contract provides getters and event emitting update functions for
                           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                           *
                           * _Available since v4.1._
                           *
                           * @custom:oz-upgrades-unsafe-allow delegatecall
                           */
                          abstract contract ERC1967UpgradeUpgradeable is Initializable {
                              function __ERC1967Upgrade_init() internal onlyInitializing {
                              }
                              function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
                              }
                              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                              /**
                               * @dev Storage slot with the address of the current implementation.
                               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                              /**
                               * @dev Emitted when the implementation is upgraded.
                               */
                              event Upgraded(address indexed implementation);
                              /**
                               * @dev Returns the current implementation address.
                               */
                              function _getImplementation() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 implementation slot.
                               */
                              function _setImplementation(address newImplementation) private {
                                  require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                                  StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                              }
                              /**
                               * @dev Perform implementation upgrade
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeTo(address newImplementation) internal {
                                  _setImplementation(newImplementation);
                                  emit Upgraded(newImplementation);
                              }
                              /**
                               * @dev Perform implementation upgrade with additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCall(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _upgradeTo(newImplementation);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(newImplementation, data);
                                  }
                              }
                              /**
                               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                               *
                               * Emits an {Upgraded} event.
                               */
                              function _upgradeToAndCallUUPS(
                                  address newImplementation,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                                  if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                                      _setImplementation(newImplementation);
                                  } else {
                                      try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                                      } catch {
                                          revert("ERC1967Upgrade: new implementation is not UUPS");
                                      }
                                      _upgradeToAndCall(newImplementation, data, forceCall);
                                  }
                              }
                              /**
                               * @dev Storage slot with the admin of the contract.
                               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                               * validated in the constructor.
                               */
                              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                              /**
                               * @dev Emitted when the admin account has changed.
                               */
                              event AdminChanged(address previousAdmin, address newAdmin);
                              /**
                               * @dev Returns the current admin.
                               */
                              function _getAdmin() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
                              }
                              /**
                               * @dev Stores a new address in the EIP1967 admin slot.
                               */
                              function _setAdmin(address newAdmin) private {
                                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                                  StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                              }
                              /**
                               * @dev Changes the admin of the proxy.
                               *
                               * Emits an {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 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                               */
                              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                              /**
                               * @dev Emitted when the beacon is upgraded.
                               */
                              event BeaconUpgraded(address indexed beacon);
                              /**
                               * @dev Returns the current beacon.
                               */
                              function _getBeacon() internal view returns (address) {
                                  return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
                              }
                              /**
                               * @dev Stores a new beacon in the EIP1967 beacon slot.
                               */
                              function _setBeacon(address newBeacon) private {
                                  require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                                  require(
                                      AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                                      "ERC1967: beacon implementation is not a contract"
                                  );
                                  StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                              }
                              /**
                               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                               *
                               * Emits a {BeaconUpgraded} event.
                               */
                              function _upgradeBeaconToAndCall(
                                  address newBeacon,
                                  bytes memory data,
                                  bool forceCall
                              ) internal {
                                  _setBeacon(newBeacon);
                                  emit BeaconUpgraded(newBeacon);
                                  if (data.length > 0 || forceCall) {
                                      _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                                  }
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a delegate call.
                               *
                               * _Available since v3.4._
                               */
                              function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                                  require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (bool success, bytes memory returndata) = target.delegatecall(data);
                                  return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
                          pragma solidity ^0.8.2;
                          import "../../utils/AddressUpgradeable.sol";
                          /**
                           * @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]
                           * ```
                           * 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 Indicates that the contract has been initialized.
                               * @custom:oz-retyped-from bool
                               */
                              uint8 private _initialized;
                              /**
                               * @dev Indicates that the contract is in the process of being initialized.
                               */
                              bool private _initializing;
                              /**
                               * @dev Triggered when the contract has been initialized or reinitialized.
                               */
                              event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
                               * constructor.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier initializer() {
                                  bool isTopLevelCall = !_initializing;
                                  require(
                                      (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                                      "Initializable: contract is already initialized"
                                  );
                                  _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 255 will prevent any future reinitialization.
                               *
                               * Emits an {Initialized} event.
                               */
                              modifier reinitializer(uint8 version) {
                                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                                  _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() {
                                  require(_initializing, "Initializable: contract is not initializing");
                                  _;
                              }
                              /**
                               * @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 {
                                  require(!_initializing, "Initializable: contract is initializing");
                                  if (_initialized < type(uint8).max) {
                                      _initialized = type(uint8).max;
                                      emit Initialized(type(uint8).max);
                                  }
                              }
                              /**
                               * @dev Returns the highest version that has been initialized. See {reinitializer}.
                               */
                              function _getInitializedVersion() internal view returns (uint8) {
                                  return _initialized;
                              }
                              /**
                               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                               */
                              function _isInitializing() internal view returns (bool) {
                                  return _initializing;
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                          pragma solidity ^0.8.0;
                          import "../proxy/utils/Initializable.sol";
                          /**
                           * @dev Provides information about the current execution context, including the
                           * sender of the transaction and its data. While these are generally available
                           * via msg.sender and msg.data, they should not be accessed in such a direct
                           * manner, since when dealing with meta-transactions the account sending and
                           * paying for execution may not be the actual sender (as far as an application
                           * is concerned).
                           *
                           * This contract is only required for intermediate, library-like contracts.
                           */
                          abstract contract ContextUpgradeable is Initializable {
                              function __Context_init() internal onlyInitializing {
                              }
                              function __Context_init_unchained() internal onlyInitializing {
                              }
                              function _msgSender() internal view virtual returns (address) {
                                  return msg.sender;
                              }
                              function _msgData() internal view virtual returns (bytes calldata) {
                                  return msg.data;
                              }
                              /**
                               * @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: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
                          pragma solidity ^0.8.0;
                          import "./math/MathUpgradeable.sol";
                          /**
                           * @dev String operations.
                           */
                          library StringsUpgradeable {
                              bytes16 private constant _SYMBOLS = "0123456789abcdef";
                              uint8 private constant _ADDRESS_LENGTH = 20;
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                               */
                              function toString(uint256 value) internal pure returns (string memory) {
                                  unchecked {
                                      uint256 length = MathUpgradeable.log10(value) + 1;
                                      string memory buffer = new string(length);
                                      uint256 ptr;
                                      /// @solidity memory-safe-assembly
                                      assembly {
                                          ptr := add(buffer, add(32, length))
                                      }
                                      while (true) {
                                          ptr--;
                                          /// @solidity memory-safe-assembly
                                          assembly {
                                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                                          }
                                          value /= 10;
                                          if (value == 0) break;
                                      }
                                      return buffer;
                                  }
                              }
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                               */
                              function toHexString(uint256 value) internal pure returns (string memory) {
                                  unchecked {
                                      return toHexString(value, MathUpgradeable.log256(value) + 1);
                                  }
                              }
                              /**
                               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                               */
                              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                                  bytes memory buffer = new bytes(2 * length + 2);
                                  buffer[0] = "0";
                                  buffer[1] = "x";
                                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                                      buffer[i] = _SYMBOLS[value & 0xf];
                                      value >>= 4;
                                  }
                                  require(value == 0, "Strings: hex length insufficient");
                                  return string(buffer);
                              }
                              /**
                               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                               */
                              function toHexString(address addr) internal pure returns (string memory) {
                                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          pragma solidity 0.8.13;
                          import "./ILiquidityPool.sol";
                          interface IStakingManager {
                              struct DepositData {
                                  bytes publicKey;
                                  bytes signature;
                                  bytes32 depositDataRoot;
                                  string ipfsHashForEncryptedValidatorKey;
                              }
                              struct StakerInfo {
                                  address staker;
                                  ILiquidityPool.SourceOfFunds sourceOfFund;
                              }
                              function bidIdToStaker(uint256 id) external view returns (address);
                              function getEtherFiNodeBeacon() external view returns (address);
                              function initialize(address _auctionAddress, address _depositContractAddress) external;
                              function setEtherFiNodesManagerAddress(address _managerAddress) external;
                              function setLiquidityPoolAddress(address _liquidityPoolAddress) external;
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, address _staker, ILiquidityPool.SourceOfFunds source, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, bool _enableRestaking) external payable returns (uint256[] memory);
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, DepositData[] calldata _depositData) external;
                              function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, address _bNftRecipient, address _tNftRecipient, DepositData[] calldata _depositData, address _user) external;
                              function batchApproveRegistration(uint256[] memory _validatorId, bytes[] calldata _pubKey, bytes[] calldata _signature, bytes32[] calldata _depositDataRootApproval) external;
                              function batchCancelDeposit(uint256[] calldata _validatorIds) external;
                              function batchCancelDepositAsBnftHolder(uint256[] calldata _validatorIds, address _caller) external;
                              function updateAdmin(address _address, bool _isAdmin) external;
                              function pauseContract() external;
                              function unPauseContract() external;
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev This is the interface that {BeaconProxy} expects of its beacon.
                           */
                          interface IBeaconUpgradeable {
                              /**
                               * @dev Must return an address that can be used as a delegate call target.
                               *
                               * {BeaconProxy} will check that this address is a contract.
                               */
                              function implementation() external view returns (address);
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
                          pragma solidity ^0.8.1;
                          /**
                           * @dev Collection of functions related to the address type
                           */
                          library AddressUpgradeable {
                              /**
                               * @dev Returns true if `account` is a contract.
                               *
                               * [IMPORTANT]
                               * ====
                               * It is unsafe to assume that an address for which this function returns
                               * false is an externally-owned account (EOA) and not a contract.
                               *
                               * Among others, `isContract` will return false for the following
                               * types of addresses:
                               *
                               *  - an externally-owned account
                               *  - a contract in construction
                               *  - an address where a contract will be created
                               *  - an address where a contract lived, but was destroyed
                               * ====
                               *
                               * [IMPORTANT]
                               * ====
                               * You shouldn't rely on `isContract` to protect against flash loan attacks!
                               *
                               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                               * constructor.
                               * ====
                               */
                              function isContract(address account) internal view returns (bool) {
                                  // This method relies on extcodesize/address.code.length, which returns 0
                                  // for contracts in construction, since the code is only stored at the end
                                  // of the constructor execution.
                                  return account.code.length > 0;
                              }
                              /**
                               * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                               */
                              function sendValue(address payable recipient, uint256 amount) internal {
                                  require(address(this).balance >= amount, "Address: insufficient balance");
                                  (bool success, ) = recipient.call{value: amount}("");
                                  require(success, "Address: unable to send value, recipient may have reverted");
                              }
                              /**
                               * @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, it is bubbled up by this
                               * function (like regular Solidity function calls).
                               *
                               * 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.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                               * `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, 0, errorMessage);
                              }
                              /**
                               * @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`.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value
                              ) internal returns (bytes memory) {
                                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                               * with `errorMessage` as a fallback revert reason when `target` reverts.
                               *
                               * _Available since v3.1._
                               */
                              function functionCallWithValue(
                                  address target,
                                  bytes memory data,
                                  uint256 value,
                                  string memory errorMessage
                              ) internal returns (bytes memory) {
                                  require(address(this).balance >= value, "Address: insufficient balance for call");
                                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                  return functionStaticCall(target, data, "Address: low-level static call failed");
                              }
                              /**
                               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                               * but performing a static call.
                               *
                               * _Available since v3.3._
                               */
                              function functionStaticCall(
                                  address target,
                                  bytes memory data,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  (bool success, bytes memory returndata) = target.staticcall(data);
                                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                              }
                              /**
                               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                               *
                               * _Available since v4.8._
                               */
                              function verifyCallResultFromTarget(
                                  address target,
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal view returns (bytes memory) {
                                  if (success) {
                                      if (returndata.length == 0) {
                                          // only check isContract if the call was successful and the return data is empty
                                          // otherwise we already know that it was a contract
                                          require(isContract(target), "Address: call to non-contract");
                                      }
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              /**
                               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                               * revert reason or using the provided one.
                               *
                               * _Available since v4.3._
                               */
                              function verifyCallResult(
                                  bool success,
                                  bytes memory returndata,
                                  string memory errorMessage
                              ) internal pure returns (bytes memory) {
                                  if (success) {
                                      return returndata;
                                  } else {
                                      _revert(returndata, errorMessage);
                                  }
                              }
                              function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @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:
                           * ```
                           * 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                           *     }
                           * }
                           * ```
                           *
                           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                           */
                          library StorageSlotUpgradeable {
                              struct AddressSlot {
                                  address value;
                              }
                              struct BooleanSlot {
                                  bool value;
                              }
                              struct Bytes32Slot {
                                  bytes32 value;
                              }
                              struct Uint256Slot {
                                  uint256 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
                                  }
                              }
                          }
                          // SPDX-License-Identifier: MIT
                          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
                          pragma solidity ^0.8.0;
                          /**
                           * @dev Standard math utilities missing in the Solidity language.
                           */
                          library MathUpgradeable {
                              enum Rounding {
                                  Down, // Toward negative infinity
                                  Up, // Toward infinity
                                  Zero // Toward zero
                              }
                              /**
                               * @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 up instead
                               * of rounding down.
                               */
                              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                                  // (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; // Least significant 256 bits of the product
                                      uint256 prod1; // Most significant 256 bits of the product
                                      assembly {
                                          let mm := mulmod(x, y, not(0))
                                          prod0 := mul(x, y)
                                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                                      }
                                      // Handle non-overflow cases, 256 by 256 division.
                                      if (prod1 == 0) {
                                          return prod0 / denominator;
                                      }
                                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                                      require(denominator > prod1);
                                      ///////////////////////////////////////////////
                                      // 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.
                                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                                      uint256 twos = denominator & (~denominator + 1);
                                      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 (rounding == Rounding.Up && 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 down.
                               *
                               * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 2, rounded down, of a positive value.
                               * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 10, rounded down, of a positive value.
                               * 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 + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                                  }
                              }
                              /**
                               * @dev Return the log in base 256, rounded down, of a positive value.
                               * 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 10, 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 + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                                  }
                              }
                          }