ETH Price: $2,060.13 (-0.74%)

Transaction Decoder

Block:
19541807 at Mar-29-2024 07:43:47 PM +UTC
Transaction Fee:
0.00518311274302426 ETH $10.68
Gas Used:
163,340 Gas / 31.732048139 Gwei

Emitted Events:

61 DepositContract.DepositEvent( pubkey=0x816C4BB0E040FD8987F9218C75ED72E69A28BBF94D823F9978E13E585B355C7CB70EBF37F2925BCF73855A25D34A3499, withdrawal_credentials=0x010000000000000000000000B25FE78FAAEFADB7249B4940EE485856DF150BBE, amount=0x0040597307000000, signature=0x8EA5D7897027887D45CECD112DD975D0614530894F1A8290FBA11F1B8E96C9223E8AB064B369BE8DD20E9F90A787AC55089926544A91F5CB85EE36E7D1CD028687EBAB81D5809CF76CCC8D215D93C88E9129656A82F1FE81F6B83C76D3124154, index=0xFF48150000000000 )
62 BeaconProxy.0x606865b7934a25d4aed43f6cdb426403353fa4b3009c4d228407474581b01e23( 0x606865b7934a25d4aed43f6cdb426403353fa4b3009c4d228407474581b01e23, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000030, 816c4bb0e040fd8987f9218c75ed72e69a28bbf94d823f9978e13e585b355c7c, b70ebf37f2925bcf73855a25d34a349900000000000000000000000000000000 )
63 TransparentUpgradeableProxy.0xa16ad5049bc6092f455ad47c45f18d9e3436db84fa5f1da8dcde4cb12296c03d( 0xa16ad5049bc6092f455ad47c45f18d9e3436db84fa5f1da8dcde4cb12296c03d, 0000000000000000000000000000000000000000000000000000000000000040, 000000000000000000000000000000000000000000000001bc16d674ec800000, 0000000000000000000000000000000000000000000000000000000000000030, 816c4bb0e040fd8987f9218c75ed72e69a28bbf94d823f9978e13e585b355c7c, b70ebf37f2925bcf73855a25d34a349900000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...03d7705Fa
(Beacon Deposit Contract)
42,479,663.376960633295804796 Eth42,479,695.376960633295804796 Eth32
(Lido: Execution Layer Rewards Vault)
79.005525642613573082 Eth79.005671519153734562 Eth0.00014587654016148
0x545D69B9...21d6d3AB9 320.000000000000004928 Eth288.000000000000004928 Eth32
0x7AAd74b7...83425af47
(Kelp DAO: Deployer)
2.120292881813822839 Eth
Nonce: 1993
2.115109769070798579 Eth
Nonce: 1994
0.00518311274302426

Execution Trace

TransparentUpgradeableProxy.09c0e997( )
  • NodeDelegator.stake32Eth( pubkey=0x816C4BB0E040FD8987F9218C75ED72E69A28BBF94D823F9978E13E585B355C7CB70EBF37F2925BCF73855A25D34A3499, signature=0x8EA5D7897027887D45CECD112DD975D0614530894F1A8290FBA11F1B8E96C9223E8AB064B369BE8DD20E9F90A787AC55089926544A91F5CB85EE36E7D1CD028687EBAB81D5809CF76CCC8D215D93C88E9129656A82F1FE81F6B83C76D3124154, depositDataRoot=F39AC96452F721751BA10B16F23CC465CE2BD0C7E85C4478DEBC8AFE87D45B85 )
    • TransparentUpgradeableProxy.91d14854( )
      • LRTConfig.hasRole( role=97667070C54EF182B0F5858B034BEAC1B6F3089AA2D3188BB1E8929F4FA9B929, account=0x7AAd74b7f0d60D5867B59dbD377a71783425af47 ) => ( True )
      • TransparentUpgradeableProxy.e16c7d98( )
        • LRTConfig.getContract( contractKey=FA35F8E3786B5A503B997A59C721169D947268D05DECC8725B4881ABE793C5E7 ) => ( 0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338 )
        • ETH 32 TransparentUpgradeableProxy.9b4e4634( )
          • ETH 32 EigenPodManager.stake( pubkey=0x816C4BB0E040FD8987F9218C75ED72E69A28BBF94D823F9978E13E585B355C7CB70EBF37F2925BCF73855A25D34A3499, signature=0x8EA5D7897027887D45CECD112DD975D0614530894F1A8290FBA11F1B8E96C9223E8AB064B369BE8DD20E9F90A787AC55089926544A91F5CB85EE36E7D1CD028687EBAB81D5809CF76CCC8D215D93C88E9129656A82F1FE81F6B83C76D3124154, depositDataRoot=F39AC96452F721751BA10B16F23CC465CE2BD0C7E85C4478DEBC8AFE87D45B85 )
            • ETH 32 BeaconProxy.9b4e4634( )
              • UpgradeableBeacon.STATICCALL( )
              • ETH 32 EigenPod.stake( pubkey=0x816C4BB0E040FD8987F9218C75ED72E69A28BBF94D823F9978E13E585B355C7CB70EBF37F2925BCF73855A25D34A3499, signature=0x8EA5D7897027887D45CECD112DD975D0614530894F1A8290FBA11F1B8E96C9223E8AB064B369BE8DD20E9F90A787AC55089926544A91F5CB85EE36E7D1CD028687EBAB81D5809CF76CCC8D215D93C88E9129656A82F1FE81F6B83C76D3124154, depositDataRoot=F39AC96452F721751BA10B16F23CC465CE2BD0C7E85C4478DEBC8AFE87D45B85 )
                • ETH 32 DepositContract.deposit( pubkey=0x816C4BB0E040FD8987F9218C75ED72E69A28BBF94D823F9978E13E585B355C7CB70EBF37F2925BCF73855A25D34A3499, withdrawal_credentials=0x010000000000000000000000B25FE78FAAEFADB7249B4940EE485856DF150BBE, signature=0x8EA5D7897027887D45CECD112DD975D0614530894F1A8290FBA11F1B8E96C9223E8AB064B369BE8DD20E9F90A787AC55089926544A91F5CB85EE36E7D1CD028687EBAB81D5809CF76CCC8D215D93C88E9129656A82F1FE81F6B83C76D3124154, deposit_data_root=F39AC96452F721751BA10B16F23CC465CE2BD0C7E85C4478DEBC8AFE87D45B85 )
                  File 1 of 10: TransparentUpgradeableProxy
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
                  pragma solidity ^0.8.0;
                  import "../ERC1967/ERC1967Proxy.sol";
                  /**
                   * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
                   * does not implement this interface directly, and some of its functions are implemented by an internal dispatch
                   * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
                   * include them in the ABI so this interface must be used to interact with it.
                   */
                  interface ITransparentUpgradeableProxy is IERC1967 {
                      function admin() external view returns (address);
                      function implementation() external view returns (address);
                      function changeAdmin(address) external;
                      function upgradeTo(address) external;
                      function upgradeToAndCall(address, bytes memory) external payable;
                  }
                  /**
                   * @dev This contract implements a proxy that is upgradeable by an admin.
                   *
                   * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                   * clashing], which can potentially be used in an attack, this contract uses the
                   * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                   * things that go hand in hand:
                   *
                   * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                   * that call matches one of the admin functions exposed by the proxy itself.
                   * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                   * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                   * "admin cannot fallback to proxy target".
                   *
                   * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                   * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                   * to sudden errors when trying to call a function from the proxy implementation.
                   *
                   * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                   * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                   *
                   * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
                   * inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch
                   * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
                   * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
                   * implementation.
                   *
                   * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler
                   * will not check that there are no selector conflicts, due to the note above. A selector clash between any new function
                   * and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could
                   * render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised.
                   */
                  contract TransparentUpgradeableProxy is ERC1967Proxy {
                      /**
                       * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                       * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                       */
                      constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                          _changeAdmin(admin_);
                      }
                      /**
                       * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                       *
                       * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the
                       * implementation provides a function with the same selector.
                       */
                      modifier ifAdmin() {
                          if (msg.sender == _getAdmin()) {
                              _;
                          } else {
                              _fallback();
                          }
                      }
                      /**
                       * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
                       */
                      function _fallback() internal virtual override {
                          if (msg.sender == _getAdmin()) {
                              bytes memory ret;
                              bytes4 selector = msg.sig;
                              if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
                                  ret = _dispatchUpgradeTo();
                              } else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                                  ret = _dispatchUpgradeToAndCall();
                              } else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) {
                                  ret = _dispatchChangeAdmin();
                              } else if (selector == ITransparentUpgradeableProxy.admin.selector) {
                                  ret = _dispatchAdmin();
                              } else if (selector == ITransparentUpgradeableProxy.implementation.selector) {
                                  ret = _dispatchImplementation();
                              } else {
                                  revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                              }
                              assembly {
                                  return(add(ret, 0x20), mload(ret))
                              }
                          } else {
                              super._fallback();
                          }
                      }
                      /**
                       * @dev Returns the current admin.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function _dispatchAdmin() private returns (bytes memory) {
                          _requireZeroValue();
                          address admin = _getAdmin();
                          return abi.encode(admin);
                      }
                      /**
                       * @dev Returns the current implementation.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                       */
                      function _dispatchImplementation() private returns (bytes memory) {
                          _requireZeroValue();
                          address implementation = _implementation();
                          return abi.encode(implementation);
                      }
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {AdminChanged} event.
                       */
                      function _dispatchChangeAdmin() private returns (bytes memory) {
                          _requireZeroValue();
                          address newAdmin = abi.decode(msg.data[4:], (address));
                          _changeAdmin(newAdmin);
                          return "";
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy.
                       */
                      function _dispatchUpgradeTo() private returns (bytes memory) {
                          _requireZeroValue();
                          address newImplementation = abi.decode(msg.data[4:], (address));
                          _upgradeToAndCall(newImplementation, bytes(""), false);
                          return "";
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                       * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                       * proxied contract.
                       */
                      function _dispatchUpgradeToAndCall() private returns (bytes memory) {
                          (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
                          _upgradeToAndCall(newImplementation, data, true);
                          return "";
                      }
                      /**
                       * @dev Returns the current admin.
                       *
                       * CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead.
                       */
                      function _admin() internal view virtual returns (address) {
                          return _getAdmin();
                      }
                      /**
                       * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
                       * emulate some proxy functions being non-payable while still allowing value to pass through.
                       */
                      function _requireZeroValue() private {
                          require(msg.value == 0);
                      }
                  }
                  // 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.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                  pragma solidity ^0.8.2;
                  import "../beacon/IBeacon.sol";
                  import "../../interfaces/IERC1967.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._
                   */
                  abstract contract ERC1967Upgrade is IERC1967 {
                      // 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 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 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 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.9.0) (interfaces/IERC1967.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
                   *
                   * _Available since v4.8.3._
                   */
                  interface IERC1967 {
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       */
                      event Upgraded(address indexed implementation);
                      /**
                       * @dev Emitted when the admin account has changed.
                       */
                      event AdminChanged(address previousAdmin, address newAdmin);
                      /**
                       * @dev Emitted when the beacon is changed.
                       */
                      event BeaconUpgraded(address indexed beacon);
                  }
                  // 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.9.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
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.0/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.9.0) (utils/StorageSlot.sol)
                  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
                  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:
                   * ```solidity
                   * contract ERC1967 {
                   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                   *
                   *     function _getImplementation() internal view returns (address) {
                   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                   *     }
                   *
                   *     function _setImplementation(address newImplementation) internal {
                   *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                   *     }
                   * }
                   * ```
                   *
                   * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
                   * _Available since v4.9 for `string`, `bytes`._
                   */
                  library StorageSlot {
                      struct AddressSlot {
                          address value;
                      }
                      struct BooleanSlot {
                          bool value;
                      }
                      struct Bytes32Slot {
                          bytes32 value;
                      }
                      struct Uint256Slot {
                          uint256 value;
                      }
                      struct StringSlot {
                          string value;
                      }
                      struct BytesSlot {
                          bytes value;
                      }
                      /**
                       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                       */
                      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                       */
                      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                       */
                      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                       */
                      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` with member `value` located at `slot`.
                       */
                      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                       */
                      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                       */
                      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                       */
                      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                  }
                  

                  File 2 of 10: DepositContract
                  // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                  // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                  // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                  // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                  // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                  // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  
                  // SPDX-License-Identifier: CC0-1.0
                  
                  pragma solidity 0.6.11;
                  
                  // 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 IDepositContract {
                      /// @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);
                  }
                  
                  // Based on official specification in https://eips.ethereum.org/EIPS/eip-165
                  interface ERC165 {
                      /// @notice Query if a contract implements an interface
                      /// @param interfaceId The interface identifier, as specified in ERC-165
                      /// @dev Interface identification is specified in ERC-165. This function
                      ///  uses less than 30,000 gas.
                      /// @return `true` if the contract implements `interfaceId` and
                      ///  `interfaceId` is not 0xffffffff, `false` otherwise
                      function supportsInterface(bytes4 interfaceId) external pure returns (bool);
                  }
                  
                  // This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
                  // It tries to stay as close as possible to the original source code.
                  /// @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
                  contract DepositContract is IDepositContract, ERC165 {
                      uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
                      // NOTE: this also ensures `deposit_count` will fit into 64-bits
                      uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
                  
                      bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
                      uint256 deposit_count;
                  
                      bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
                  
                      constructor() public {
                          // Compute hashes in empty sparse Merkle tree
                          for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
                              zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
                      }
                  
                      function get_deposit_root() override external view returns (bytes32) {
                          bytes32 node;
                          uint size = deposit_count;
                          for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
                              if ((size & 1) == 1)
                                  node = sha256(abi.encodePacked(branch[height], node));
                              else
                                  node = sha256(abi.encodePacked(node, zero_hashes[height]));
                              size /= 2;
                          }
                          return sha256(abi.encodePacked(
                              node,
                              to_little_endian_64(uint64(deposit_count)),
                              bytes24(0)
                          ));
                      }
                  
                      function get_deposit_count() override external view returns (bytes memory) {
                          return to_little_endian_64(uint64(deposit_count));
                      }
                  
                      function deposit(
                          bytes calldata pubkey,
                          bytes calldata withdrawal_credentials,
                          bytes calldata signature,
                          bytes32 deposit_data_root
                      ) override external payable {
                          // Extended ABI length checks since dynamic types are used.
                          require(pubkey.length == 48, "DepositContract: invalid pubkey length");
                          require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
                          require(signature.length == 96, "DepositContract: invalid signature length");
                  
                          // Check deposit amount
                          require(msg.value >= 1 ether, "DepositContract: deposit value too low");
                          require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
                          uint deposit_amount = msg.value / 1 gwei;
                          require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
                  
                          // Emit `DepositEvent` log
                          bytes memory amount = to_little_endian_64(uint64(deposit_amount));
                          emit DepositEvent(
                              pubkey,
                              withdrawal_credentials,
                              amount,
                              signature,
                              to_little_endian_64(uint64(deposit_count))
                          );
                  
                          // Compute deposit data root (`DepositData` hash tree root)
                          bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
                          bytes32 signature_root = sha256(abi.encodePacked(
                              sha256(abi.encodePacked(signature[:64])),
                              sha256(abi.encodePacked(signature[64:], bytes32(0)))
                          ));
                          bytes32 node = sha256(abi.encodePacked(
                              sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
                              sha256(abi.encodePacked(amount, bytes24(0), signature_root))
                          ));
                  
                          // Verify computed and expected deposit data roots match
                          require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
                  
                          // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
                          require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
                  
                          // Add deposit data root to Merkle tree (update a single `branch` node)
                          deposit_count += 1;
                          uint size = deposit_count;
                          for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
                              if ((size & 1) == 1) {
                                  branch[height] = node;
                                  return;
                              }
                              node = sha256(abi.encodePacked(branch[height], node));
                              size /= 2;
                          }
                          // As the loop should always end prematurely with the `return` statement,
                          // this code should be unreachable. We assert `false` just to be safe.
                          assert(false);
                      }
                  
                      function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
                          return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
                      }
                  
                      function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
                          ret = new bytes(8);
                          bytes8 bytesValue = bytes8(value);
                          // Byteswapping during copying to bytes.
                          ret[0] = bytesValue[7];
                          ret[1] = bytesValue[6];
                          ret[2] = bytesValue[5];
                          ret[3] = bytesValue[4];
                          ret[4] = bytesValue[3];
                          ret[5] = bytesValue[2];
                          ret[6] = bytesValue[1];
                          ret[7] = bytesValue[0];
                      }
                  }

                  File 3 of 10: BeaconProxy
                  // 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);
                  }
                  
                  // 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._
                   */
                  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);
                          }
                      }
                  }
                  
                  // 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 {}
                  }
                  
                  // 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);
                  }
                  
                  // 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
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.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);
                          }
                      }
                  }
                  
                  // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
                  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
                  
                  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:
                   * ```solidity
                   * contract ERC1967 {
                   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                   *
                   *     function _getImplementation() internal view returns (address) {
                   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                   *     }
                   *
                   *     function _setImplementation(address newImplementation) internal {
                   *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                   *     }
                   * }
                   * ```
                   *
                   * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
                   * _Available since v4.9 for `string`, `bytes`._
                   */
                  library StorageSlot {
                      struct AddressSlot {
                          address value;
                      }
                  
                      struct BooleanSlot {
                          bool value;
                      }
                  
                      struct Bytes32Slot {
                          bytes32 value;
                      }
                  
                      struct Uint256Slot {
                          uint256 value;
                      }
                  
                      struct StringSlot {
                          string value;
                      }
                  
                      struct BytesSlot {
                          bytes value;
                      }
                  
                      /**
                       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                       */
                      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                       */
                      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                       */
                      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                       */
                      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `StringSlot` with member `value` located at `slot`.
                       */
                      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                       */
                      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                       */
                      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                  
                      /**
                       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                       */
                      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                  }
                  
                  // OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  // import "./IBeacon.sol";
                  // import "../Proxy.sol";
                  // import "../ERC1967/ERC1967Upgrade.sol";
                  
                  /**
                   * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
                   *
                   * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
                   * conflict with the storage layout of the implementation behind the proxy.
                   *
                   * _Available since v3.4._
                   */
                  contract BeaconProxy is Proxy, ERC1967Upgrade {
                      /**
                       * @dev Initializes the proxy with `beacon`.
                       *
                       * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                       * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
                       * constructor.
                       *
                       * Requirements:
                       *
                       * - `beacon` must be a contract with the interface {IBeacon}.
                       */
                      constructor(address beacon, bytes memory data) payable {
                          _upgradeBeaconToAndCall(beacon, data, false);
                      }
                  
                      /**
                       * @dev Returns the current beacon address.
                       */
                      function _beacon() internal view virtual returns (address) {
                          return _getBeacon();
                      }
                  
                      /**
                       * @dev Returns the current implementation address of the associated beacon.
                       */
                      function _implementation() internal view virtual override returns (address) {
                          return IBeacon(_getBeacon()).implementation();
                      }
                  
                      /**
                       * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                       *
                       * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                       *
                       * Requirements:
                       *
                       * - `beacon` must be a contract.
                       * - The implementation returned by `beacon` must be a contract.
                       */
                      function _setBeacon(address beacon, bytes memory data) internal virtual {
                          _upgradeBeaconToAndCall(beacon, data, false);
                      }
                  }

                  File 4 of 10: NodeDelegator
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  import { UtilLib } from "./utils/UtilLib.sol";
                  import { LRTConstants } from "./utils/LRTConstants.sol";
                  import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol";
                  import { INodeDelegator } from "./interfaces/INodeDelegator.sol";
                  import { IStrategy } from "./interfaces/IStrategy.sol";
                  import { IEigenStrategyManager } from "./interfaces/IEigenStrategyManager.sol";
                  import { IEigenDelayedWithdrawalRouter } from "./interfaces/IEigenDelayedWithdrawalRouter.sol";
                  import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
                  import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
                  import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                  import { IEigenPodManager } from "./interfaces/IEigenPodManager.sol";
                  import { IEigenPod, BeaconChainProofs, IBeaconDeposit } from "./interfaces/IEigenPod.sol";
                  /// @title NodeDelegator Contract
                  /// @notice The contract that handles the depositing of assets into strategies
                  contract NodeDelegator is INodeDelegator, LRTConfigRoleChecker, PausableUpgradeable, ReentrancyGuardUpgradeable {
                      /// @dev The EigenPod is created and owned by this contract
                      IEigenPod public eigenPod;
                      /// @dev Tracks the balance staked to validators and has yet to have the credentials verified with EigenLayer.
                      /// call verifyWithdrawalCredentialsAndBalance in EL to verify the validator credentials on EigenLayer
                      uint256 public stakedButUnverifiedNativeETH;
                      /// @custom:oz-upgrades-unsafe-allow constructor
                      constructor() {
                          _disableInitializers();
                      }
                      /// @dev Initializes the contract
                      /// @param lrtConfigAddr LRT config address
                      function initialize(address lrtConfigAddr) external initializer {
                          UtilLib.checkNonZeroAddress(lrtConfigAddr);
                          __Pausable_init();
                          __ReentrancyGuard_init();
                          lrtConfig = ILRTConfig(lrtConfigAddr);
                          emit UpdatedLRTConfig(lrtConfigAddr);
                      }
                      function createEigenPod() external onlyLRTManager {
                          IEigenPodManager eigenPodManager = IEigenPodManager(lrtConfig.getContract(LRTConstants.EIGEN_POD_MANAGER));
                          eigenPodManager.createPod();
                          eigenPod = eigenPodManager.ownerToPod(address(this));
                          emit EigenPodCreated(address(eigenPod), address(this));
                      }
                      /// @notice Approves the maximum amount of an asset to the eigen strategy manager
                      /// @dev only supported assets can be deposited and only called by the LRT manager
                      /// @param asset the asset to deposit
                      function maxApproveToEigenStrategyManager(address asset)
                          external
                          override
                          onlySupportedAsset(asset)
                          onlyLRTManager
                      {
                          address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
                          IERC20(asset).approve(eigenlayerStrategyManagerAddress, type(uint256).max);
                      }
                      /// @notice Deposits an asset lying in this NDC into its strategy
                      /// @dev only supported assets can be deposited and only called by the LRT manager
                      /// @param asset the asset to deposit
                      function depositAssetIntoStrategy(address asset)
                          external
                          override
                          whenNotPaused
                          nonReentrant
                          onlySupportedAsset(asset)
                          onlyLRTManager
                      {
                          address strategy = lrtConfig.assetStrategy(asset);
                          if (strategy == address(0)) {
                              revert StrategyIsNotSetForAsset();
                          }
                          IERC20 token = IERC20(asset);
                          address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
                          uint256 balance = token.balanceOf(address(this));
                          IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance);
                          emit AssetDepositIntoStrategy(asset, strategy, balance);
                      }
                      /// @notice Transfers an asset back to the LRT deposit pool
                      /// @dev only supported assets can be transferred and only called by the LRT manager
                      /// @param asset the asset to transfer
                      /// @param amount the amount to transfer
                      function transferBackToLRTDepositPool(
                          address asset,
                          uint256 amount
                      )
                          external
                          whenNotPaused
                          nonReentrant
                          onlySupportedAsset(asset)
                          onlyLRTManager
                      {
                          address lrtDepositPool = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);
                          bool success;
                          if (asset == LRTConstants.ETH_TOKEN) {
                              (success,) = payable(lrtDepositPool).call{ value: amount }("");
                          } else {
                              success = IERC20(asset).transfer(lrtDepositPool, amount);
                          }
                          if (!success) {
                              revert TokenTransferFailed();
                          }
                      }
                      /// @notice Fetches balance of all assets staked in eigen layer through this contract
                      /// @return assets the assets that the node delegator has deposited into strategies
                      /// @return assetBalances the balances of the assets that the node delegator has deposited into strategies
                      function getAssetBalances()
                          external
                          view
                          override
                          returns (address[] memory assets, uint256[] memory assetBalances)
                      {
                          address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER);
                          (IStrategy[] memory strategies,) =
                              IEigenStrategyManager(eigenlayerStrategyManagerAddress).getDeposits(address(this));
                          uint256 strategiesLength = strategies.length;
                          assets = new address[](strategiesLength);
                          assetBalances = new uint256[](strategiesLength);
                          for (uint256 i = 0; i < strategiesLength;) {
                              assets[i] = address(IStrategy(strategies[i]).underlyingToken());
                              assetBalances[i] = IStrategy(strategies[i]).userUnderlyingView(address(this));
                              unchecked {
                                  ++i;
                              }
                          }
                      }
                      /// @dev Returns the balance of an asset that the node delegator has deposited into the strategy
                      /// @param asset the asset to get the balance of
                      /// @return stakedBalance the balance of the asset
                      function getAssetBalance(address asset) external view override returns (uint256) {
                          address strategy = lrtConfig.assetStrategy(asset);
                          if (strategy == address(0)) {
                              return 0;
                          }
                          return IStrategy(strategy).userUnderlyingView(address(this));
                      }
                      /// @dev Returns the balance of an asset that the node delegator has deposited into its EigenPod strategy
                      function getETHEigenPodBalance() external view override returns (uint256 ethStaked) {
                          // TODO: Implement functionality to manage pending withdrawals and accommodate negative shares once withdrawal
                          // feature is activated. Additionally, ensure verification of both staked but unverified and staked and verified
                          // ETH native supply NDCs as provided to Eigenlayer.
                          ethStaked = stakedButUnverifiedNativeETH;
                      }
                      /// @notice Stake ETH from NDC into EigenLayer. it calls the stake function in the EigenPodManager
                      /// which in turn calls the stake function in the EigenPod
                      /// @param pubkey The pubkey of the validator
                      /// @param signature The signature of the validator
                      /// @param depositDataRoot The deposit data root of the validator
                      /// @dev Only LRT Operator should call this function
                      /// @dev Exactly 32 ether is allowed, hence it is hardcoded
                      /// @dev offchain checks withdraw credentials authenticity
                      function stake32Eth(
                          bytes calldata pubkey,
                          bytes calldata signature,
                          bytes32 depositDataRoot
                      )
                          external
                          whenNotPaused
                          onlyLRTOperator
                      {
                          IEigenPodManager eigenPodManager = IEigenPodManager(lrtConfig.getContract(LRTConstants.EIGEN_POD_MANAGER));
                          eigenPodManager.stake{ value: 32 ether }(pubkey, signature, depositDataRoot);
                          // tracks staked but unverified native ETH
                          stakedButUnverifiedNativeETH += 32 ether;
                          emit ETHStaked(pubkey, 32 ether);
                      }
                      /// @notice Stake ETH from NDC into EigenLayer
                      /// @param pubkey The pubkey of the validator
                      /// @param signature The signature of the validator
                      /// @param depositDataRoot The deposit data root of the validator
                      /// @param expectedDepositRoot The expected deposit data root, which is computed offchain
                      /// @dev Only LRT Operator should call this function
                      /// @dev Exactly 32 ether is allowed, hence it is hardcoded
                      /// @dev offchain checks withdraw credentials authenticity
                      /// @dev compares expected deposit root with actual deposit root
                      function stake32EthValidated(
                          bytes calldata pubkey,
                          bytes calldata signature,
                          bytes32 depositDataRoot,
                          bytes32 expectedDepositRoot
                      )
                          external
                          whenNotPaused
                          onlyLRTOperator
                      {
                          IBeaconDeposit depositContract = eigenPod.ethPOS();
                          bytes32 actualDepositRoot = depositContract.get_deposit_root();
                          if (expectedDepositRoot != actualDepositRoot) {
                              revert InvalidDepositRoot(expectedDepositRoot, actualDepositRoot);
                          }
                          IEigenPodManager eigenPodManager = IEigenPodManager(lrtConfig.getContract(LRTConstants.EIGEN_POD_MANAGER));
                          eigenPodManager.stake{ value: 32 ether }(pubkey, signature, depositDataRoot);
                          // tracks staked but unverified native ETH
                          stakedButUnverifiedNativeETH += 32 ether;
                          emit ETHStaked(pubkey, 32 ether);
                      }
                      /// @dev initiate a delayed withdraw of the ETH before the eigenpod is verified
                      /// which will be available to claim after withdrawalDelay blocks
                      function initiateWithdrawRewards() external onlyLRTOperator {
                          uint256 eigenPodBalance = address(eigenPod).balance;
                          uint256 ethValidatorMinBalanceThreshold = 16 ether;
                          if (eigenPodBalance > ethValidatorMinBalanceThreshold) {
                              revert InvalidRewardAmount();
                          }
                          eigenPod.withdrawBeforeRestaking();
                          emit ETHRewardsWithdrawInitiated(eigenPodBalance);
                      }
                      /// @dev claims back the withdrawal amount initiated to this nodeDelegator contract
                      /// once withdrawal amount is claimable
                      function claimRewards(uint256 maxNumberOfDelayedWithdrawalsToClaim) external onlyLRTOperator {
                          uint256 balanceBefore = address(this).balance;
                          address delayedRouterAddr = eigenPod.delayedWithdrawalRouter();
                          IEigenDelayedWithdrawalRouter elDelayedRouter = IEigenDelayedWithdrawalRouter(delayedRouterAddr);
                          elDelayedRouter.claimDelayedWithdrawals(address(this), maxNumberOfDelayedWithdrawalsToClaim);
                          uint256 balanceAfter = address(this).balance;
                          emit ETHRewardsClaimed(balanceAfter - balanceBefore);
                      }
                      /// @dev Triggers stopped state. Contract must not be paused.
                      function pause() external onlyLRTManager {
                          _pause();
                      }
                      /// @dev Returns to normal state. Contract must be paused
                      function unpause() external onlyLRTAdmin {
                          _unpause();
                      }
                      /// @dev allow NodeDelegator to receive ETH
                      function sendETHFromDepositPoolToNDC() external payable override {
                          // only allow LRT deposit pool to send ETH to this contract
                          address lrtDepositPool = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);
                          if (msg.sender != lrtDepositPool) {
                              revert InvalidETHSender();
                          }
                          emit ETHDepositFromDepositPool(msg.value);
                      }
                      /// @dev allow NodeDelegator to receive ETH
                      receive() external payable { }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  /// @title UtilLib - Utility library
                  /// @notice Utility functions
                  library UtilLib {
                      error ZeroAddressNotAllowed();
                      /// @dev zero address check modifier
                      /// @param address_ address to check
                      function checkNonZeroAddress(address address_) internal pure {
                          if (address_ == address(0)) revert ZeroAddressNotAllowed();
                      }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  library LRTConstants {
                      //tokens
                      //rETH token
                      bytes32 public constant R_ETH_TOKEN = keccak256("R_ETH_TOKEN");
                      //stETH token
                      bytes32 public constant ST_ETH_TOKEN = keccak256("ST_ETH_TOKEN");
                      //cbETH token
                      bytes32 public constant CB_ETH_TOKEN = keccak256("CB_ETH_TOKEN");
                      //ETHX token
                      bytes32 public constant ETHX_TOKEN = keccak256("ETHX_TOKEN");
                      //contracts
                      bytes32 public constant LRT_ORACLE = keccak256("LRT_ORACLE");
                      bytes32 public constant LRT_DEPOSIT_POOL = keccak256("LRT_DEPOSIT_POOL");
                      bytes32 public constant EIGEN_STRATEGY_MANAGER = keccak256("EIGEN_STRATEGY_MANAGER");
                      //Roles
                      bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                      bytes32 public constant MANAGER = keccak256("MANAGER");
                      bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
                      bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
                      // updated library variables
                      bytes32 public constant SFRXETH_TOKEN = keccak256("SFRXETH_TOKEN");
                      // add new vars below
                      bytes32 public constant EIGEN_POD_MANAGER = keccak256("EIGEN_POD_MANAGER");
                      // native ETH as ERC20 for ease of implementation
                      address public constant ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                      // Operator Role
                      bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  import { UtilLib } from "./UtilLib.sol";
                  import { LRTConstants } from "./LRTConstants.sol";
                  import { ILRTConfig } from "../interfaces/ILRTConfig.sol";
                  import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
                  /// @title LRTConfigRoleChecker - LRT Config Role Checker Contract
                  /// @notice Handles LRT config role checks
                  abstract contract LRTConfigRoleChecker {
                      ILRTConfig public lrtConfig;
                      // events
                      event UpdatedLRTConfig(address indexed lrtConfig);
                      // modifiers
                      modifier onlyRole(bytes32 role) {
                          if (!IAccessControl(address(lrtConfig)).hasRole(role, msg.sender)) {
                              string memory roleStr = string(abi.encodePacked(role));
                              revert ILRTConfig.CallerNotLRTConfigAllowedRole(roleStr);
                          }
                          _;
                      }
                      modifier onlyLRTManager() {
                          if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.MANAGER, msg.sender)) {
                              revert ILRTConfig.CallerNotLRTConfigManager();
                          }
                          _;
                      }
                      modifier onlyLRTOperator() {
                          if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.OPERATOR_ROLE, msg.sender)) {
                              revert ILRTConfig.CallerNotLRTConfigOperator();
                          }
                          _;
                      }
                      modifier onlyLRTAdmin() {
                          if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.DEFAULT_ADMIN_ROLE, msg.sender)) {
                              revert ILRTConfig.CallerNotLRTConfigAdmin();
                          }
                          _;
                      }
                      modifier onlySupportedAsset(address asset) {
                          if (!lrtConfig.isSupportedAsset(asset)) {
                              revert ILRTConfig.AssetNotSupported();
                          }
                          _;
                      }
                      // setters
                      /// @notice Updates the LRT config contract
                      /// @dev only callable by LRT admin
                      /// @param lrtConfigAddr the new LRT config contract Address
                      function updateLRTConfig(address lrtConfigAddr) external virtual onlyLRTAdmin {
                          if (address(lrtConfig) != address(0)) revert ILRTConfig.ValueAlreadyInUse();
                          UtilLib.checkNonZeroAddress(lrtConfigAddr);
                          lrtConfig = ILRTConfig(lrtConfigAddr);
                          emit UpdatedLRTConfig(lrtConfigAddr);
                      }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  import "./IStrategy.sol";
                  interface INodeDelegator {
                      // event
                      event AssetDepositIntoStrategy(address indexed asset, address indexed strategy, uint256 depositAmount);
                      event ETHDepositFromDepositPool(uint256 depositAmount);
                      event EigenPodCreated(address indexed eigenPod, address indexed podOwner);
                      event ETHStaked(bytes valPubKey, uint256 amount);
                      event ETHRewardsClaimed(uint256 amount);
                      event ETHRewardsWithdrawInitiated(uint256 amount);
                      // errors
                      error TokenTransferFailed();
                      error StrategyIsNotSetForAsset();
                      error InvalidETHSender();
                      error InvalidRewardAmount();
                      error InvalidDepositRoot(bytes32 expectedDepositRoot, bytes32 actualDepositRoot);
                      // getter
                      function stakedButUnverifiedNativeETH() external view returns (uint256);
                      // methods
                      function depositAssetIntoStrategy(address asset) external;
                      function maxApproveToEigenStrategyManager(address asset) external;
                      function getAssetBalances() external view returns (address[] memory, uint256[] memory);
                      function getAssetBalance(address asset) external view returns (uint256);
                      function getETHEigenPodBalance() external view returns (uint256);
                      function transferBackToLRTDepositPool(address asset, uint256 amount) external;
                      function sendETHFromDepositPoolToNDC() external payable;
                  }
                  // 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);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  import "./IStrategy.sol";
                  interface IEigenStrategyManager {
                      /**
                       * @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 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);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  interface IEigenDelayedWithdrawalRouter {
                      function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) external;
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
                  pragma solidity ^0.8.0;
                  import "../token/ERC20/IERC20.sol";
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
                  pragma solidity ^0.8.0;
                  import "../utils/ContextUpgradeable.sol";
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module which allows children to implement an emergency stop
                   * mechanism that can be triggered by an authorized account.
                   *
                   * This module is used through inheritance. It will make available the
                   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
                   * the functions of your contract. Note that they will not be pausable by
                   * simply including this module, only once the modifiers are put in place.
                   */
                  abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                      /**
                       * @dev Emitted when the pause is triggered by `account`.
                       */
                      event Paused(address account);
                      /**
                       * @dev Emitted when the pause is lifted by `account`.
                       */
                      event Unpaused(address account);
                      bool private _paused;
                      /**
                       * @dev Initializes the contract in unpaused state.
                       */
                      function __Pausable_init() internal onlyInitializing {
                          __Pausable_init_unchained();
                      }
                      function __Pausable_init_unchained() internal onlyInitializing {
                          _paused = false;
                      }
                      /**
                       * @dev Modifier to make a function callable only when the contract is not paused.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      modifier whenNotPaused() {
                          _requireNotPaused();
                          _;
                      }
                      /**
                       * @dev Modifier to make a function callable only when the contract is paused.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      modifier whenPaused() {
                          _requirePaused();
                          _;
                      }
                      /**
                       * @dev Returns true if the contract is paused, and false otherwise.
                       */
                      function paused() public view virtual returns (bool) {
                          return _paused;
                      }
                      /**
                       * @dev Throws if the contract is paused.
                       */
                      function _requireNotPaused() internal view virtual {
                          require(!paused(), "Pausable: paused");
                      }
                      /**
                       * @dev Throws if the contract is not paused.
                       */
                      function _requirePaused() internal view virtual {
                          require(paused(), "Pausable: not paused");
                      }
                      /**
                       * @dev Triggers stopped state.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      function _pause() internal virtual whenNotPaused {
                          _paused = true;
                          emit Paused(_msgSender());
                      }
                      /**
                       * @dev Returns to normal state.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      function _unpause() internal virtual whenPaused {
                          _paused = false;
                          emit Unpaused(_msgSender());
                      }
                      /**
                       * @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.9.0) (security/ReentrancyGuard.sol)
                  pragma solidity ^0.8.0;
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module that helps prevent reentrant calls to a function.
                   *
                   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                   * available, which can be applied to functions to make sure there are no nested
                   * (reentrant) calls to them.
                   *
                   * Note that because there is a single `nonReentrant` guard, functions marked as
                   * `nonReentrant` may not call one another. This can be worked around by making
                   * those functions `private`, and then adding `external` `nonReentrant` entry
                   * points to them.
                   *
                   * TIP: If you would like to learn more about reentrancy and alternative ways
                   * to protect against it, check out our blog post
                   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                   */
                  abstract contract ReentrancyGuardUpgradeable is Initializable {
                      // Booleans are more expensive than uint256 or any type that takes up a full
                      // word because each write operation emits an extra SLOAD to first read the
                      // slot's contents, replace the bits taken up by the boolean, and then write
                      // back. This is the compiler's defense against contract upgrades and
                      // pointer aliasing, and it cannot be disabled.
                      // The values being non-zero value makes deployment a bit more expensive,
                      // but in exchange the refund on every call to nonReentrant will be lower in
                      // amount. Since refunds are capped to a percentage of the total
                      // transaction's gas, it is best to keep them low in cases like this one, to
                      // increase the likelihood of the full refund coming into effect.
                      uint256 private constant _NOT_ENTERED = 1;
                      uint256 private constant _ENTERED = 2;
                      uint256 private _status;
                      function __ReentrancyGuard_init() internal onlyInitializing {
                          __ReentrancyGuard_init_unchained();
                      }
                      function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                          _status = _NOT_ENTERED;
                      }
                      /**
                       * @dev Prevents a contract from calling itself, directly or indirectly.
                       * Calling a `nonReentrant` function from another `nonReentrant`
                       * function is not supported. It is possible to prevent this from happening
                       * by making the `nonReentrant` function external, and making it call a
                       * `private` function that does the actual work.
                       */
                      modifier nonReentrant() {
                          _nonReentrantBefore();
                          _;
                          _nonReentrantAfter();
                      }
                      function _nonReentrantBefore() private {
                          // On the first call to nonReentrant, _status will be _NOT_ENTERED
                          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                          // Any calls to nonReentrant after this point will fail
                          _status = _ENTERED;
                      }
                      function _nonReentrantAfter() private {
                          // By storing the original value once again, a refund is triggered (see
                          // https://eips.ethereum.org/EIPS/eip-2200)
                          _status = _NOT_ENTERED;
                      }
                      /**
                       * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                       * `nonReentrant` function in the call stack.
                       */
                      function _reentrancyGuardEntered() internal view returns (bool) {
                          return _status == _ENTERED;
                      }
                      /**
                       * @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: BUSL-1.1
                  pragma solidity 0.8.21;
                  import { IEigenPod } from "./IEigenPod.sol";
                  interface IEigenPodManager {
                      /**
                       * @notice Creates an EigenPod for the sender.
                       * @dev Function will revert if the `msg.sender` already has an EigenPod.
                       */
                      function createPod() external;
                      /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
                      function ownerToPod(address podOwner) external view returns (IEigenPod);
                      /**
                       * @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;
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  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);
                      }
                  }
                  library BeaconChainProofs {
                      struct ValidatorFieldsAndBalanceProofs {
                          bytes validatorFieldsProof;
                          bytes validatorBalanceProof;
                          bytes32 balanceRoot;
                      }
                      /**
                       *
                       * @notice This function is parses the balanceRoot to get the uint64 balance of a validator.  During merkleization
                       * of the
                       * beacon state balance tree, four uint64 values (making 32 bytes) are grouped together and treated as a single leaf
                       * in the merkle tree. Thus the
                       * validatorIndex mod 4 is used to determine which of the four uint64 values to extract from the balanceRoot.
                       * @param validatorIndex is the index of the validator being proven for.
                       * @param balanceRoot is the combination of 4 validator balances being proven for.
                       * @return The validator's balance, in Gwei
                       */
                      function getBalanceFromBalanceRoot(uint40 validatorIndex, bytes32 balanceRoot) internal pure returns (uint64) {
                          uint256 bitShiftAmount = (validatorIndex % 4) * 64;
                          bytes32 validatorBalanceLittleEndian = bytes32((uint256(balanceRoot) << bitShiftAmount));
                          uint64 validatorBalance = Endian.fromLittleEndianUint64(validatorBalanceLittleEndian);
                          return validatorBalance;
                      }
                  }
                  interface IBeaconDeposit {
                      /// @notice Query the current deposit root hash.
                      /// @return The deposit root hash.
                      function get_deposit_root() external view returns (bytes32);
                  }
                  interface IEigenPod {
                      /// @notice This is the beacon chain deposit contract
                      function ethPOS() external returns (IBeaconDeposit);
                      /// @return delayedWithdrawalRouter address of eigenlayer delayedWithdrawalRouter,
                      /// which does book keeping of delayed withdrawls
                      function delayedWithdrawalRouter() external returns (address);
                      /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                      function withdrawBeforeRestaking() external;
                      /**
                       * @notice This function verifies that the withdrawal credentials of the podOwner are pointed to
                       * this contract. It also verifies the current (not 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 oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs
                       * @param proofs is the bytes that prove the 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 verifyWithdrawalCredentialsAndBalance(
                          uint64 oracleBlockNumber,
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs memory proofs,
                          bytes32[] calldata validatorFields
                      )
                          external;
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  interface ILRTConfig {
                      // Errors
                      error ValueAlreadyInUse();
                      error AssetAlreadySupported();
                      error AssetNotSupported();
                      error CallerNotLRTConfigAdmin();
                      error CallerNotLRTConfigManager();
                      error CallerNotLRTConfigOperator();
                      error CallerNotLRTConfigAllowedRole(string role);
                      error CannotUpdateStrategyAsItHasFundsNDCFunds(address ndc, uint256 amount);
                      error InvalidMaxRewardAmount();
                      // Events
                      event SetToken(bytes32 key, address indexed tokenAddr);
                      event SetContract(bytes32 key, address indexed contractAddr);
                      event AddedNewSupportedAsset(address indexed asset, uint256 depositLimit);
                      event RemovedSupportedAsset(address indexed asset);
                      event AssetDepositLimitUpdate(address indexed asset, uint256 depositLimit);
                      event AssetStrategyUpdate(address indexed asset, address indexed strategy);
                      event SetRSETH(address indexed rsETH);
                      event UpdateMaxRewardAmount(uint256 maxRewardAmount);
                      // methods
                      function rsETH() external view returns (address);
                      function assetStrategy(address asset) external view returns (address);
                      function isSupportedAsset(address asset) external view returns (bool);
                      function getLSTToken(bytes32 tokenId) external view returns (address);
                      function getContract(bytes32 contractId) external view returns (address);
                      function getSupportedAssetList() external view returns (address[] memory);
                      function depositLimitByAsset(address asset) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev External interface of AccessControl declared to support ERC165 detection.
                   */
                  interface IAccessControl {
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       *
                       * _Available since v3.1._
                       */
                      event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call, an admin role
                       * bearer except when using {AccessControl-_setupRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       */
                      function renounceRole(bytes32 role, address account) external;
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.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 (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.9.0) (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]
                   * ```solidity
                   * contract MyToken is ERC20Upgradeable {
                   *     function initialize() initializer public {
                   *         __ERC20_init("MyToken", "MTK");
                   *     }
                   * }
                   *
                   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                   *     function initializeV2() reinitializer(2) public {
                   *         __ERC20Permit_init("MyToken");
                   *     }
                   * }
                   * ```
                   *
                   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                   *
                   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                   *
                   * [CAUTION]
                   * ====
                   * Avoid leaving a contract uninitialized.
                   *
                   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * /// @custom:oz-upgrades-unsafe-allow constructor
                   * constructor() {
                   *     _disableInitializers();
                   * }
                   * ```
                   * ====
                   */
                  abstract contract Initializable {
                      /**
                       * @dev 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.9.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
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.0/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);
                          }
                      }
                  }
                  

                  File 5 of 10: TransparentUpgradeableProxy
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
                  pragma solidity ^0.8.0;
                  import "../ERC1967/ERC1967Proxy.sol";
                  /**
                   * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
                   * does not implement this interface directly, and some of its functions are implemented by an internal dispatch
                   * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
                   * include them in the ABI so this interface must be used to interact with it.
                   */
                  interface ITransparentUpgradeableProxy is IERC1967 {
                      function admin() external view returns (address);
                      function implementation() external view returns (address);
                      function changeAdmin(address) external;
                      function upgradeTo(address) external;
                      function upgradeToAndCall(address, bytes memory) external payable;
                  }
                  /**
                   * @dev This contract implements a proxy that is upgradeable by an admin.
                   *
                   * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                   * clashing], which can potentially be used in an attack, this contract uses the
                   * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                   * things that go hand in hand:
                   *
                   * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                   * that call matches one of the admin functions exposed by the proxy itself.
                   * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                   * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                   * "admin cannot fallback to proxy target".
                   *
                   * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                   * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                   * to sudden errors when trying to call a function from the proxy implementation.
                   *
                   * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                   * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                   *
                   * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
                   * inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch
                   * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
                   * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
                   * implementation.
                   *
                   * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler
                   * will not check that there are no selector conflicts, due to the note above. A selector clash between any new function
                   * and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could
                   * render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised.
                   */
                  contract TransparentUpgradeableProxy is ERC1967Proxy {
                      /**
                       * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                       * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                       */
                      constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                          _changeAdmin(admin_);
                      }
                      /**
                       * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                       *
                       * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the
                       * implementation provides a function with the same selector.
                       */
                      modifier ifAdmin() {
                          if (msg.sender == _getAdmin()) {
                              _;
                          } else {
                              _fallback();
                          }
                      }
                      /**
                       * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
                       */
                      function _fallback() internal virtual override {
                          if (msg.sender == _getAdmin()) {
                              bytes memory ret;
                              bytes4 selector = msg.sig;
                              if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
                                  ret = _dispatchUpgradeTo();
                              } else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                                  ret = _dispatchUpgradeToAndCall();
                              } else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) {
                                  ret = _dispatchChangeAdmin();
                              } else if (selector == ITransparentUpgradeableProxy.admin.selector) {
                                  ret = _dispatchAdmin();
                              } else if (selector == ITransparentUpgradeableProxy.implementation.selector) {
                                  ret = _dispatchImplementation();
                              } else {
                                  revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                              }
                              assembly {
                                  return(add(ret, 0x20), mload(ret))
                              }
                          } else {
                              super._fallback();
                          }
                      }
                      /**
                       * @dev Returns the current admin.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function _dispatchAdmin() private returns (bytes memory) {
                          _requireZeroValue();
                          address admin = _getAdmin();
                          return abi.encode(admin);
                      }
                      /**
                       * @dev Returns the current implementation.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                       */
                      function _dispatchImplementation() private returns (bytes memory) {
                          _requireZeroValue();
                          address implementation = _implementation();
                          return abi.encode(implementation);
                      }
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {AdminChanged} event.
                       */
                      function _dispatchChangeAdmin() private returns (bytes memory) {
                          _requireZeroValue();
                          address newAdmin = abi.decode(msg.data[4:], (address));
                          _changeAdmin(newAdmin);
                          return "";
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy.
                       */
                      function _dispatchUpgradeTo() private returns (bytes memory) {
                          _requireZeroValue();
                          address newImplementation = abi.decode(msg.data[4:], (address));
                          _upgradeToAndCall(newImplementation, bytes(""), false);
                          return "";
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                       * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                       * proxied contract.
                       */
                      function _dispatchUpgradeToAndCall() private returns (bytes memory) {
                          (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
                          _upgradeToAndCall(newImplementation, data, true);
                          return "";
                      }
                      /**
                       * @dev Returns the current admin.
                       *
                       * CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead.
                       */
                      function _admin() internal view virtual returns (address) {
                          return _getAdmin();
                      }
                      /**
                       * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
                       * emulate some proxy functions being non-payable while still allowing value to pass through.
                       */
                      function _requireZeroValue() private {
                          require(msg.value == 0);
                      }
                  }
                  // 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.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
                  pragma solidity ^0.8.2;
                  import "../beacon/IBeacon.sol";
                  import "../../interfaces/IERC1967.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._
                   */
                  abstract contract ERC1967Upgrade is IERC1967 {
                      // 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 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 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 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.9.0) (interfaces/IERC1967.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
                   *
                   * _Available since v4.8.3._
                   */
                  interface IERC1967 {
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       */
                      event Upgraded(address indexed implementation);
                      /**
                       * @dev Emitted when the admin account has changed.
                       */
                      event AdminChanged(address previousAdmin, address newAdmin);
                      /**
                       * @dev Emitted when the beacon is changed.
                       */
                      event BeaconUpgraded(address indexed beacon);
                  }
                  // 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.9.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
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.0/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.9.0) (utils/StorageSlot.sol)
                  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
                  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:
                   * ```solidity
                   * contract ERC1967 {
                   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                   *
                   *     function _getImplementation() internal view returns (address) {
                   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                   *     }
                   *
                   *     function _setImplementation(address newImplementation) internal {
                   *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                   *     }
                   * }
                   * ```
                   *
                   * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
                   * _Available since v4.9 for `string`, `bytes`._
                   */
                  library StorageSlot {
                      struct AddressSlot {
                          address value;
                      }
                      struct BooleanSlot {
                          bool value;
                      }
                      struct Bytes32Slot {
                          bytes32 value;
                      }
                      struct Uint256Slot {
                          uint256 value;
                      }
                      struct StringSlot {
                          string value;
                      }
                      struct BytesSlot {
                          bytes value;
                      }
                      /**
                       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                       */
                      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                       */
                      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                       */
                      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                       */
                      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` with member `value` located at `slot`.
                       */
                      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                       */
                      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                       */
                      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := slot
                          }
                      }
                      /**
                       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                       */
                      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                          /// @solidity memory-safe-assembly
                          assembly {
                              r.slot := store.slot
                          }
                      }
                  }
                  

                  File 6 of 10: LRTConfig
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
                  pragma solidity ^0.8.0;
                  import "./IAccessControlUpgradeable.sol";
                  import "../utils/ContextUpgradeable.sol";
                  import "../utils/StringsUpgradeable.sol";
                  import "../utils/introspection/ERC165Upgradeable.sol";
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module that allows children to implement role-based access
                   * control mechanisms. This is a lightweight version that doesn't allow enumerating role
                   * members except through off-chain means by accessing the contract event logs. Some
                   * applications may benefit from on-chain enumerability, for those cases see
                   * {AccessControlEnumerable}.
                   *
                   * Roles are referred to by their `bytes32` identifier. These should be exposed
                   * in the external API and be unique. The best way to achieve this is by
                   * using `public constant` hash digests:
                   *
                   * ```solidity
                   * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
                   * ```
                   *
                   * Roles can be used to represent a set of permissions. To restrict access to a
                   * function call, use {hasRole}:
                   *
                   * ```solidity
                   * function foo() public {
                   *     require(hasRole(MY_ROLE, msg.sender));
                   *     ...
                   * }
                   * ```
                   *
                   * Roles can be granted and revoked dynamically via the {grantRole} and
                   * {revokeRole} functions. Each role has an associated admin role, and only
                   * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
                   *
                   * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
                   * that only accounts with this role will be able to grant or revoke other
                   * roles. More complex role relationships can be created by using
                   * {_setRoleAdmin}.
                   *
                   * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
                   * grant and revoke this role. Extra precautions should be taken to secure
                   * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
                   * to enforce additional security measures for this role.
                   */
                  abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
                      function __AccessControl_init() internal onlyInitializing {
                      }
                      function __AccessControl_init_unchained() internal onlyInitializing {
                      }
                      struct RoleData {
                          mapping(address => bool) members;
                          bytes32 adminRole;
                      }
                      mapping(bytes32 => RoleData) private _roles;
                      bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                      /**
                       * @dev Modifier that checks that an account has a specific role. Reverts
                       * with a standardized message including the required role.
                       *
                       * The format of the revert reason is given by the following regular expression:
                       *
                       *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                       *
                       * _Available since v4.1._
                       */
                      modifier onlyRole(bytes32 role) {
                          _checkRole(role);
                          _;
                      }
                      /**
                       * @dev See {IERC165-supportsInterface}.
                       */
                      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                          return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
                      }
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                          return _roles[role].members[account];
                      }
                      /**
                       * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                       * Overriding this function changes the behavior of the {onlyRole} modifier.
                       *
                       * Format of the revert message is described in {_checkRole}.
                       *
                       * _Available since v4.6._
                       */
                      function _checkRole(bytes32 role) internal view virtual {
                          _checkRole(role, _msgSender());
                      }
                      /**
                       * @dev Revert with a standard message if `account` is missing `role`.
                       *
                       * The format of the revert reason is given by the following regular expression:
                       *
                       *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                       */
                      function _checkRole(bytes32 role, address account) internal view virtual {
                          if (!hasRole(role, account)) {
                              revert(
                                  string(
                                      abi.encodePacked(
                                          "AccessControl: account ",
                                          StringsUpgradeable.toHexString(account),
                                          " is missing role ",
                                          StringsUpgradeable.toHexString(uint256(role), 32)
                                      )
                                  )
                              );
                          }
                      }
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                          return _roles[role].adminRole;
                      }
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       *
                       * May emit a {RoleGranted} event.
                       */
                      function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                          _grantRole(role, account);
                      }
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                          _revokeRole(role, account);
                      }
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been revoked `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function renounceRole(bytes32 role, address account) public virtual override {
                          require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                          _revokeRole(role, account);
                      }
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event. Note that unlike {grantRole}, this function doesn't perform any
                       * checks on the calling account.
                       *
                       * May emit a {RoleGranted} event.
                       *
                       * [WARNING]
                       * ====
                       * This function should only be called from the constructor when setting
                       * up the initial roles for the system.
                       *
                       * Using this function in any other way is effectively circumventing the admin
                       * system imposed by {AccessControl}.
                       * ====
                       *
                       * NOTE: This function is deprecated in favor of {_grantRole}.
                       */
                      function _setupRole(bytes32 role, address account) internal virtual {
                          _grantRole(role, account);
                      }
                      /**
                       * @dev Sets `adminRole` as ``role``'s admin role.
                       *
                       * Emits a {RoleAdminChanged} event.
                       */
                      function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                          bytes32 previousAdminRole = getRoleAdmin(role);
                          _roles[role].adminRole = adminRole;
                          emit RoleAdminChanged(role, previousAdminRole, adminRole);
                      }
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * Internal function without access restriction.
                       *
                       * May emit a {RoleGranted} event.
                       */
                      function _grantRole(bytes32 role, address account) internal virtual {
                          if (!hasRole(role, account)) {
                              _roles[role].members[account] = true;
                              emit RoleGranted(role, account, _msgSender());
                          }
                      }
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * Internal function without access restriction.
                       *
                       * May emit a {RoleRevoked} event.
                       */
                      function _revokeRole(bytes32 role, address account) internal virtual {
                          if (hasRole(role, account)) {
                              _roles[role].members[account] = false;
                              emit RoleRevoked(role, account, _msgSender());
                          }
                      }
                      /**
                       * @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 v4.4.1 (access/IAccessControl.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev External interface of AccessControl declared to support ERC165 detection.
                   */
                  interface IAccessControlUpgradeable {
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       *
                       * _Available since v3.1._
                       */
                      event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call, an admin role
                       * bearer except when using {AccessControl-_setupRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       */
                      function renounceRole(bytes32 role, address account) external;
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.0) (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]
                   * ```solidity
                   * contract MyToken is ERC20Upgradeable {
                   *     function initialize() initializer public {
                   *         __ERC20_init("MyToken", "MTK");
                   *     }
                   * }
                   *
                   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                   *     function initializeV2() reinitializer(2) public {
                   *         __ERC20Permit_init("MyToken");
                   *     }
                   * }
                   * ```
                   *
                   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                   *
                   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                   *
                   * [CAUTION]
                   * ====
                   * Avoid leaving a contract uninitialized.
                   *
                   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * /// @custom:oz-upgrades-unsafe-allow constructor
                   * constructor() {
                   *     _disableInitializers();
                   * }
                   * ```
                   * ====
                   */
                  abstract contract Initializable {
                      /**
                       * @dev 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.9.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
                       *
                       * Furthermore, `isContract` will also return true if the target contract within
                       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                       * which only has an effect at the end of a transaction.
                       * ====
                       *
                       * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.8.0/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 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 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 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.9.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) {
                                  // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                                  // The surrounding unchecked block does not change this fact.
                                  // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                                  return prod0 / denominator;
                              }
                              // Make sure the result is less than 2^256. Also prevents denominator == 0.
                              require(denominator > prod1, "Math: mulDiv overflow");
                              ///////////////////////////////////////////////
                              // 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 256, following the selected rounding direction, of a positive value.
                       * Returns 0 if given 0.
                       */
                      function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                          unchecked {
                              uint256 result = log256(value);
                              return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Standard signed math utilities missing in the Solidity language.
                   */
                  library SignedMathUpgradeable {
                      /**
                       * @dev Returns the largest of two signed numbers.
                       */
                      function max(int256 a, int256 b) internal pure returns (int256) {
                          return a > b ? a : b;
                      }
                      /**
                       * @dev Returns the smallest of two signed numbers.
                       */
                      function min(int256 a, int256 b) internal pure returns (int256) {
                          return a < b ? a : b;
                      }
                      /**
                       * @dev Returns the average of two signed numbers without overflow.
                       * The result is rounded towards zero.
                       */
                      function average(int256 a, int256 b) internal pure returns (int256) {
                          // Formula from the book "Hacker's Delight"
                          int256 x = (a & b) + ((a ^ b) >> 1);
                          return x + (int256(uint256(x) >> 255) & (a ^ b));
                      }
                      /**
                       * @dev Returns the absolute unsigned value of a signed value.
                       */
                      function abs(int256 n) internal pure returns (uint256) {
                          unchecked {
                              // must be unchecked in order to support `n = type(int256).min`
                              return uint256(n >= 0 ? n : -n);
                          }
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
                  pragma solidity ^0.8.0;
                  import "./math/MathUpgradeable.sol";
                  import "./math/SignedMathUpgradeable.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 `int256` to its ASCII `string` decimal representation.
                       */
                      function toString(int256 value) internal pure returns (string memory) {
                          return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
                      }
                      /**
                       * @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);
                      }
                      /**
                       * @dev Returns true if the two strings are equal.
                       */
                      function equal(string memory a, string memory b) internal pure returns (bool) {
                          return keccak256(bytes(a)) == keccak256(bytes(b));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.9.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: BUSL-1.1
                  pragma solidity 0.8.21;
                  interface ILRTConfig {
                      // Errors
                      error ValueAlreadyInUse();
                      error AssetAlreadySupported();
                      error AssetNotSupported();
                      error CallerNotLRTConfigAdmin();
                      error CallerNotLRTConfigManager();
                      error CallerNotLRTConfigOperator();
                      error CallerNotLRTConfigAllowedRole(string role);
                      error CannotUpdateStrategyAsItHasFundsNDCFunds(address ndc, uint256 amount);
                      error InvalidMaxRewardAmount();
                      // Events
                      event SetToken(bytes32 key, address indexed tokenAddr);
                      event SetContract(bytes32 key, address indexed contractAddr);
                      event AddedNewSupportedAsset(address indexed asset, uint256 depositLimit);
                      event RemovedSupportedAsset(address indexed asset);
                      event AssetDepositLimitUpdate(address indexed asset, uint256 depositLimit);
                      event AssetStrategyUpdate(address indexed asset, address indexed strategy);
                      event SetRSETH(address indexed rsETH);
                      event UpdateMaxRewardAmount(uint256 maxRewardAmount);
                      // methods
                      function rsETH() external view returns (address);
                      function assetStrategy(address asset) external view returns (address);
                      function isSupportedAsset(address asset) external view returns (bool);
                      function getLSTToken(bytes32 tokenId) external view returns (address);
                      function getContract(bytes32 contractId) external view returns (address);
                      function getSupportedAssetList() external view returns (address[] memory);
                      function depositLimitByAsset(address asset) external view returns (uint256);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  interface ILRTDepositPool {
                      //errors
                      error TokenTransferFailed();
                      error InvalidAmountToDeposit();
                      error NotEnoughAssetToTransfer();
                      error MaximumDepositLimitReached();
                      error MaximumNodeDelegatorLimitReached();
                      error InvalidMaximumNodeDelegatorLimit();
                      error MinimumAmountToReceiveNotMet();
                      error NodeDelegatorNotFound();
                      error NodeDelegatorHasAssetBalance(address assetAddress, uint256 assetBalance);
                      error NodeDelegatorHasETH();
                      //events
                      event MaxNodeDelegatorLimitUpdated(uint256 maxNodeDelegatorLimit);
                      event NodeDelegatorAddedinQueue(address[] nodeDelegatorContracts);
                      event NodeDelegatorRemovedFromQueue(address nodeDelegatorContracts);
                      event AssetDeposit(
                          address indexed depositor,
                          address indexed asset,
                          uint256 depositAmount,
                          uint256 rsethMintAmount,
                          string referralId
                      );
                      event ETHDeposit(address indexed depositor, uint256 depositAmount, uint256 rsethMintAmount, string referralId);
                      event MinAmountToDepositUpdated(uint256 minAmountToDeposit);
                      event ETHSwappedForLST(uint256 ethAmount, address indexed toAsset, uint256 returnAmount);
                      function depositAsset(
                          address asset,
                          uint256 depositAmount,
                          uint256 minRSETHAmountExpected,
                          string calldata referralId
                      )
                          external;
                      function getTotalAssetDeposits(address asset) external view returns (uint256);
                      function getAssetCurrentLimit(address asset) external view returns (uint256);
                      function getRsETHAmountToMint(address asset, uint256 depositAmount) external view returns (uint256);
                      function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContract) external;
                      function transferAssetToNodeDelegator(uint256 ndcIndex, address asset, uint256 amount) external;
                      function updateMaxNodeDelegatorLimit(uint256 maxNodeDelegatorLimit) external;
                      function getNodeDelegatorQueue() external view returns (address[] memory);
                      function getAssetDistributionData(address asset)
                          external
                          view
                          returns (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer);
                      function getETHDistributionData()
                          external
                          view
                          returns (uint256 ethLyingInDepositPool, uint256 ethLyingInNDCs, uint256 ethStakedInEigenLayer);
                  }
                  // 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);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  import { UtilLib } from "./utils/UtilLib.sol";
                  import { LRTConstants } from "./utils/LRTConstants.sol";
                  import { ILRTConfig } from "./interfaces/ILRTConfig.sol";
                  import { IStrategy } from "./interfaces/IStrategy.sol";
                  import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol";
                  import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
                  /// @title LRTConfig - LRT Config Contract
                  /// @notice Handles LRT configuration
                  contract LRTConfig is ILRTConfig, AccessControlUpgradeable {
                      mapping(bytes32 tokenKey => address tokenAddress) public tokenMap;
                      mapping(bytes32 contractKey => address contractAddress) public contractMap;
                      mapping(address token => bool isSupported) public isSupportedAsset;
                      mapping(address token => uint256 amount) public depositLimitByAsset;
                      mapping(address token => address strategy) public override assetStrategy;
                      address[] public supportedAssetList;
                      address public rsETH;
                      /// @custom:oz-upgrades-unsafe-allow constructor
                      constructor() {
                          _disableInitializers();
                      }
                      modifier onlySupportedAsset(address asset) {
                          if (!isSupportedAsset[asset]) {
                              revert AssetNotSupported();
                          }
                          _;
                      }
                      /// @dev Initializes the contract
                      /// @param admin Admin address
                      /// @param stETH stETH address
                      /// @param ethX ETHX address
                      /// @param rsETH_ rsETH address
                      function initialize(address admin, address stETH, address ethX, address rsETH_) external initializer {
                          UtilLib.checkNonZeroAddress(admin);
                          UtilLib.checkNonZeroAddress(rsETH_);
                          __AccessControl_init();
                          _setToken(LRTConstants.ST_ETH_TOKEN, stETH);
                          _setToken(LRTConstants.ETHX_TOKEN, ethX);
                          _addNewSupportedAsset(stETH, 100_000 ether);
                          _addNewSupportedAsset(ethX, 100_000 ether);
                          _grantRole(DEFAULT_ADMIN_ROLE, admin);
                          rsETH = rsETH_;
                      }
                      /// @dev Adds a new supported asset
                      /// @param asset Asset address
                      /// @param depositLimit Deposit limit for the asset
                      function addNewSupportedAsset(address asset, uint256 depositLimit) external onlyRole(DEFAULT_ADMIN_ROLE) {
                          _addNewSupportedAsset(asset, depositLimit);
                      }
                      /// @dev private function to add a new supported asset
                      /// @param asset Asset address
                      /// @param depositLimit Deposit limit for the asset
                      function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
                          UtilLib.checkNonZeroAddress(asset);
                          if (isSupportedAsset[asset]) {
                              revert AssetAlreadySupported();
                          }
                          isSupportedAsset[asset] = true;
                          supportedAssetList.push(asset);
                          depositLimitByAsset[asset] = depositLimit;
                          emit AddedNewSupportedAsset(asset, depositLimit);
                      }
                      /// @dev Updates the deposit limit for an asset
                      /// @param asset Asset address
                      /// @param depositLimit New deposit limit
                      function updateAssetDepositLimit(
                          address asset,
                          uint256 depositLimit
                      )
                          external
                          onlyRole(LRTConstants.MANAGER)
                          onlySupportedAsset(asset)
                      {
                          depositLimitByAsset[asset] = depositLimit;
                          emit AssetDepositLimitUpdate(asset, depositLimit);
                      }
                      /// @dev Updates the strategy for an asset
                      /// @param asset Asset address
                      /// @param strategy New strategy address
                      function updateAssetStrategy(
                          address asset,
                          address strategy
                      )
                          external
                          onlyRole(DEFAULT_ADMIN_ROLE)
                          onlySupportedAsset(asset)
                      {
                          UtilLib.checkNonZeroAddress(strategy);
                          if (assetStrategy[asset] == strategy) {
                              revert ValueAlreadyInUse();
                          }
                          // if strategy is already set, check if it has any funds
                          if (assetStrategy[asset] != address(0)) {
                              // get ndcs
                              address depositPool = getContract(LRTConstants.LRT_DEPOSIT_POOL);
                              address[] memory ndcs = ILRTDepositPool(depositPool).getNodeDelegatorQueue();
                              uint256 length = ndcs.length;
                              for (uint256 i = 0; i < length;) {
                                  uint256 ndcBalance = IStrategy(assetStrategy[asset]).userUnderlyingView(ndcs[i]);
                                  if (ndcBalance > 0) {
                                      revert CannotUpdateStrategyAsItHasFundsNDCFunds(ndcs[i], ndcBalance);
                                  }
                                  unchecked {
                                      ++i;
                                  }
                              }
                          }
                          assetStrategy[asset] = strategy;
                          emit AssetStrategyUpdate(asset, strategy);
                      }
                      /*//////////////////////////////////////////////////////////////
                                              GETTERS
                      //////////////////////////////////////////////////////////////*/
                      function getLSTToken(bytes32 tokenKey) external view override returns (address) {
                          return tokenMap[tokenKey];
                      }
                      function getContract(bytes32 contractKey) public view override returns (address) {
                          return contractMap[contractKey];
                      }
                      function getSupportedAssetList() external view override returns (address[] memory) {
                          return supportedAssetList;
                      }
                      /*//////////////////////////////////////////////////////////////
                                              SETTERS
                      //////////////////////////////////////////////////////////////*/
                      /// @dev Sets the rsETH contract address. Only callable by the admin
                      /// @param rsETH_ rsETH contract address
                      function setRSETH(address rsETH_) external onlyRole(DEFAULT_ADMIN_ROLE) {
                          UtilLib.checkNonZeroAddress(rsETH_);
                          rsETH = rsETH_;
                          emit SetRSETH(rsETH_);
                      }
                      function setToken(bytes32 tokenKey, address assetAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
                          _setToken(tokenKey, assetAddress);
                      }
                      /// @dev private function to set a token
                      /// @param key Token key
                      /// @param val Token address
                      function _setToken(bytes32 key, address val) private {
                          UtilLib.checkNonZeroAddress(val);
                          if (tokenMap[key] == val) {
                              revert ValueAlreadyInUse();
                          }
                          tokenMap[key] = val;
                          emit SetToken(key, val);
                      }
                      function setContract(bytes32 contractKey, address contractAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
                          _setContract(contractKey, contractAddress);
                      }
                      /// @dev private function to set a contract
                      /// @param key Contract key
                      /// @param val Contract address
                      function _setContract(bytes32 key, address val) private {
                          UtilLib.checkNonZeroAddress(val);
                          if (contractMap[key] == val) {
                              revert ValueAlreadyInUse();
                          }
                          contractMap[key] = val;
                          emit SetContract(key, val);
                      }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  library LRTConstants {
                      //tokens
                      //rETH token
                      bytes32 public constant R_ETH_TOKEN = keccak256("R_ETH_TOKEN");
                      //stETH token
                      bytes32 public constant ST_ETH_TOKEN = keccak256("ST_ETH_TOKEN");
                      //cbETH token
                      bytes32 public constant CB_ETH_TOKEN = keccak256("CB_ETH_TOKEN");
                      //ETHX token
                      bytes32 public constant ETHX_TOKEN = keccak256("ETHX_TOKEN");
                      //contracts
                      bytes32 public constant LRT_ORACLE = keccak256("LRT_ORACLE");
                      bytes32 public constant LRT_DEPOSIT_POOL = keccak256("LRT_DEPOSIT_POOL");
                      bytes32 public constant EIGEN_STRATEGY_MANAGER = keccak256("EIGEN_STRATEGY_MANAGER");
                      //Roles
                      bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                      bytes32 public constant MANAGER = keccak256("MANAGER");
                      bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
                      bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
                      // updated library variables
                      bytes32 public constant SFRXETH_TOKEN = keccak256("SFRXETH_TOKEN");
                      // add new vars below
                      bytes32 public constant EIGEN_POD_MANAGER = keccak256("EIGEN_POD_MANAGER");
                      // native ETH as ERC20 for ease of implementation
                      address public constant ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                      // Operator Role
                      bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity 0.8.21;
                  /// @title UtilLib - Utility library
                  /// @notice Utility functions
                  library UtilLib {
                      error ZeroAddressNotAllowed();
                      /// @dev zero address check modifier
                      /// @param address_ address to check
                      function checkNonZeroAddress(address address_) internal pure {
                          if (address_ == address(0)) revert ZeroAddressNotAllowed();
                      }
                  }
                  

                  File 7 of 10: TransparentUpgradeableProxy
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
                  pragma solidity ^0.8.0;
                  import "../ERC1967/ERC1967Proxy.sol";
                  /**
                   * @dev This contract implements a proxy that is upgradeable by an admin.
                   *
                   * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                   * clashing], which can potentially be used in an attack, this contract uses the
                   * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                   * things that go hand in hand:
                   *
                   * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                   * that call matches one of the admin functions exposed by the proxy itself.
                   * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                   * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                   * "admin cannot fallback to proxy target".
                   *
                   * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                   * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                   * to sudden errors when trying to call a function from the proxy implementation.
                   *
                   * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                   * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                   */
                  contract TransparentUpgradeableProxy is ERC1967Proxy {
                      /**
                       * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                       * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                       */
                      constructor(
                          address _logic,
                          address admin_,
                          bytes memory _data
                      ) payable ERC1967Proxy(_logic, _data) {
                          _changeAdmin(admin_);
                      }
                      /**
                       * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                       */
                      modifier ifAdmin() {
                          if (msg.sender == _getAdmin()) {
                              _;
                          } else {
                              _fallback();
                          }
                      }
                      /**
                       * @dev Returns the current admin.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function admin() external ifAdmin returns (address admin_) {
                          admin_ = _getAdmin();
                      }
                      /**
                       * @dev Returns the current implementation.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                       */
                      function implementation() external ifAdmin returns (address implementation_) {
                          implementation_ = _implementation();
                      }
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {AdminChanged} event.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                       */
                      function changeAdmin(address newAdmin) external virtual ifAdmin {
                          _changeAdmin(newAdmin);
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                       */
                      function upgradeTo(address newImplementation) external ifAdmin {
                          _upgradeToAndCall(newImplementation, bytes(""), false);
                      }
                      /**
                       * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                       * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                       * proxied contract.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                       */
                      function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                          _upgradeToAndCall(newImplementation, data, true);
                      }
                      /**
                       * @dev Returns the current admin.
                       */
                      function _admin() internal view virtual returns (address) {
                          return _getAdmin();
                      }
                      /**
                       * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                       */
                      function _beforeFallback() internal virtual override {
                          require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                          super._beforeFallback();
                      }
                  }
                  // 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.7.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 functionCall(target, data, "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");
                          require(isContract(target), "Address: call to non-contract");
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: static call to non-contract");
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: delegate call to non-contract");
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason 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 {
                              // 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 8 of 10: EigenPodManager
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "@openzeppelin/contracts/utils/Create2.sol";
                  import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
                  import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
                  import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
                  import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
                  import "../interfaces/IStrategyManager.sol";
                  import "../interfaces/IDelegationManager.sol";
                  import "../interfaces/IEigenPodManager.sol";
                  import "../interfaces/IETHPOSDeposit.sol";
                  import "../interfaces/IEigenPod.sol";
                  import "../interfaces/IBeaconChainOracle.sol";
                  import "../permissions/Pausable.sol";
                  import "./EigenPodPausingConstants.sol";
                  /**
                   * @title The contract used for creating and managing EigenPods
                   * @author Layr Labs, Inc.
                   * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                   * @notice The main functionalities are:
                   * - creating EigenPods
                   * - staking for new validators on EigenPods
                   * - keeping track of the balances of all validators of EigenPods, and their stake in EigenLayer
                   * - withdrawing eth when withdrawals are initiated
                   */
                  contract EigenPodManager is Initializable, OwnableUpgradeable, Pausable, IEigenPodManager, EigenPodPausingConstants {
                      /**
                       * @notice Stored code of type(BeaconProxy).creationCode
                       * @dev Maintained as a constant to solve an edge case - changes to OpenZeppelin's BeaconProxy code should not cause
                       * addresses of EigenPods that are pre-computed with Create2 to change, even upon upgrading this contract, changing compiler version, etc.
                      */
                      bytes internal constant beaconProxyBytecode = hex"608060405260405161090e38038061090e83398101604081905261002291610460565b61002e82826000610035565b505061058a565b61003e83610100565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100fb576100f9836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e99190610520565b836102a360201b6100291760201c565b505b505050565b610113816102cf60201b6100551760201c565b6101725760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101e6816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d79190610520565b6102cf60201b6100551760201c565b61024b5760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610169565b806102827fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b6102de60201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b60606102c883836040518060600160405280602781526020016108e7602791396102e1565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b0316856040516102fe919061053b565b600060405180830381855af49150503d8060008114610339576040519150601f19603f3d011682016040523d82523d6000602084013e61033e565b606091505b5090925090506103508683838761035a565b9695505050505050565b606083156103c65782516103bf576001600160a01b0385163b6103bf5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610169565b50816103d0565b6103d083836103d8565b949350505050565b8151156103e85781518083602001fd5b8060405162461bcd60e51b81526004016101699190610557565b80516001600160a01b038116811461041957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561044f578181015183820152602001610437565b838111156100f95750506000910152565b6000806040838503121561047357600080fd5b61047c83610402565b60208401519092506001600160401b038082111561049957600080fd5b818501915085601f8301126104ad57600080fd5b8151818111156104bf576104bf61041e565b604051601f8201601f19908116603f011681019083821181831017156104e7576104e761041e565b8160405282815288602084870101111561050057600080fd5b610511836020830160208801610434565b80955050505050509250929050565b60006020828403121561053257600080fd5b6102c882610402565b6000825161054d818460208701610434565b9190910192915050565b6020815260008251806020840152610576816040850160208701610434565b601f01601f19169190910160400192915050565b61034e806105996000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102f260279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb9190610249565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b03168560405161014191906102a2565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020d578251610206576001600160a01b0385163b6102065760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610217565b610217838361021f565b949350505050565b81511561022f5781518083602001fd5b8060405162461bcd60e51b81526004016101fd91906102be565b60006020828403121561025b57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028d578181015183820152602001610275565b8381111561029c576000848401525b50505050565b600082516102b4818460208701610272565b9190910192915050565b60208152600082518060208401526102dd816040850160208701610272565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220d51e81d3bc5ed20a26aeb05dce7e825c503b2061aa78628027300c8d65b9d89a64736f6c634300080c0033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564";
                      /// @notice The ETH2 Deposit Contract
                      IETHPOSDeposit public immutable ethPOS;
                      
                      /// @notice Beacon proxy to which the EigenPods point
                      IBeacon public immutable eigenPodBeacon;
                      /// @notice EigenLayer's StrategyManager contract
                      IStrategyManager public immutable strategyManager;
                      /// @notice EigenLayer's Slasher contract
                      ISlasher public immutable slasher;
                      /// @notice Oracle contract that provides updates to the beacon chain's state
                      IBeaconChainOracle public beaconChainOracle;
                      
                      /// @notice Pod owner to deployed EigenPod address
                      mapping(address => IEigenPod) public ownerToPod;
                      // BEGIN STORAGE VARIABLES ADDED AFTER FIRST TESTNET DEPLOYMENT -- DO NOT SUGGEST REORDERING TO CONVENTIONAL ORDER
                      /// @notice The number of EigenPods that have been deployed
                      uint256 public numPods;
                      /// @notice The maximum number of EigenPods that can be deployed
                      uint256 public maxPods;
                      /// @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);
                      modifier onlyEigenPod(address podOwner) {
                          require(address(ownerToPod[podOwner]) == msg.sender, "EigenPodManager.onlyEigenPod: not a pod");
                          _;
                      }
                      modifier onlyStrategyManager {
                          require(msg.sender == address(strategyManager), "EigenPodManager.onlyStrategyManager: not strategyManager");
                          _;
                      }
                      constructor(IETHPOSDeposit _ethPOS, IBeacon _eigenPodBeacon, IStrategyManager _strategyManager, ISlasher _slasher) {
                          ethPOS = _ethPOS;
                          eigenPodBeacon = _eigenPodBeacon;
                          strategyManager = _strategyManager;
                          slasher = _slasher;
                          _disableInitializers();
                      }
                      function initialize(
                          uint256 _maxPods,
                          IBeaconChainOracle _beaconChainOracle,
                          address initialOwner,
                          IPauserRegistry _pauserRegistry,
                          uint256 _initPausedStatus
                      ) external initializer {
                          _setMaxPods(_maxPods);
                          _updateBeaconChainOracle(_beaconChainOracle);
                          _transferOwnership(initialOwner);
                          _initializePauser(_pauserRegistry, _initPausedStatus);
                      }
                      
                      /**
                       * @notice Creates an EigenPod for the sender.
                       * @dev Function will revert if the `msg.sender` already has an EigenPod.
                       */
                      function createPod() external {
                          require(!hasPod(msg.sender), "EigenPodManager.createPod: Sender already has a pod");
                          // deploy a pod if the sender doesn't have one already
                          _deployPod();
                      }
                      /**
                       * @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 {
                          IEigenPod pod = ownerToPod[msg.sender];
                          if(address(pod) == address(0)) {
                              //deploy a pod if the sender doesn't have one already
                              pod = _deployPod();
                          }
                          pod.stake{value: msg.value}(pubkey, signature, depositDataRoot);
                      }
                      /**
                       * @notice Deposits/Restakes beacon chain ETH in EigenLayer on behalf of the owner of an EigenPod.
                       * @param podOwner The owner of the pod whose balance must be deposited.
                       * @param amount The amount of ETH to 'deposit' (i.e. be credited to the podOwner).
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function restakeBeaconChainETH(address podOwner, uint256 amount) external onlyEigenPod(podOwner) {
                          strategyManager.depositBeaconChainETH(podOwner, amount);
                          emit BeaconChainETHDeposited(podOwner, amount);
                      }
                      /**
                       * @notice Removes beacon chain ETH from EigenLayer on behalf of the owner of an EigenPod, when the
                       *         balance of a validator is lower than how much stake they have committed to EigenLayer
                       * @param podOwner The owner of the pod whose balance must be removed.
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwner's strategies
                       * @param amount The amount of beacon chain ETH to decrement from the podOwner's shares in the strategyManager.
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function recordOvercommittedBeaconChainETH(address podOwner, uint256 beaconChainETHStrategyIndex, uint256 amount) external onlyEigenPod(podOwner) {
                          strategyManager.recordOvercommittedBeaconChainETH(podOwner, beaconChainETHStrategyIndex, amount);
                      }
                      /**
                       * @notice Withdraws ETH from an EigenPod. The ETH must have first been withdrawn from the beacon chain.
                       * @param podOwner The owner of the pod whose balance must be withdrawn.
                       * @param recipient The recipient of the withdrawn ETH.
                       * @param amount The amount of ETH to withdraw.
                       * @dev Callable only by the StrategyManager contract.
                       */
                      function withdrawRestakedBeaconChainETH(address podOwner, address recipient, uint256 amount)
                          external onlyStrategyManager onlyWhenNotPaused(PAUSED_WITHDRAW_RESTAKED_ETH)
                      {
                          ownerToPod[podOwner].withdrawRestakedBeaconChainETH(recipient, amount);
                      }
                      /**
                       * Sets the maximum number of pods that can be deployed
                       * @param newMaxPods The new maximum number of pods that can be deployed
                       * @dev Callable by the unpauser of this contract
                       */
                      function setMaxPods(uint256 newMaxPods) external onlyUnpauser {
                          _setMaxPods(newMaxPods);
                      }
                      /**
                       * @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 onlyOwner {
                          _updateBeaconChainOracle(newBeaconChainOracle);
                      }
                      // INTERNAL FUNCTIONS
                      function _deployPod() internal onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) returns (IEigenPod) {
                          // check that the limit of EigenPods has not been hit, and increment the EigenPod count
                          require(numPods + 1 <= maxPods, "EigenPodManager._deployPod: pod limit reached");
                          ++numPods;
                          // create the pod
                          IEigenPod pod = 
                              IEigenPod(
                                  Create2.deploy(
                                      0, 
                                      bytes32(uint256(uint160(msg.sender))), 
                                      // set the beacon address to the eigenPodBeacon and initialize it
                                      abi.encodePacked(
                                          beaconProxyBytecode, 
                                          abi.encode(eigenPodBeacon, "")
                                      )
                                  )
                              );
                          pod.initialize(msg.sender);
                          // store the pod in the mapping
                          ownerToPod[msg.sender] = pod;
                          emit PodDeployed(address(pod), msg.sender);
                          return pod;
                      }
                      /// @notice Internal setter for `beaconChainOracle` that also emits an event
                      function _updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) internal {
                          beaconChainOracle = newBeaconChainOracle;
                          emit BeaconOracleUpdated(address(newBeaconChainOracle));
                      }
                      /// @notice Internal setter for `maxPods` that also emits an event
                      function _setMaxPods(uint256 _maxPods) internal {
                          emit MaxPodsUpdated(maxPods, _maxPods);
                          maxPods = _maxPods;
                      }
                      // VIEW FUNCTIONS
                      /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
                      function getPod(address podOwner) public view returns (IEigenPod) {
                          IEigenPod pod = ownerToPod[podOwner];
                          // if pod does not exist already, calculate what its address *will be* once it is deployed
                          if (address(pod) == address(0)) {
                              pod = IEigenPod(
                                  Create2.computeAddress(
                                      bytes32(uint256(uint160(podOwner))), //salt
                                      keccak256(abi.encodePacked(
                                          beaconProxyBytecode, 
                                          abi.encode(eigenPodBeacon, "")
                                      )) //bytecode
                                  ));
                          }
                          return pod;
                      }
                      /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
                      function hasPod(address podOwner) public view returns (bool) {
                          return address(ownerToPod[podOwner]) != address(0);
                      }
                      /// @notice Returns the Beacon Chain state root at `blockNumber`. Reverts if the Beacon Chain state root at `blockNumber` has not yet been finalized.
                      function getBeaconChainStateRoot(uint64 blockNumber) external view returns(bytes32) {
                          bytes32 stateRoot = beaconChainOracle.beaconStateRootAtBlockNumber(blockNumber);
                          require(stateRoot != bytes32(0), "EigenPodManager.getBeaconChainStateRoot: state root at blockNumber not yet finalized");
                          return stateRoot;
                      }
                      /**
                       * @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[46] private __gap;
                  }// SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (utils/Create2.sol)
                  pragma solidity ^0.8.0;
                  /**
                   * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
                   * `CREATE2` can be used to compute in advance the address where a smart
                   * contract will be deployed, which allows for interesting new mechanisms known
                   * as 'counterfactual interactions'.
                   *
                   * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
                   * information.
                   */
                  library Create2 {
                      /**
                       * @dev Deploys a contract using `CREATE2`. The address where the contract
                       * will be deployed can be known in advance via {computeAddress}.
                       *
                       * The bytecode for a contract can be obtained from Solidity with
                       * `type(contractName).creationCode`.
                       *
                       * Requirements:
                       *
                       * - `bytecode` must not be empty.
                       * - `salt` must have not been used for `bytecode` already.
                       * - the factory must have a balance of at least `amount`.
                       * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
                       */
                      function deploy(
                          uint256 amount,
                          bytes32 salt,
                          bytes memory bytecode
                      ) internal returns (address) {
                          address addr;
                          require(address(this).balance >= amount, "Create2: insufficient balance");
                          require(bytecode.length != 0, "Create2: bytecode length is zero");
                          /// @solidity memory-safe-assembly
                          assembly {
                              addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                          }
                          require(addr != address(0), "Create2: Failed on deploy");
                          return addr;
                      }
                      /**
                       * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
                       * `bytecodeHash` or `salt` will result in a new destination address.
                       */
                      function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
                          return computeAddress(salt, bytecodeHash, address(this));
                      }
                      /**
                       * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
                       * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
                       */
                      function computeAddress(
                          bytes32 salt,
                          bytes32 bytecodeHash,
                          address deployer
                      ) internal pure returns (address) {
                          bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
                          return address(uint160(uint256(_data)));
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
                  pragma solidity ^0.8.0;
                  import "./IBeacon.sol";
                  import "../Proxy.sol";
                  import "../ERC1967/ERC1967Upgrade.sol";
                  /**
                   * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
                   *
                   * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
                   * conflict with the storage layout of the implementation behind the proxy.
                   *
                   * _Available since v3.4._
                   */
                  contract BeaconProxy is Proxy, ERC1967Upgrade {
                      /**
                       * @dev Initializes the proxy with `beacon`.
                       *
                       * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                       * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
                       * constructor.
                       *
                       * Requirements:
                       *
                       * - `beacon` must be a contract with the interface {IBeacon}.
                       */
                      constructor(address beacon, bytes memory data) payable {
                          _upgradeBeaconToAndCall(beacon, data, false);
                      }
                      /**
                       * @dev Returns the current beacon address.
                       */
                      function _beacon() internal view virtual returns (address) {
                          return _getBeacon();
                      }
                      /**
                       * @dev Returns the current implementation address of the associated beacon.
                       */
                      function _implementation() internal view virtual override returns (address) {
                          return IBeacon(_getBeacon()).implementation();
                      }
                      /**
                       * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                       *
                       * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                       *
                       * Requirements:
                       *
                       * - `beacon` must be a contract.
                       * - The implementation returned by `beacon` must be a contract.
                       */
                      function _setBeacon(address beacon, bytes memory data) internal virtual {
                          _upgradeBeaconToAndCall(beacon, data, false);
                      }
                  }
                  // 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.7.0) (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. Equivalent to `reinitializer(1)`.
                       */
                      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.
                       *
                       * `initializer` is equivalent to `reinitializer(1)`, so 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.
                       *
                       * 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.
                       */
                      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.
                       */
                      function _disableInitializers() internal virtual {
                          require(!_initializing, "Initializable: contract is initializing");
                          if (_initialized < type(uint8).max) {
                              _initialized = type(uint8).max;
                              emit Initialized(type(uint8).max);
                          }
                      }
                  }
                  // 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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.8.12;
                  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.12;
                  import "./IStrategyManager.sol";
                  import "./IEigenPod.sol";
                  import "./IBeaconChainOracle.sol";
                  import "./IPausable.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 Creates an EigenPod for the sender.
                       * @dev Function will revert if the `msg.sender` already has an EigenPod.
                       */
                      function createPod() external;
                      /**
                       * @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 Deposits/Restakes beacon chain ETH in EigenLayer on behalf of the owner of an EigenPod.
                       * @param podOwner The owner of the pod whose balance must be deposited.
                       * @param amount The amount of ETH to 'deposit' (i.e. be credited to the podOwner).
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function restakeBeaconChainETH(address podOwner, uint256 amount) external;
                      /**
                       * @notice Removes beacon chain ETH from EigenLayer on behalf of the owner of an EigenPod, when the
                       *         balance of a validator is lower than how much stake they have committed to EigenLayer
                       * @param podOwner The owner of the pod whose balance must be removed.
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwner's strategies
                       * @param amount The amount of ETH to remove.
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function recordOvercommittedBeaconChainETH(address podOwner, uint256 beaconChainETHStrategyIndex, uint256 amount) external;
                      
                      /**
                       * @notice Withdraws ETH from an EigenPod. The ETH must have first been withdrawn from the beacon chain.
                       * @param podOwner The owner of the pod whose balance must be withdrawn.
                       * @param recipient The recipient of the withdrawn ETH.
                       * @param amount The amount of ETH to withdraw.
                       * @dev Callable only by the StrategyManager contract.
                       */
                      function withdrawRestakedBeaconChainETH(address podOwner, address recipient, uint256 amount) 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 Oracle contract that provides updates to the beacon chain's state
                      function beaconChainOracle() external view returns(IBeaconChainOracle);    
                      /// @notice Returns the Beacon Chain state root at `blockNumber`. Reverts if the Beacon Chain state root at `blockNumber` has not yet been finalized.
                      function getBeaconChainStateRoot(uint64 blockNumber) 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);
                      function hasPod(address podOwner) external view returns (bool);
                  }// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                  // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                  // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                  // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                  // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                  // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  // SPDX-License-Identifier: CC0-1.0
                  pragma solidity =0.8.12;
                  // 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.8.12;
                  import "../libraries/BeaconChainProofs.sol";
                  import "./IEigenPodManager.sol";
                  import "./IBeaconChainOracle.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
                          OVERCOMMITTED, // proven to be overcommitted to EigenLayer
                          WITHDRAWN // withdrawn from the Beacon Chain
                      }
                      // this struct keeps track of PartialWithdrawalClaims
                      struct PartialWithdrawalClaim {
                          PARTIAL_WITHDRAWAL_CLAIM_STATUS status;
                          // block at which the PartialWithdrawalClaim was created
                          uint32 creationBlockNumber;
                          // last block (inclusive) in which the PartialWithdrawalClaim can be fraudproofed
                          uint32 fraudproofPeriodEndBlockNumber;
                          // amount of ETH -- in Gwei -- to be withdrawn until completion of this claim
                          uint64 partialWithdrawalAmountGwei;
                      }
                      enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
                          REDEEMED,
                          PENDING,
                          FAILED
                      }
                      /// @notice The amount of eth, in gwei, that is restaked per validator
                      function REQUIRED_BALANCE_GWEI() external view returns(uint64);
                      /// @notice The amount of eth, in wei, that is restaked per validator
                      function REQUIRED_BALANCE_WEI() external view returns(uint256);
                      /// @notice this is a mapping of validator indices to a Validator struct containing pertinent info about the validator
                      function validatorStatus(uint40 validatorIndex) external view returns(VALIDATOR_STATUS);
                      /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer), 
                      function restakedExecutionLayerGwei() external view returns(uint64);
                      /// @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 Called during withdrawal or slashing.
                       * @dev Note that this function is marked as non-reentrant to prevent the recipient calling back into it
                       */
                      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 block number of the most recent withdrawal
                      function mostRecentWithdrawalBlockNumber() external view returns (uint64);
                      ///@notice mapping that tracks proven partial withdrawals
                      function provenPartialWithdrawal(uint40 validatorIndex, uint64 slot) external view returns (bool);
                      /**
                       * @notice This function verifies that the withdrawal credentials of the podOwner are pointed to
                       * this contract. It also verifies the current (not 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 oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the bytes that prove the 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 verifyWithdrawalCredentialsAndBalance(
                          uint64 oracleBlockNumber,
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs memory proofs,
                          bytes32[] calldata validatorFields
                      ) external;
                      
                      /**
                       * @notice This function records an overcommitment of stake to EigenLayer on behalf of a certain ETH validator.
                       *         If successful, the overcommitted balance is penalized (available for withdrawal whenever the pod's balance allows).
                       *         The ETH validator's shares in the enshrined beaconChainETH strategy are also removed from the StrategyManager and undelegated.
                       * @param oracleBlockNumber The oracleBlockNumber whose state root the `proof` will be proven against.
                       *        Must be within `VERIFY_OVERCOMMITTED_WINDOW_BLOCKS` of the current block.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the proof of the validator's balance and validatorFields in the balance tree and the balanceRoot to prove for
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwners strategies
                       * @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 verifyOvercommittedStake(
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs calldata proofs,
                          bytes32[] calldata validatorFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      ) external;
                      /**
                       * @notice This function records a full withdrawal on behalf of one of the Ethereum validators for this EigenPod
                       * @param withdrawalProofs is the information needed to check the veracity of the block number and withdrawal being proven
                       * @param validatorFieldsProof is the proof of the validator's fields in the validator tree
                       * @param withdrawalFields are the fields of the withdrawal being proven
                       * @param validatorFields are the fields of the validator being proven
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *        the EigenPodManager to the StrategyManager in case it must be removed from the podOwner's list of strategies
                       */
                      function verifyAndProcessWithdrawal(
                          BeaconChainProofs.WithdrawalProofs calldata withdrawalProofs, 
                          bytes calldata validatorFieldsProof,
                          bytes32[] calldata validatorFields,
                          bytes32[] calldata withdrawalFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      ) external;
                      /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                      function withdrawBeforeRestaking() external;
                  }// SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @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 Largest blockNumber that has been confirmed by the oracle.
                      function latestConfirmedOracleBlockNumber() external view returns(uint64);
                      /// @notice Mapping: Beacon Chain blockNumber => the Beacon Chain state root at the specified blockNumber.
                      /// @dev This will return `bytes32(0)` if the state root at the specified blockNumber is not yet confirmed.
                      function beaconStateRootAtBlockNumber(uint64 blockNumber) external view returns(bytes32);
                      /// @notice Mapping: address => whether or not the address is in the set of oracle signers.
                      function isOracleSigner(address _oracleSigner) external view returns(bool);
                      /// @notice Mapping: Beacon Chain blockNumber => oracle signer address => whether or not the oracle signer has voted on the state root at the blockNumber.
                      function hasVoted(uint64 blockNumber, address oracleSigner) external view returns(bool);
                      /// @notice Mapping: Beacon Chain blockNumber => state root => total number of oracle signer votes for the state root at the blockNumber. 
                      function stateRootVotes(uint64 blockNumber, bytes32 stateRoot) external view returns(uint256);
                      /// @notice Total number of members of the set of oracle signers.
                      function totalOracleSigners() external view returns(uint256);
                      /**
                       * @notice Number of oracle signers that must vote for a state root in order for the state root to be confirmed.
                       * Adjustable by this contract's owner through use of the `setThreshold` function.
                       * @dev We note that there is an edge case -- when the threshold is adjusted downward, if a state root already has enough votes to meet the *new* threshold,
                       * the state root must still receive one additional vote from an oracle signer to be confirmed. This behavior is intended, to minimize unexpected root confirmations.
                       */
                      function threshold() external view returns(uint256);
                      /**
                       * @notice Owner-only function used to modify the value of the `threshold` variable.
                       * @param _threshold Desired new value for the `threshold` variable. Function will revert if this is set to zero.
                       */
                      function setThreshold(uint256 _threshold) external;
                      /**
                       * @notice Owner-only function used to add a signer to the set of oracle signers.
                       * @param _oracleSigners Array of address to be added to the set.
                       * @dev Function will have no effect on the i-th input address if `_oracleSigners[i]`is already in the set of oracle signers.
                       */
                      function addOracleSigners(address[] memory _oracleSigners) external;
                      /**
                       * @notice Owner-only function used to remove a signer from the set of oracle signers.
                       * @param _oracleSigners Array of address to be removed from the set.
                       * @dev Function will have no effect on the i-th input address if `_oracleSigners[i]`is already not in the set of oracle signers.
                       */
                      function removeOracleSigners(address[] memory _oracleSigners) external;
                      /**
                       * @notice Called by a member of the set of oracle signers to assert that the Beacon Chain state root is `stateRoot` at `blockNumber`.
                       * @dev The state root will be finalized once the total number of votes *for this exact state root at this exact blockNumber* meets the `threshold` value.
                       * @param blockNumber The Beacon Chain blockNumber of interest.
                       * @param stateRoot The Beacon Chain state root that the caller asserts was the correct root, at the specified `blockNumber`.
                       */
                      function voteForBeaconChainStateRoot(uint64 blockNumber, bytes32 stateRoot) external;
                  }// SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "../interfaces/IPausable.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
                   */
                  contract Pausable is IPausable {
                      /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
                      IPauserRegistry public pauserRegistry;
                      /// @dev whether or not the contract is currently paused
                      uint256 private _paused;
                      uint256 constant internal UNPAUSE_ALL = 0;
                      uint256 constant internal PAUSE_ALL = type(uint256).max;
                      /// @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
                      modifier onlyPauser() {
                          require(pauserRegistry.isPauser(msg.sender), "msg.sender is not permissioned as pauser");
                          _;
                      }
                      modifier onlyUnpauser() {
                          require(msg.sender == pauserRegistry.unpauser(), "msg.sender is not permissioned as unpauser");
                          _;
                      }
                      /// @notice Throws if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1.
                      modifier whenNotPaused() {
                          require(_paused == 0, "Pausable: contract is paused");
                          _;
                      }
                      /// @notice Throws if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped.
                      modifier onlyWhenNotPaused(uint8 index) {
                          require(!paused(index), "Pausable: index is paused");
                          _;
                      }
                      /// @notice One-time function for setting the `pauserRegistry` and initializing the value of `_paused`.
                      function _initializePauser(IPauserRegistry _pauserRegistry, uint256 initPausedStatus) internal {
                          require(
                              address(pauserRegistry) == address(0) && address(_pauserRegistry) != address(0),
                              "Pausable._initializePauser: _initializePauser() can only be called once"
                          );
                          _paused = initPausedStatus;
                          emit Paused(msg.sender, initPausedStatus);
                          _setPauserRegistry(_pauserRegistry);
                      }
                      /**
                       * @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 onlyPauser {
                          // verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain)
                          require((_paused & newPausedStatus) == _paused, "Pausable.pause: invalid attempt to unpause functionality");
                          _paused = newPausedStatus;
                          emit Paused(msg.sender, newPausedStatus);
                      }
                      /**
                       * @notice Alias for `pause(type(uint256).max)`.
                       */
                      function pauseAll() external onlyPauser {
                          _paused = type(uint256).max;
                          emit Paused(msg.sender, type(uint256).max);
                      }
                      /**
                       * @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 onlyUnpauser {
                          // verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain)
                          require(((~_paused) & (~newPausedStatus)) == (~_paused), "Pausable.unpause: invalid attempt to pause functionality");
                          _paused = newPausedStatus;
                          emit Unpaused(msg.sender, newPausedStatus);
                      }
                      /// @notice Returns the current paused status as a uint256.
                      function paused() public view virtual returns (uint256) {
                          return _paused;
                      }
                      /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
                      function paused(uint8 index) public view virtual returns (bool) {
                          uint256 mask = 1 << index;
                          return ((_paused & mask) == mask);
                      }
                      /// @notice Allows the unpauser to set a new pauser registry
                      function setPauserRegistry(IPauserRegistry newPauserRegistry) external onlyUnpauser {
                          _setPauserRegistry(newPauserRegistry);
                      }
                      /// internal function for setting pauser registry
                      function _setPauserRegistry(IPauserRegistry newPauserRegistry) internal {
                          require(address(newPauserRegistry) != address(0), "Pausable._setPauserRegistry: newPauserRegistry cannot be the zero address");
                          emit PauserRegistrySet(pauserRegistry, newPauserRegistry);
                          pauserRegistry = newPauserRegistry;
                      }
                      /**
                       * @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[48] private __gap;
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @title Constants shared between 'EigenPod' and 'EigenPodManager' contracts.
                   * @author Layr Labs, Inc.
                   * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                   */
                  abstract contract EigenPodPausingConstants {
                      /// @notice Index for flag that pauses creation of new EigenPods when set. See EigenPodManager code for details.
                      uint8 internal constant PAUSED_NEW_EIGENPODS = 0;
                      /// @notice Index for flag that pauses the `withdrawRestakedBeaconChainETH` function *of the EigenPodManager* when set. See EigenPodManager code for details.
                      uint8 internal constant PAUSED_WITHDRAW_RESTAKED_ETH = 1;
                      /// @notice Index for flag that pauses the `verifyCorrectWithdrawalCredentials` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_CREDENTIALS = 2;
                      /// @notice Index for flag that pauses the `verifyOvercommittedStake` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_OVERCOMMITTED = 3;
                      /// @notice Index for flag that pauses the `verifyBeaconChainFullWithdrawal` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_WITHDRAWAL = 4;
                  }// 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 (last updated v4.7.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 functionCall(target, data, "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");
                          require(isContract(target), "Address: call to non-contract");
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: static call to non-contract");
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason 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 {
                              // 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @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 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 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 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 getMiddlewareTimesIndexBlock(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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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
                  pragma solidity =0.8.12;
                  import "../interfaces/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 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.8.12;
                  import "./Merkle.sol";
                  import "../libraries/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;
                      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;
                      //refer to the eigenlayer-cli proof library.  Despite being the same dimensions as the validator tree, the balance tree is merkleized differently
                      uint256 internal constant BALANCE_TREE_HEIGHT = 38;
                      // MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
                      uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
                      //in beacon block body
                      uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
                      // in beacon block header
                      uint256 internal constant STATE_ROOT_INDEX = 3;
                      uint256 internal constant PROPOSER_INDEX_INDEX = 1;
                      uint256 internal constant SLOT_INDEX = 0;
                      uint256 internal constant BODY_ROOT_INDEX = 4;
                      // in beacon state
                      uint256 internal constant STATE_ROOTS_INDEX = 6;
                      uint256 internal constant BLOCK_ROOTS_INDEX = 5;
                      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 BALANCE_INDEX = 12;
                      uint256 internal constant EXECUTION_PAYLOAD_HEADER_INDEX = 24;
                      uint256 internal constant HISTORICAL_BATCH_STATE_ROOT_INDEX = 1;
                      // in validator
                      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 BLOCK_NUMBER_INDEX = 6;
                      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
                      uint256 internal constant SLOTS_PER_EPOCH = 32;
                      bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
                      struct WithdrawalProofs {
                          bytes blockHeaderProof;
                          bytes withdrawalProof;
                          bytes slotProof;
                          bytes executionPayloadProof;
                          bytes blockNumberProof;
                          uint64 blockHeaderRootIndex;
                          uint64 withdrawalIndex;
                          bytes32 blockHeaderRoot;
                          bytes32 blockBodyRoot;
                          bytes32 slotRoot;
                          bytes32 blockNumberRoot;
                          bytes32 executionPayloadRoot;
                      }
                      struct ValidatorFieldsAndBalanceProofs {
                          bytes validatorFieldsProof;
                          bytes validatorBalanceProof;
                          bytes32 balanceRoot;
                      }
                      struct ValidatorFieldsProof {
                          bytes validatorProof;
                          uint40 validatorIndex;
                      }
                      /**
                       * 
                       * @notice This function is parses the balanceRoot to get the uint64 balance of a validator.  During merkleization of the
                       * beacon state balance tree, four uint64 values (making 32 bytes) are grouped together and treated as a single leaf in the merkle tree. Thus the
                       * validatorIndex mod 4 is used to determine which of the four uint64 values to extract from the balanceRoot.
                       * @param validatorIndex is the index of the validator being proven for.
                       * @param balanceRoot is the combination of 4 validator balances being proven for.
                       * @return The validator's balance, in Gwei
                       */
                     function getBalanceFromBalanceRoot(uint40 validatorIndex, bytes32 balanceRoot) internal pure returns (uint64) {
                          uint256 bitShiftAmount = (validatorIndex % 4) * 64;
                          bytes32 validatorBalanceLittleEndian = bytes32((uint256(balanceRoot) << bitShiftAmount));
                          uint64 validatorBalance = Endian.fromLittleEndianUint64(validatorBalanceLittleEndian);
                          return validatorBalance;
                      }
                      /**
                       * @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 proof is the data used in proving the validator's fields
                       * @param validatorFields the claimed fields of the validator
                       */
                      function verifyValidatorFields(
                          uint40 validatorIndex,
                          bytes32 beaconStateRoot,
                          bytes calldata proof, 
                          bytes32[] calldata validatorFields
                      ) 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(proof.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 = Merkle.merkleizeSha256(validatorFields);
                          // verify the proof of the validatorRoot against the beaconStateRoot
                          require(Merkle.verifyInclusionSha256(proof, beaconStateRoot, validatorRoot, index), "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof");
                      }
                      /**
                       * @notice This function verifies merkle proofs of the balance 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 proof is the proof of the balance against the beacon chain state root
                       * @param balanceRoot is the serialized balance used to prove the balance of the validator (refer to `getBalanceFromBalanceRoot` above for detailed explanation)
                       */
                      function verifyValidatorBalance(
                          uint40 validatorIndex,
                          bytes32 beaconStateRoot,
                          bytes calldata proof,
                          bytes32 balanceRoot
                      ) internal view {
                          require(proof.length == 32 * ((BALANCE_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT), "BeaconChainProofs.verifyValidatorBalance: Proof has incorrect length");
                          /**
                          * the beacon state's balance list is a list of uint64 values, and these are grouped together in 4s when merkleized.  
                          * Therefore, the index of the balance of a validator is validatorIndex/4
                          */
                          uint256 balanceIndex = uint256(validatorIndex/4);
                          balanceIndex = (BALANCE_INDEX << (BALANCE_TREE_HEIGHT + 1)) | balanceIndex;
                          require(Merkle.verifyInclusionSha256(proof, beaconStateRoot, balanceRoot, balanceIndex), "BeaconChainProofs.verifyValidatorBalance: Invalid merkle proof");
                      }
                      /**
                       * @notice This function verifies the slot and the withdrawal fields for a given withdrawal
                       * @param beaconStateRoot is the beacon chain state root to be proven against.
                       * @param proofs is the provided set of merkle proofs
                       * @param withdrawalFields is the serialized withdrawal container to be proven
                       */
                      function verifyWithdrawalProofs(
                          bytes32 beaconStateRoot,
                          WithdrawalProofs calldata proofs,
                          bytes32[] calldata withdrawalFields
                      ) internal view {
                          require(withdrawalFields.length == 2**WITHDRAWAL_FIELD_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: withdrawalFields has incorrect length");
                          require(proofs.blockHeaderRootIndex < 2**BLOCK_ROOTS_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: blockRootIndex is too large");
                          require(proofs.withdrawalIndex < 2**WITHDRAWALS_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: withdrawalIndex is too large");
                         
                          // verify the block header proof length
                          require(proofs.blockHeaderProof.length == 32 * (BEACON_STATE_FIELD_TREE_HEIGHT + BLOCK_ROOTS_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: blockHeaderProof has incorrect length");
                          require(proofs.withdrawalProof.length == 32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT + WITHDRAWALS_TREE_HEIGHT + 1),
                              "BeaconChainProofs.verifyWithdrawalProofs: withdrawalProof has incorrect length");
                          require(proofs.executionPayloadProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: executionPayloadProof has incorrect length");
                          require(proofs.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: slotProof has incorrect length");
                          require(proofs.blockNumberProof.length == 32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: blockNumberProof has incorrect length");
                          /**
                           * Computes the block_header_index relative to the beaconStateRoot.  It concatenates the indexes of all the
                           * intermediate root indexes from the bottom of the sub trees (the block header container) to the top of the tree
                           */
                          uint256 blockHeaderIndex = BLOCK_ROOTS_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)  | uint256(proofs.blockHeaderRootIndex);
                          // Verify the blockHeaderRoot against the beaconStateRoot
                          require(Merkle.verifyInclusionSha256(proofs.blockHeaderProof, beaconStateRoot, proofs.blockHeaderRoot, blockHeaderIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid block header merkle proof");
                          //Next we verify the slot against the blockHeaderRoot
                          require(Merkle.verifyInclusionSha256(proofs.slotProof, proofs.blockHeaderRoot, proofs.slotRoot, SLOT_INDEX), "BeaconChainProofs.verifyWithdrawalProofs: Invalid slot merkle proof");
                          // Next we verify the executionPayloadRoot against the blockHeaderRoot
                          uint256 executionPayloadIndex = BODY_ROOT_INDEX << (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)| EXECUTION_PAYLOAD_INDEX ;
                          require(Merkle.verifyInclusionSha256(proofs.executionPayloadProof, proofs.blockHeaderRoot, proofs.executionPayloadRoot, executionPayloadIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid executionPayload merkle proof");
                          // Next we verify the blockNumberRoot against the executionPayload root
                          require(Merkle.verifyInclusionSha256(proofs.blockNumberProof, proofs.executionPayloadRoot, proofs.blockNumberRoot, BLOCK_NUMBER_INDEX),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid blockNumber merkle proof");
                          /**
                           * Next we verify the withdrawal fields against the blockHeaderRoot:
                           * First we compute the withdrawal_index relative to the blockHeaderRoot by concatenating the indexes of all the 
                           * intermediate root indexes from the bottom of the sub trees (the withdrawal container) to the top, the blockHeaderRoot.
                           * Then we calculate merkleize the withdrawalFields container to calculate the the withdrawalRoot.
                           * Finally we verify the withdrawalRoot against the executionPayloadRoot.
                           */
                          uint256 withdrawalIndex = WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1) | uint256(proofs.withdrawalIndex);
                          bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
                          require(Merkle.verifyInclusionSha256(proofs.withdrawalProof, proofs.executionPayloadRoot, withdrawalRoot, withdrawalIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid withdrawal merkle proof");
                      }
                  }// 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.7.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 functionCall(target, data, "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");
                          require(isContract(target), "Address: call to non-contract");
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: static call to non-contract");
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: delegate call to non-contract");
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason 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 {
                              // 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
                          }
                      }
                  }
                  // 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @title Interface for the `PauserRegistry` contract.
                   * @author Layr Labs, Inc.
                   * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                   */
                  interface IPauserRegistry {
                      /// @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: BUSL-1.1
                  // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
                  pragma solidity =0.8.12;
                  /**
                   * @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 Merkle {
                      /**
                       * @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 (uint 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 (uint 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.12;
                  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 9 of 10: UpgradeableBeacon
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)
                  pragma solidity ^0.8.0;
                  import "./IBeacon.sol";
                  import "../../access/Ownable.sol";
                  import "../../utils/Address.sol";
                  /**
                   * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
                   * implementation contract, which is where they will delegate all function calls.
                   *
                   * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
                   */
                  contract UpgradeableBeacon is IBeacon, Ownable {
                      address private _implementation;
                      /**
                       * @dev Emitted when the implementation returned by the beacon is changed.
                       */
                      event Upgraded(address indexed implementation);
                      /**
                       * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                       * beacon.
                       */
                      constructor(address implementation_) {
                          _setImplementation(implementation_);
                      }
                      /**
                       * @dev Returns the current implementation address.
                       */
                      function implementation() public view virtual override returns (address) {
                          return _implementation;
                      }
                      /**
                       * @dev Upgrades the beacon to a new implementation.
                       *
                       * Emits an {Upgraded} event.
                       *
                       * Requirements:
                       *
                       * - msg.sender must be the owner of the contract.
                       * - `newImplementation` must be a contract.
                       */
                      function upgradeTo(address newImplementation) public virtual onlyOwner {
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                      /**
                       * @dev Sets the implementation contract address for this beacon
                       *
                       * Requirements:
                       *
                       * - `newImplementation` must be a contract.
                       */
                      function _setImplementation(address newImplementation) private {
                          require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                          _implementation = newImplementation;
                      }
                  }
                  // 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.7.0) (access/Ownable.sol)
                  pragma solidity ^0.8.0;
                  import "../utils/Context.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 Ownable is Context {
                      address private _owner;
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      constructor() {
                          _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);
                      }
                  }
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.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 functionCall(target, data, "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");
                          require(isContract(target), "Address: call to non-contract");
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: static call to non-contract");
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: delegate call to non-contract");
                          (bool success, bytes memory returndata) = target.delegatecall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason 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 {
                              // 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;
                  /**
                   * @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 Context {
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  }
                  

                  File 10 of 10: EigenPod
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
                  import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
                  import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol";
                  import "@openzeppelin-upgrades/contracts/utils/AddressUpgradeable.sol";
                  import "../libraries/BeaconChainProofs.sol";
                  import "../libraries/BytesLib.sol";
                  import "../libraries/Endian.sol";
                  import "../interfaces/IETHPOSDeposit.sol";
                  import "../interfaces/IEigenPodManager.sol";
                  import "../interfaces/IEigenPod.sol";
                  import "../interfaces/IDelayedWithdrawalRouter.sol";
                  import "../interfaces/IPausable.sol";
                  import "./EigenPodPausingConstants.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
                   */
                  contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, EigenPodPausingConstants {
                      using BytesLib for bytes;
                      // CONSTANTS + IMMUTABLES
                      uint256 internal constant GWEI_TO_WEI = 1e9;
                      /// @notice Maximum "staleness" of a Beacon Chain state root against which `verifyOvercommittedStake` may be proven. 7 days in blocks.
                      uint256 internal constant VERIFY_OVERCOMMITTED_WINDOW_BLOCKS = 50400;
                      /// @notice This is the beacon chain deposit contract
                      IETHPOSDeposit public immutable ethPOS;
                      /// @notice Contract used for withdrawal routing, to provide an extra "safety net" mechanism
                      IDelayedWithdrawalRouter public immutable delayedWithdrawalRouter;
                      /// @notice The single EigenPodManager for EigenLayer
                      IEigenPodManager public immutable eigenPodManager;
                      /// @notice The amount of eth, in gwei, that is restaked per validator
                      uint64 public immutable REQUIRED_BALANCE_GWEI;
                      /// @notice The amount of eth, in wei, that is restaked per ETH validator into EigenLayer
                      uint256 public immutable REQUIRED_BALANCE_WEI;
                      /// @notice The owner of this EigenPod
                      address public podOwner;
                      /**
                       * @notice The latest block number at which the pod owner withdrew the balance of the pod.
                       * @dev This variable is only updated when the `withdraw` 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 blocks after the stored `mostRecentWithdrawalBlockNumber`.
                       */
                      uint64 public mostRecentWithdrawalBlockNumber;
                      // STORAGE VARIABLES
                      /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from the Beacon Chain but not from EigenLayer), 
                      uint64 public restakedExecutionLayerGwei;
                      /// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
                      bool public hasRestaked;
                      /// @notice this is a mapping of validator indices to a Validator struct containing pertinent info about the validator
                      mapping(uint40 => VALIDATOR_STATUS) public validatorStatus;
                      /// @notice This is a mapping of validatorIndex to withdrawalIndex to whether or not they have proven a withdrawal for that index
                      mapping(uint40 => mapping(uint64 => bool)) public provenPartialWithdrawal;
                      /// @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 is proven to have a balance less than `REQUIRED_BALANCE_GWEI` in the beacon chain
                      event ValidatorOvercommitted(uint40 validatorIndex);
                      
                      /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
                      event FullWithdrawalRedeemed(uint40 validatorIndex, address indexed recipient, uint64 withdrawalAmountGwei);
                      /// @notice Emitted when a partial withdrawal claim is successfully redeemed
                      event PartialWithdrawalRedeemed(uint40 validatorIndex, address indexed recipient, uint64 partialWithdrawalAmountGwei);
                      /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
                      event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
                      modifier onlyEigenPodManager {
                          require(msg.sender == address(eigenPodManager), "EigenPod.onlyEigenPodManager: not eigenPodManager");
                          _;
                      }
                      modifier onlyEigenPodOwner {
                          require(msg.sender == podOwner, "EigenPod.onlyEigenPodOwner: not podOwner");
                          _;
                      }
                      modifier onlyNotFrozen {
                          require(!eigenPodManager.slasher().isFrozen(podOwner), "EigenPod.onlyNotFrozen: pod owner is frozen");
                          _;
                      }
                      modifier hasNeverRestaked {
                          require(!hasRestaked, "EigenPod.hasNeverRestaked: restaking is enabled");
                          _;
                      }
                      /// @notice Checks that `blockNumber` is strictly greater than the value stored in `mostRecentWithdrawalBlockNumber`
                      modifier proofIsForValidBlockNumber(uint64 blockNumber) {
                          require(blockNumber > mostRecentWithdrawalBlockNumber,
                              "EigenPod.proofIsForValidBlockNumber: beacon chain proof must be for block number after mostRecentWithdrawalBlockNumber");
                          _;
                      }
                      /**
                       * @notice Based on 'Pausable' code, but uses the storage of the EigenPodManager instead of this contract. This construction
                       * is necessary for enabling pausing all EigenPods at the same time (due to EigenPods being Beacon Proxies).
                       * Modifier throws if the `indexed`th bit of `_paused` in the EigenPodManager is 1, i.e. if the `index`th pause switch is flipped.
                       */
                      modifier onlyWhenNotPaused(uint8 index) {
                          require(!IPausable(address(eigenPodManager)).paused(index), "EigenPod.onlyWhenNotPaused: index is paused in EigenPodManager");
                          _;
                      }
                      constructor(
                          IETHPOSDeposit _ethPOS,
                          IDelayedWithdrawalRouter _delayedWithdrawalRouter,
                          IEigenPodManager _eigenPodManager,
                          uint256 _REQUIRED_BALANCE_WEI
                      ) {
                          ethPOS = _ethPOS;
                          delayedWithdrawalRouter = _delayedWithdrawalRouter;
                          eigenPodManager = _eigenPodManager;
                          REQUIRED_BALANCE_WEI = _REQUIRED_BALANCE_WEI;
                          REQUIRED_BALANCE_GWEI = uint64(_REQUIRED_BALANCE_WEI / GWEI_TO_WEI);
                          require(_REQUIRED_BALANCE_WEI % GWEI_TO_WEI == 0, "EigenPod.contructor: _REQUIRED_BALANCE_WEI is not a whole number of gwei");
                          _disableInitializers();
                      }
                      /// @notice Used to initialize the pointers to addresses crucial to the pod's functionality. Called on construction by the EigenPodManager.
                      function initialize(address _podOwner) external initializer {
                          require(_podOwner != address(0), "EigenPod.initialize: podOwner cannot be zero address");
                          podOwner = _podOwner;
                      }
                      /// @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 onlyEigenPodManager {
                          // stake on ethpos
                          require(msg.value == 32 ether, "EigenPod.stake: must initially stake for any validator with 32 ether");
                          ethPOS.deposit{value : 32 ether}(pubkey, _podWithdrawalCredentials(), signature, depositDataRoot);
                          emit EigenPodStaked(pubkey);
                      }
                      /**
                       * @notice  This function verifies that the withdrawal credentials of the podOwner are pointed to
                       * this contract. It also verifies the current (not 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 oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the bytes that prove the 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 verifyWithdrawalCredentialsAndBalance(
                          uint64 oracleBlockNumber,
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs calldata proofs,
                          bytes32[] calldata validatorFields
                      )
                          external
                          onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_CREDENTIALS)
                          // check that the provided `oracleBlockNumber` is after the `mostRecentWithdrawalBlockNumber`
                          proofIsForValidBlockNumber(oracleBlockNumber)
                      {
                          require(validatorStatus[validatorIndex] == VALIDATOR_STATUS.INACTIVE,
                              "EigenPod.verifyCorrectWithdrawalCredentials: Validator must be inactive to prove withdrawal credentials");
                          require(validatorFields[BeaconChainProofs.VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX] == bytes32(_podWithdrawalCredentials()),
                              "EigenPod.verifyCorrectWithdrawalCredentials: Proof is not for this EigenPod");
                          // deserialize the balance field from the balanceRoot
                          uint64 validatorCurrentBalanceGwei = BeaconChainProofs.getBalanceFromBalanceRoot(validatorIndex, proofs.balanceRoot);
                          
                          // make sure the balance is greater than the amount restaked per validator
                          require(validatorCurrentBalanceGwei >= REQUIRED_BALANCE_GWEI,
                              "EigenPod.verifyCorrectWithdrawalCredentials: ETH validator's balance must be greater than or equal to the restaked balance per validator");
                          // verify ETH validator proof
                          bytes32 beaconStateRoot = eigenPodManager.getBeaconChainStateRoot(oracleBlockNumber);
                          BeaconChainProofs.verifyValidatorFields(
                              validatorIndex,
                              beaconStateRoot,
                              proofs.validatorFieldsProof,
                              validatorFields
                          );
                          // verify ETH validator's current balance, which is stored in the `balances` container of the beacon state
                          BeaconChainProofs.verifyValidatorBalance(
                              validatorIndex,
                              beaconStateRoot,
                              proofs.validatorBalanceProof,
                              proofs.balanceRoot
                          );
                          // set the status to active
                          validatorStatus[validatorIndex] = VALIDATOR_STATUS.ACTIVE;
                          // Sets "hasRestaked" to true if it hasn't been set yet. 
                          if (!hasRestaked) {
                              hasRestaked = true;
                          }
                          emit ValidatorRestaked(validatorIndex);
                          // virtually deposit REQUIRED_BALANCE_WEI for new ETH validator
                          eigenPodManager.restakeBeaconChainETH(podOwner, REQUIRED_BALANCE_WEI);
                      }
                      /**
                       * @notice This function records an overcommitment of stake to EigenLayer on behalf of a certain ETH validator.
                       *         If successful, the overcommitted balance is penalized (available for withdrawal whenever the pod's balance allows).
                       *         The ETH validator's shares in the enshrined beaconChainETH strategy are also removed from the StrategyManager and undelegated.
                       * @param oracleBlockNumber The oracleBlockNumber whose state root the `proof` will be proven against.
                       *        Must be within `VERIFY_OVERCOMMITTED_WINDOW_BLOCKS` of the current block.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the proof of the validator's balance and validatorFields in the balance tree and the balanceRoot to prove for
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwner's strategies
                       * @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 verifyOvercommittedStake(
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs calldata proofs,
                          bytes32[] calldata validatorFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      ) external onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_OVERCOMMITTED) {
                         // ensure that the blockNumber being proven against is not "too stale", i.e. that the validator was *recently* overcommitted.
                          require(oracleBlockNumber + VERIFY_OVERCOMMITTED_WINDOW_BLOCKS >= block.number,
                              "EigenPod.verifyOvercommittedStake: specified blockNumber is too far in past");
                          require(validatorStatus[validatorIndex] == VALIDATOR_STATUS.ACTIVE, "EigenPod.verifyOvercommittedStake: Validator not active");
                          // deserialize the balance field from the balanceRoot
                          uint64 validatorCurrentBalanceGwei = BeaconChainProofs.getBalanceFromBalanceRoot(validatorIndex, proofs.balanceRoot);        
                          require(validatorCurrentBalanceGwei < REQUIRED_BALANCE_GWEI,
                              "EigenPod.verifyOvercommittedStake: validator's balance must be less than the restaked balance per validator");
                          
                          // verify ETH validator proof
                          bytes32 beaconStateRoot = eigenPodManager.getBeaconChainStateRoot(oracleBlockNumber);
                   
                          /**
                           * If validator's balance is zero, then either they have fully withdrawn or they have been slashed down zero.
                           * If the validator *has* been slashed, then this function can proceed. If they have *not* been slashed, then
                           * the `verifyAndProcessWithdrawal` function should be called instead.
                           */
                          if (validatorCurrentBalanceGwei == 0) {
                              uint64 slashedStatus = Endian.fromLittleEndianUint64(validatorFields[BeaconChainProofs.VALIDATOR_SLASHED_INDEX]);
                              require(slashedStatus == 1, "EigenPod.verifyOvercommittedStake: Validator must be slashed to be overcommitted");
                              //Verify the validator fields, which contain the validator's slashed status
                              BeaconChainProofs.verifyValidatorFields(
                                  validatorIndex,
                                  beaconStateRoot,
                                  proofs.validatorFieldsProof,
                                  validatorFields
                              );
                          }
                          // verify ETH validator's current balance, which is stored in the `balances` container of the beacon state
                         BeaconChainProofs.verifyValidatorBalance(
                              validatorIndex,
                              beaconStateRoot,
                              proofs.validatorBalanceProof,
                              proofs.balanceRoot
                          );
                          // mark the ETH validator as overcommitted
                          validatorStatus[validatorIndex] = VALIDATOR_STATUS.OVERCOMMITTED;
                          emit ValidatorOvercommitted(validatorIndex);
                          // remove and undelegate shares in EigenLayer
                          eigenPodManager.recordOvercommittedBeaconChainETH(podOwner, beaconChainETHStrategyIndex, REQUIRED_BALANCE_WEI);
                      }
                      /**
                       * @notice This function records a full withdrawal on behalf of one of the Ethereum validators for this EigenPod
                       * @param withdrawalProofs is the information needed to check the veracity of the block number and withdrawal being proven
                       * @param validatorFieldsProof is the information needed to check the veracity of the validator fields being proven
                       * @param withdrawalFields are the fields of the withdrawal being proven
                       * @param validatorFields are the fields of the validator being proven
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *        the EigenPodManager to the StrategyManager in case it must be removed from the podOwner's list of strategies
                       * @param oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against.
                       */
                      function verifyAndProcessWithdrawal(
                          BeaconChainProofs.WithdrawalProofs calldata withdrawalProofs, 
                          bytes calldata validatorFieldsProof,
                          bytes32[] calldata validatorFields,
                          bytes32[] calldata withdrawalFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      )
                          external
                          onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_WITHDRAWAL)
                          onlyNotFrozen
                          /** 
                           * Check that the provided block number being proven against is after the `mostRecentWithdrawalBlockNumber`.
                           * Without this check, there is an edge case where a user proves a past withdrawal for a validator whose funds they already withdrew,
                           * as a way to "withdraw the same funds twice" without providing adequate proof.
                           * Note that this check is not made using the oracleBlockNumber as in the `verifyWithdrawalCredentials` proof; instead this proof
                           * proof is made for the block number of the withdrawal, which may be within 8192 slots of the oracleBlockNumber. 
                           * This difference in modifier usage is OK, since it is still not possible to `verifyAndProcessWithdrawal` against a slot that occurred
                           * *prior* to the proof provided in the `verifyWithdrawalCredentials` function.
                           */
                          proofIsForValidBlockNumber(Endian.fromLittleEndianUint64(withdrawalProofs.blockNumberRoot))
                      {
                          /**
                           * If the validator status is inactive, then withdrawal credentials were never verified for the validator,
                           * and thus we cannot know that the validator is related to this EigenPod at all!
                           */
                          uint40 validatorIndex = uint40(Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_INDEX_INDEX]));
                          
                          require(validatorStatus[validatorIndex] != VALIDATOR_STATUS.INACTIVE,
                              "EigenPod.verifyOvercommittedStake: Validator never proven to have withdrawal credentials pointed to this contract");
                          // fetch the beacon state root for the specified block
                          bytes32 beaconStateRoot = eigenPodManager.getBeaconChainStateRoot(oracleBlockNumber);
                          // Verifying the withdrawal as well as the slot
                          BeaconChainProofs.verifyWithdrawalProofs(beaconStateRoot, withdrawalProofs, withdrawalFields);
                          // Verifying the validator fields, specifically the withdrawable epoch
                          BeaconChainProofs.verifyValidatorFields(validatorIndex, beaconStateRoot, validatorFieldsProof, validatorFields);
                          uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
                          //check if the withdrawal occured after mostRecentWithdrawalBlockNumber
                          uint64 slot = Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot);
                          /**
                           * if the validator's withdrawable epoch is less than or equal to the slot's epoch, then the validator has fully withdrawn because
                           * a full withdrawal is only processable after the withdrawable epoch has passed.
                           */
                          // reference: uint64 withdrawableEpoch = Endian.fromLittleEndianUint64(validatorFields[BeaconChainProofs.VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
                          if (Endian.fromLittleEndianUint64(validatorFields[BeaconChainProofs.VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]) <= slot/BeaconChainProofs.SLOTS_PER_EPOCH) {
                              _processFullWithdrawal(withdrawalAmountGwei, validatorIndex, beaconChainETHStrategyIndex, podOwner, validatorStatus[validatorIndex]);
                          } else {
                              _processPartialWithdrawal(slot, withdrawalAmountGwei, validatorIndex, podOwner);
                          }
                      }
                      function _processFullWithdrawal(
                          uint64 withdrawalAmountGwei,
                          uint40 validatorIndex,
                          uint256 beaconChainETHStrategyIndex,
                          address recipient,
                          VALIDATOR_STATUS status
                      ) internal {
                          uint256 amountToSend;
                          // if the validator has not previously been proven to be "overcommitted"
                          if (status == VALIDATOR_STATUS.ACTIVE) {
                              // if the withdrawal amount is greater than the REQUIRED_BALANCE_GWEI (i.e. the amount restaked on EigenLayer, per ETH validator)
                              if (withdrawalAmountGwei >= REQUIRED_BALANCE_GWEI) {
                                  // then the excess is immediately withdrawable
                                  amountToSend = uint256(withdrawalAmountGwei - REQUIRED_BALANCE_GWEI) * uint256(GWEI_TO_WEI);
                                  // and the extra execution layer ETH in the contract is REQUIRED_BALANCE_GWEI, which must be withdrawn through EigenLayer's normal withdrawal process
                                  restakedExecutionLayerGwei += REQUIRED_BALANCE_GWEI;
                              } else {
                                  // otherwise, just use the full withdrawal amount to continue to "back" the podOwner's remaining shares in EigenLayer (i.e. none is instantly withdrawable)
                                  restakedExecutionLayerGwei += withdrawalAmountGwei;
                                  // remove and undelegate 'extra' (i.e. "overcommitted") shares in EigenLayer
                                  eigenPodManager.recordOvercommittedBeaconChainETH(podOwner, beaconChainETHStrategyIndex, uint256(REQUIRED_BALANCE_GWEI - withdrawalAmountGwei) * GWEI_TO_WEI);
                              }
                          // if the validator *has* previously been proven to be "overcommitted"
                          } else if (status == VALIDATOR_STATUS.OVERCOMMITTED) {
                              // if the withdrawal amount is greater than the REQUIRED_BALANCE_GWEI (i.e. the amount restaked on EigenLayer, per ETH validator)
                              if (withdrawalAmountGwei >= REQUIRED_BALANCE_GWEI) {
                                  // then the excess is immediately withdrawable
                                  amountToSend = uint256(withdrawalAmountGwei - REQUIRED_BALANCE_GWEI) * uint256(GWEI_TO_WEI);
                                  // and the extra execution layer ETH in the contract is REQUIRED_BALANCE_GWEI, which must be withdrawn through EigenLayer's normal withdrawal process
                                  restakedExecutionLayerGwei += REQUIRED_BALANCE_GWEI;
                                  /**
                                   * since in `verifyOvercommittedStake` the podOwner's beaconChainETH shares are decremented by `REQUIRED_BALANCE_WEI`, we must reverse the process here,
                                   * in order to allow the podOwner to complete their withdrawal through EigenLayer's normal withdrawal process
                                   */
                                  eigenPodManager.restakeBeaconChainETH(podOwner, REQUIRED_BALANCE_WEI);
                              } else {
                                  // otherwise, just use the full withdrawal amount to continue to "back" the podOwner's remaining shares in EigenLayer (i.e. none is instantly withdrawable)
                                  restakedExecutionLayerGwei += withdrawalAmountGwei;
                                  /**
                                   * since in `verifyOvercommittedStake` the podOwner's beaconChainETH shares are decremented by `REQUIRED_BALANCE_WEI`, we must reverse the process here,
                                   * in order to allow the podOwner to complete their withdrawal through EigenLayer's normal withdrawal process
                                   */
                                  eigenPodManager.restakeBeaconChainETH(podOwner, uint256(withdrawalAmountGwei) * GWEI_TO_WEI);
                              }
                          // If the validator status is withdrawn, they have already processed their ETH withdrawal
                          }  else {
                              revert("EigenPod.verifyBeaconChainFullWithdrawal: VALIDATOR_STATUS is WITHDRAWN or invalid VALIDATOR_STATUS");
                          }
                          // set the ETH validator status to withdrawn
                          validatorStatus[validatorIndex] = VALIDATOR_STATUS.WITHDRAWN;
                          emit FullWithdrawalRedeemed(validatorIndex, recipient, withdrawalAmountGwei);
                          // send ETH to the `recipient`, if applicable
                          if (amountToSend != 0) {
                              _sendETH(recipient, amountToSend);
                          }
                      }
                      function _processPartialWithdrawal(uint64 withdrawalHappenedSlot, uint64 partialWithdrawalAmountGwei, uint40 validatorIndex, address recipient) internal {
                          require(!provenPartialWithdrawal[validatorIndex][withdrawalHappenedSlot], "EigenPod._processPartialWithdrawal: partial withdrawal has already been proven for this slot");
                          provenPartialWithdrawal[validatorIndex][withdrawalHappenedSlot] = true;
                          emit PartialWithdrawalRedeemed(validatorIndex, recipient, partialWithdrawalAmountGwei);
                          // send the ETH to the `recipient`
                          _sendETH(recipient, uint256(partialWithdrawalAmountGwei) * uint256(GWEI_TO_WEI));
                      }
                      /**
                       * @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 Called during withdrawal or slashing.
                       */
                      function withdrawRestakedBeaconChainETH(
                          address recipient,
                          uint256 amountWei
                      )
                          external
                          onlyEigenPodManager
                      {
                          // reduce the restakedExecutionLayerGwei
                          restakedExecutionLayerGwei -= uint64(amountWei / GWEI_TO_WEI);
                          emit RestakedBeaconChainETHWithdrawn(recipient, amountWei);
                          // transfer ETH from pod to `recipient`
                          _sendETH(recipient, amountWei);
                      }
                      /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                      function withdrawBeforeRestaking() external onlyEigenPodOwner hasNeverRestaked {
                          mostRecentWithdrawalBlockNumber = uint32(block.number);
                          _sendETH(podOwner, address(this).balance);
                      }
                      // INTERNAL FUNCTIONS
                      function _podWithdrawalCredentials() internal view returns(bytes memory) {
                          return abi.encodePacked(bytes1(uint8(1)), bytes11(0), address(this));
                      }
                      function _sendETH(address recipient, uint256 amountWei) internal {
                          delayedWithdrawalRouter.createDelayedWithdrawal{value: amountWei}(podOwner, recipient);
                      }
                      /**
                       * @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[46] private __gap;
                  }// SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.7.0) (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. Equivalent to `reinitializer(1)`.
                       */
                      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.
                       *
                       * `initializer` is equivalent to `reinitializer(1)`, so 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.
                       *
                       * 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.
                       */
                      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.
                       */
                      function _disableInitializers() internal virtual {
                          require(!_initializing, "Initializable: contract is initializing");
                          if (_initialized < type(uint8).max) {
                              _initialized = type(uint8).max;
                              emit Initialized(type(uint8).max);
                          }
                      }
                  }
                  // 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 v4.4.1 (security/ReentrancyGuard.sol)
                  pragma solidity ^0.8.0;
                  import "../proxy/utils/Initializable.sol";
                  /**
                   * @dev Contract module that helps prevent reentrant calls to a function.
                   *
                   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                   * available, which can be applied to functions to make sure there are no nested
                   * (reentrant) calls to them.
                   *
                   * Note that because there is a single `nonReentrant` guard, functions marked as
                   * `nonReentrant` may not call one another. This can be worked around by making
                   * those functions `private`, and then adding `external` `nonReentrant` entry
                   * points to them.
                   *
                   * TIP: If you would like to learn more about reentrancy and alternative ways
                   * to protect against it, check out our blog post
                   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                   */
                  abstract contract ReentrancyGuardUpgradeable is Initializable {
                      // Booleans are more expensive than uint256 or any type that takes up a full
                      // word because each write operation emits an extra SLOAD to first read the
                      // slot's contents, replace the bits taken up by the boolean, and then write
                      // back. This is the compiler's defense against contract upgrades and
                      // pointer aliasing, and it cannot be disabled.
                      // The values being non-zero value makes deployment a bit more expensive,
                      // but in exchange the refund on every call to nonReentrant will be lower in
                      // amount. Since refunds are capped to a percentage of the total
                      // transaction's gas, it is best to keep them low in cases like this one, to
                      // increase the likelihood of the full refund coming into effect.
                      uint256 private constant _NOT_ENTERED = 1;
                      uint256 private constant _ENTERED = 2;
                      uint256 private _status;
                      function __ReentrancyGuard_init() internal onlyInitializing {
                          __ReentrancyGuard_init_unchained();
                      }
                      function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                          _status = _NOT_ENTERED;
                      }
                      /**
                       * @dev Prevents a contract from calling itself, directly or indirectly.
                       * Calling a `nonReentrant` function from another `nonReentrant`
                       * function is not supported. It is possible to prevent this from happening
                       * by making the `nonReentrant` function external, and making it call a
                       * `private` function that does the actual work.
                       */
                      modifier nonReentrant() {
                          // On the first call to nonReentrant, _notEntered will be true
                          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                          // Any calls to nonReentrant after this point will fail
                          _status = _ENTERED;
                          _;
                          // By storing the original value once again, a refund is triggered (see
                          // https://eips.ethereum.org/EIPS/eip-2200)
                          _status = _NOT_ENTERED;
                      }
                      /**
                       * @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.7.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 functionCall(target, data, "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");
                          require(isContract(target), "Address: call to non-contract");
                          (bool success, bytes memory returndata) = target.call{value: value}(data);
                          return verifyCallResult(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) {
                          require(isContract(target), "Address: static call to non-contract");
                          (bool success, bytes memory returndata) = target.staticcall(data);
                          return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason 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 {
                              // 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "./Merkle.sol";
                  import "../libraries/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;
                      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;
                      //refer to the eigenlayer-cli proof library.  Despite being the same dimensions as the validator tree, the balance tree is merkleized differently
                      uint256 internal constant BALANCE_TREE_HEIGHT = 38;
                      // MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
                      uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
                      //in beacon block body
                      uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
                      // in beacon block header
                      uint256 internal constant STATE_ROOT_INDEX = 3;
                      uint256 internal constant PROPOSER_INDEX_INDEX = 1;
                      uint256 internal constant SLOT_INDEX = 0;
                      uint256 internal constant BODY_ROOT_INDEX = 4;
                      // in beacon state
                      uint256 internal constant STATE_ROOTS_INDEX = 6;
                      uint256 internal constant BLOCK_ROOTS_INDEX = 5;
                      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 BALANCE_INDEX = 12;
                      uint256 internal constant EXECUTION_PAYLOAD_HEADER_INDEX = 24;
                      uint256 internal constant HISTORICAL_BATCH_STATE_ROOT_INDEX = 1;
                      // in validator
                      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 BLOCK_NUMBER_INDEX = 6;
                      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
                      uint256 internal constant SLOTS_PER_EPOCH = 32;
                      bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
                      struct WithdrawalProofs {
                          bytes blockHeaderProof;
                          bytes withdrawalProof;
                          bytes slotProof;
                          bytes executionPayloadProof;
                          bytes blockNumberProof;
                          uint64 blockHeaderRootIndex;
                          uint64 withdrawalIndex;
                          bytes32 blockHeaderRoot;
                          bytes32 blockBodyRoot;
                          bytes32 slotRoot;
                          bytes32 blockNumberRoot;
                          bytes32 executionPayloadRoot;
                      }
                      struct ValidatorFieldsAndBalanceProofs {
                          bytes validatorFieldsProof;
                          bytes validatorBalanceProof;
                          bytes32 balanceRoot;
                      }
                      struct ValidatorFieldsProof {
                          bytes validatorProof;
                          uint40 validatorIndex;
                      }
                      /**
                       * 
                       * @notice This function is parses the balanceRoot to get the uint64 balance of a validator.  During merkleization of the
                       * beacon state balance tree, four uint64 values (making 32 bytes) are grouped together and treated as a single leaf in the merkle tree. Thus the
                       * validatorIndex mod 4 is used to determine which of the four uint64 values to extract from the balanceRoot.
                       * @param validatorIndex is the index of the validator being proven for.
                       * @param balanceRoot is the combination of 4 validator balances being proven for.
                       * @return The validator's balance, in Gwei
                       */
                     function getBalanceFromBalanceRoot(uint40 validatorIndex, bytes32 balanceRoot) internal pure returns (uint64) {
                          uint256 bitShiftAmount = (validatorIndex % 4) * 64;
                          bytes32 validatorBalanceLittleEndian = bytes32((uint256(balanceRoot) << bitShiftAmount));
                          uint64 validatorBalance = Endian.fromLittleEndianUint64(validatorBalanceLittleEndian);
                          return validatorBalance;
                      }
                      /**
                       * @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 proof is the data used in proving the validator's fields
                       * @param validatorFields the claimed fields of the validator
                       */
                      function verifyValidatorFields(
                          uint40 validatorIndex,
                          bytes32 beaconStateRoot,
                          bytes calldata proof, 
                          bytes32[] calldata validatorFields
                      ) 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(proof.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 = Merkle.merkleizeSha256(validatorFields);
                          // verify the proof of the validatorRoot against the beaconStateRoot
                          require(Merkle.verifyInclusionSha256(proof, beaconStateRoot, validatorRoot, index), "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof");
                      }
                      /**
                       * @notice This function verifies merkle proofs of the balance 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 proof is the proof of the balance against the beacon chain state root
                       * @param balanceRoot is the serialized balance used to prove the balance of the validator (refer to `getBalanceFromBalanceRoot` above for detailed explanation)
                       */
                      function verifyValidatorBalance(
                          uint40 validatorIndex,
                          bytes32 beaconStateRoot,
                          bytes calldata proof,
                          bytes32 balanceRoot
                      ) internal view {
                          require(proof.length == 32 * ((BALANCE_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT), "BeaconChainProofs.verifyValidatorBalance: Proof has incorrect length");
                          /**
                          * the beacon state's balance list is a list of uint64 values, and these are grouped together in 4s when merkleized.  
                          * Therefore, the index of the balance of a validator is validatorIndex/4
                          */
                          uint256 balanceIndex = uint256(validatorIndex/4);
                          balanceIndex = (BALANCE_INDEX << (BALANCE_TREE_HEIGHT + 1)) | balanceIndex;
                          require(Merkle.verifyInclusionSha256(proof, beaconStateRoot, balanceRoot, balanceIndex), "BeaconChainProofs.verifyValidatorBalance: Invalid merkle proof");
                      }
                      /**
                       * @notice This function verifies the slot and the withdrawal fields for a given withdrawal
                       * @param beaconStateRoot is the beacon chain state root to be proven against.
                       * @param proofs is the provided set of merkle proofs
                       * @param withdrawalFields is the serialized withdrawal container to be proven
                       */
                      function verifyWithdrawalProofs(
                          bytes32 beaconStateRoot,
                          WithdrawalProofs calldata proofs,
                          bytes32[] calldata withdrawalFields
                      ) internal view {
                          require(withdrawalFields.length == 2**WITHDRAWAL_FIELD_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: withdrawalFields has incorrect length");
                          require(proofs.blockHeaderRootIndex < 2**BLOCK_ROOTS_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: blockRootIndex is too large");
                          require(proofs.withdrawalIndex < 2**WITHDRAWALS_TREE_HEIGHT, "BeaconChainProofs.verifyWithdrawalProofs: withdrawalIndex is too large");
                         
                          // verify the block header proof length
                          require(proofs.blockHeaderProof.length == 32 * (BEACON_STATE_FIELD_TREE_HEIGHT + BLOCK_ROOTS_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: blockHeaderProof has incorrect length");
                          require(proofs.withdrawalProof.length == 32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT + WITHDRAWALS_TREE_HEIGHT + 1),
                              "BeaconChainProofs.verifyWithdrawalProofs: withdrawalProof has incorrect length");
                          require(proofs.executionPayloadProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: executionPayloadProof has incorrect length");
                          require(proofs.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: slotProof has incorrect length");
                          require(proofs.blockNumberProof.length == 32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT),
                              "BeaconChainProofs.verifyWithdrawalProofs: blockNumberProof has incorrect length");
                          /**
                           * Computes the block_header_index relative to the beaconStateRoot.  It concatenates the indexes of all the
                           * intermediate root indexes from the bottom of the sub trees (the block header container) to the top of the tree
                           */
                          uint256 blockHeaderIndex = BLOCK_ROOTS_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)  | uint256(proofs.blockHeaderRootIndex);
                          // Verify the blockHeaderRoot against the beaconStateRoot
                          require(Merkle.verifyInclusionSha256(proofs.blockHeaderProof, beaconStateRoot, proofs.blockHeaderRoot, blockHeaderIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid block header merkle proof");
                          //Next we verify the slot against the blockHeaderRoot
                          require(Merkle.verifyInclusionSha256(proofs.slotProof, proofs.blockHeaderRoot, proofs.slotRoot, SLOT_INDEX), "BeaconChainProofs.verifyWithdrawalProofs: Invalid slot merkle proof");
                          // Next we verify the executionPayloadRoot against the blockHeaderRoot
                          uint256 executionPayloadIndex = BODY_ROOT_INDEX << (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)| EXECUTION_PAYLOAD_INDEX ;
                          require(Merkle.verifyInclusionSha256(proofs.executionPayloadProof, proofs.blockHeaderRoot, proofs.executionPayloadRoot, executionPayloadIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid executionPayload merkle proof");
                          // Next we verify the blockNumberRoot against the executionPayload root
                          require(Merkle.verifyInclusionSha256(proofs.blockNumberProof, proofs.executionPayloadRoot, proofs.blockNumberRoot, BLOCK_NUMBER_INDEX),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid blockNumber merkle proof");
                          /**
                           * Next we verify the withdrawal fields against the blockHeaderRoot:
                           * First we compute the withdrawal_index relative to the blockHeaderRoot by concatenating the indexes of all the 
                           * intermediate root indexes from the bottom of the sub trees (the withdrawal container) to the top, the blockHeaderRoot.
                           * Then we calculate merkleize the withdrawalFields container to calculate the the withdrawalRoot.
                           * Finally we verify the withdrawalRoot against the executionPayloadRoot.
                           */
                          uint256 withdrawalIndex = WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1) | uint256(proofs.withdrawalIndex);
                          bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
                          require(Merkle.verifyInclusionSha256(proofs.withdrawalProof, proofs.executionPayloadRoot, withdrawalRoot, withdrawalIndex),
                              "BeaconChainProofs.verifyWithdrawalProofs: Invalid withdrawal merkle proof");
                      }
                  }// SPDX-License-Identifier: Unlicense
                  /*
                   * @title Solidity Bytes Arrays Utils
                   * @author Gonçalo Sá <goncalo.sa@consensys.net>
                   *
                   * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
                   *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
                   */
                  pragma solidity >=0.8.0 <0.9.0;
                  library BytesLib {
                      function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
                          bytes memory tempBytes;
                          assembly {
                              // Get a location of some free memory and store it in tempBytes as
                              // Solidity does for memory variables.
                              tempBytes := mload(0x40)
                              // Store the length of the first bytes array at the beginning of
                              // the memory for tempBytes.
                              let length := mload(_preBytes)
                              mstore(tempBytes, length)
                              // Maintain a memory counter for the current write location in the
                              // temp bytes array by adding the 32 bytes for the array length to
                              // the starting location.
                              let mc := add(tempBytes, 0x20)
                              // Stop copying when the memory counter reaches the length of the
                              // first bytes array.
                              let end := add(mc, length)
                              for {
                                  // Initialize a copy counter to the start of the _preBytes data,
                                  // 32 bytes into its memory.
                                  let cc := add(_preBytes, 0x20)
                              } lt(mc, end) {
                                  // Increase both counters by 32 bytes each iteration.
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } {
                                  // Write the _preBytes data into the tempBytes memory 32 bytes
                                  // at a time.
                                  mstore(mc, mload(cc))
                              }
                              // Add the length of _postBytes to the current length of tempBytes
                              // and store it as the new length in the first 32 bytes of the
                              // tempBytes memory.
                              length := mload(_postBytes)
                              mstore(tempBytes, add(length, mload(tempBytes)))
                              // Move the memory counter back from a multiple of 0x20 to the
                              // actual end of the _preBytes data.
                              mc := end
                              // Stop copying when the memory counter reaches the new combined
                              // length of the arrays.
                              end := add(mc, length)
                              for { let cc := add(_postBytes, 0x20) } lt(mc, end) {
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } { mstore(mc, mload(cc)) }
                              // Update the free-memory pointer by padding our last write location
                              // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                              // next 32 byte block, then round down to the nearest multiple of
                              // 32. If the sum of the length of the two arrays is zero then add
                              // one before rounding down to leave a blank 32 bytes (the length block with 0).
                              mstore(
                                  0x40,
                                  and(
                                      add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                                      not(31) // Round down to the nearest 32 bytes.
                                  )
                              )
                          }
                          return tempBytes;
                      }
                      function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
                          assembly {
                              // Read the first 32 bytes of _preBytes storage, which is the length
                              // of the array. (We don't need to use the offset into the slot
                              // because arrays use the entire slot.)
                              let fslot := sload(_preBytes.slot)
                              // Arrays of 31 bytes or less have an even value in their slot,
                              // while longer arrays have an odd value. The actual length is
                              // the slot divided by two for odd values, and the lowest order
                              // byte divided by two for even values.
                              // If the slot is even, bitwise and the slot with 255 and divide by
                              // two to get the length. If the slot is odd, bitwise and the slot
                              // with -1 and divide by two.
                              let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                              let mlength := mload(_postBytes)
                              let newlength := add(slength, mlength)
                              // slength can contain both the length and contents of the array
                              // if length < 32 bytes so let's prepare for that
                              // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                              switch add(lt(slength, 32), lt(newlength, 32))
                              case 2 {
                                  // Since the new array still fits in the slot, we just need to
                                  // update the contents of the slot.
                                  // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                                  sstore(
                                      _preBytes.slot,
                                      // all the modifications to the slot are inside this
                                      // next block
                                      add(
                                          // we can just add to the slot contents because the
                                          // bytes we want to change are the LSBs
                                          fslot,
                                          add(
                                              mul(
                                                  div(
                                                      // load the bytes from memory
                                                      mload(add(_postBytes, 0x20)),
                                                      // zero all bytes to the right
                                                      exp(0x100, sub(32, mlength))
                                                  ),
                                                  // and now shift left the number of bytes to
                                                  // leave space for the length in the slot
                                                  exp(0x100, sub(32, newlength))
                                              ),
                                              // increase length by the double of the memory
                                              // bytes length
                                              mul(mlength, 2)
                                          )
                                      )
                                  )
                              }
                              case 1 {
                                  // The stored value fits in the slot, but the combined value
                                  // will exceed it.
                                  // get the keccak hash to get the contents of the array
                                  mstore(0x0, _preBytes.slot)
                                  let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                                  // save new length
                                  sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                                  // The contents of the _postBytes array start 32 bytes into
                                  // the structure. Our first read should obtain the `submod`
                                  // bytes that can fit into the unused space in the last word
                                  // of the stored array. To get this, we read 32 bytes starting
                                  // from `submod`, so the data we read overlaps with the array
                                  // contents by `submod` bytes. Masking the lowest-order
                                  // `submod` bytes allows us to add that value directly to the
                                  // stored value.
                                  let submod := sub(32, slength)
                                  let mc := add(_postBytes, submod)
                                  let end := add(_postBytes, mlength)
                                  let mask := sub(exp(0x100, submod), 1)
                                  sstore(
                                      sc,
                                      add(
                                          and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
                                          and(mload(mc), mask)
                                      )
                                  )
                                  for {
                                      mc := add(mc, 0x20)
                                      sc := add(sc, 1)
                                  } lt(mc, end) {
                                      sc := add(sc, 1)
                                      mc := add(mc, 0x20)
                                  } { sstore(sc, mload(mc)) }
                                  mask := exp(0x100, sub(mc, end))
                                  sstore(sc, mul(div(mload(mc), mask), mask))
                              }
                              default {
                                  // get the keccak hash to get the contents of the array
                                  mstore(0x0, _preBytes.slot)
                                  // Start copying to the last used word of the stored array.
                                  let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                                  // save new length
                                  sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                                  // Copy over the first `submod` bytes of the new data as in
                                  // case 1 above.
                                  let slengthmod := mod(slength, 32)
                                  // solhint-disable-next-line no-unused-vars
                                  let mlengthmod := mod(mlength, 32)
                                  let submod := sub(32, slengthmod)
                                  let mc := add(_postBytes, submod)
                                  let end := add(_postBytes, mlength)
                                  let mask := sub(exp(0x100, submod), 1)
                                  sstore(sc, add(sload(sc), and(mload(mc), mask)))
                                  for {
                                      sc := add(sc, 1)
                                      mc := add(mc, 0x20)
                                  } lt(mc, end) {
                                      sc := add(sc, 1)
                                      mc := add(mc, 0x20)
                                  } { sstore(sc, mload(mc)) }
                                  mask := exp(0x100, sub(mc, end))
                                  sstore(sc, mul(div(mload(mc), mask), mask))
                              }
                          }
                      }
                      function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
                          require(_length + 31 >= _length, "slice_overflow");
                          require(_bytes.length >= _start + _length, "slice_outOfBounds");
                          bytes memory tempBytes;
                          assembly {
                              switch iszero(_length)
                              case 0 {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
                                  // The first word of the slice result is potentially a partial
                                  // word read from the original array. To read it, we calculate
                                  // the length of that partial word and start copying that many
                                  // bytes into the array. The first word we copy will start with
                                  // data we don't care about, but the last `lengthmod` bytes will
                                  // land at the beginning of the contents of the new array. When
                                  // we're done copying, we overwrite the full first word with
                                  // the actual length of the slice.
                                  let lengthmod := and(_length, 31)
                                  // The multiplication in the next line is necessary
                                  // because when slicing multiples of 32 bytes (lengthmod == 0)
                                  // the following copy loop was copying the origin's length
                                  // and then ending prematurely not copying everything it should.
                                  let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                                  let end := add(mc, _length)
                                  for {
                                      // The multiplication in the next line has the same exact purpose
                                      // as the one above.
                                      let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } { mstore(mc, mload(cc)) }
                                  mstore(tempBytes, _length)
                                  //update free-memory pointer
                                  //allocating the array padded to 32 bytes like the compiler does now
                                  mstore(0x40, and(add(mc, 31), not(31)))
                              }
                              //if we want a zero-length slice let's just return a zero-length array
                              default {
                                  tempBytes := mload(0x40)
                                  //zero out the 32 bytes slice we are about to return
                                  //we need to do it because Solidity does not garbage collect
                                  mstore(tempBytes, 0)
                                  mstore(0x40, add(tempBytes, 0x20))
                              }
                          }
                          return tempBytes;
                      }
                      function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                          require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                          address tempAddress;
                          assembly {
                              tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                          }
                          return tempAddress;
                      }
                      function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
                          require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
                          uint8 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x1), _start))
                          }
                          return tempUint;
                      }
                      function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                          require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                          uint16 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x2), _start))
                          }
                          return tempUint;
                      }
                      function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                          require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                          uint32 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x4), _start))
                          }
                          return tempUint;
                      }
                      function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                          require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                          uint64 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x8), _start))
                          }
                          return tempUint;
                      }
                      function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                          require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                          uint96 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0xc), _start))
                          }
                          return tempUint;
                      }
                      function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                          require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                          uint128 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x10), _start))
                          }
                          return tempUint;
                      }
                      function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                          require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                          uint256 tempUint;
                          assembly {
                              tempUint := mload(add(add(_bytes, 0x20), _start))
                          }
                          return tempUint;
                      }
                      function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                          require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                          bytes32 tempBytes32;
                          assembly {
                              tempBytes32 := mload(add(add(_bytes, 0x20), _start))
                          }
                          return tempBytes32;
                      }
                      function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
                          bool success = true;
                          assembly {
                              let length := mload(_preBytes)
                              // if lengths don't match the arrays are not equal
                              switch eq(length, mload(_postBytes))
                              case 1 {
                                  // cb is a circuit breaker in the for loop since there's
                                  //  no said feature for inline assembly loops
                                  // cb = 1 - don't breaker
                                  // cb = 0 - break
                                  let cb := 1
                                  let mc := add(_preBytes, 0x20)
                                  let end := add(mc, length)
                                  for { let cc := add(_postBytes, 0x20) }
                                  // the next line is the loop condition:
                                  // while(uint256(mc < end) + cb == 2)
                                  eq(add(lt(mc, end), cb), 2) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      // if any of these checks fails then arrays are not equal
                                      if iszero(eq(mload(mc), mload(cc))) {
                                          // unsuccess:
                                          success := 0
                                          cb := 0
                                      }
                                  }
                              }
                              default {
                                  // unsuccess:
                                  success := 0
                              }
                          }
                          return success;
                      }
                      function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
                          bool success = true;
                          assembly {
                              // we know _preBytes_offset is 0
                              let fslot := sload(_preBytes.slot)
                              // Decode the length of the stored array like in concatStorage().
                              let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                              let mlength := mload(_postBytes)
                              // if lengths don't match the arrays are not equal
                              switch eq(slength, mlength)
                              case 1 {
                                  // slength can contain both the length and contents of the array
                                  // if length < 32 bytes so let's prepare for that
                                  // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                                  if iszero(iszero(slength)) {
                                      switch lt(slength, 32)
                                      case 1 {
                                          // blank the last byte which is the length
                                          fslot := mul(div(fslot, 0x100), 0x100)
                                          if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                              // unsuccess:
                                              success := 0
                                          }
                                      }
                                      default {
                                          // cb is a circuit breaker in the for loop since there's
                                          //  no said feature for inline assembly loops
                                          // cb = 1 - don't breaker
                                          // cb = 0 - break
                                          let cb := 1
                                          // get the keccak hash to get the contents of the array
                                          mstore(0x0, _preBytes.slot)
                                          let sc := keccak256(0x0, 0x20)
                                          let mc := add(_postBytes, 0x20)
                                          let end := add(mc, mlength)
                                          // the next line is the loop condition:
                                          // while(uint256(mc < end) + cb == 2)
                                          // solhint-disable-next-line no-empty-blocks
                                          for {} eq(add(lt(mc, end), cb), 2) {
                                              sc := add(sc, 1)
                                              mc := add(mc, 0x20)
                                          } {
                                              if iszero(eq(sload(sc), mload(mc))) {
                                                  // unsuccess:
                                                  success := 0
                                                  cb := 0
                                              }
                                          }
                                      }
                                  }
                              }
                              default {
                                  // unsuccess:
                                  success := 0
                              }
                          }
                          return success;
                      }
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  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);
                      }
                  }
                  // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                  // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                  // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                  // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                  // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                  // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                  // SPDX-License-Identifier: CC0-1.0
                  pragma solidity =0.8.12;
                  // 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.8.12;
                  import "./IStrategyManager.sol";
                  import "./IEigenPod.sol";
                  import "./IBeaconChainOracle.sol";
                  import "./IPausable.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 Creates an EigenPod for the sender.
                       * @dev Function will revert if the `msg.sender` already has an EigenPod.
                       */
                      function createPod() external;
                      /**
                       * @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 Deposits/Restakes beacon chain ETH in EigenLayer on behalf of the owner of an EigenPod.
                       * @param podOwner The owner of the pod whose balance must be deposited.
                       * @param amount The amount of ETH to 'deposit' (i.e. be credited to the podOwner).
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function restakeBeaconChainETH(address podOwner, uint256 amount) external;
                      /**
                       * @notice Removes beacon chain ETH from EigenLayer on behalf of the owner of an EigenPod, when the
                       *         balance of a validator is lower than how much stake they have committed to EigenLayer
                       * @param podOwner The owner of the pod whose balance must be removed.
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwner's strategies
                       * @param amount The amount of ETH to remove.
                       * @dev Callable only by the podOwner's EigenPod contract.
                       */
                      function recordOvercommittedBeaconChainETH(address podOwner, uint256 beaconChainETHStrategyIndex, uint256 amount) external;
                      
                      /**
                       * @notice Withdraws ETH from an EigenPod. The ETH must have first been withdrawn from the beacon chain.
                       * @param podOwner The owner of the pod whose balance must be withdrawn.
                       * @param recipient The recipient of the withdrawn ETH.
                       * @param amount The amount of ETH to withdraw.
                       * @dev Callable only by the StrategyManager contract.
                       */
                      function withdrawRestakedBeaconChainETH(address podOwner, address recipient, uint256 amount) 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 Oracle contract that provides updates to the beacon chain's state
                      function beaconChainOracle() external view returns(IBeaconChainOracle);    
                      /// @notice Returns the Beacon Chain state root at `blockNumber`. Reverts if the Beacon Chain state root at `blockNumber` has not yet been finalized.
                      function getBeaconChainStateRoot(uint64 blockNumber) 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);
                      function hasPod(address podOwner) external view returns (bool);
                  }// SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "../libraries/BeaconChainProofs.sol";
                  import "./IEigenPodManager.sol";
                  import "./IBeaconChainOracle.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
                          OVERCOMMITTED, // proven to be overcommitted to EigenLayer
                          WITHDRAWN // withdrawn from the Beacon Chain
                      }
                      // this struct keeps track of PartialWithdrawalClaims
                      struct PartialWithdrawalClaim {
                          PARTIAL_WITHDRAWAL_CLAIM_STATUS status;
                          // block at which the PartialWithdrawalClaim was created
                          uint32 creationBlockNumber;
                          // last block (inclusive) in which the PartialWithdrawalClaim can be fraudproofed
                          uint32 fraudproofPeriodEndBlockNumber;
                          // amount of ETH -- in Gwei -- to be withdrawn until completion of this claim
                          uint64 partialWithdrawalAmountGwei;
                      }
                      enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
                          REDEEMED,
                          PENDING,
                          FAILED
                      }
                      /// @notice The amount of eth, in gwei, that is restaked per validator
                      function REQUIRED_BALANCE_GWEI() external view returns(uint64);
                      /// @notice The amount of eth, in wei, that is restaked per validator
                      function REQUIRED_BALANCE_WEI() external view returns(uint256);
                      /// @notice this is a mapping of validator indices to a Validator struct containing pertinent info about the validator
                      function validatorStatus(uint40 validatorIndex) external view returns(VALIDATOR_STATUS);
                      /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer), 
                      function restakedExecutionLayerGwei() external view returns(uint64);
                      /// @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 Called during withdrawal or slashing.
                       * @dev Note that this function is marked as non-reentrant to prevent the recipient calling back into it
                       */
                      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 block number of the most recent withdrawal
                      function mostRecentWithdrawalBlockNumber() external view returns (uint64);
                      ///@notice mapping that tracks proven partial withdrawals
                      function provenPartialWithdrawal(uint40 validatorIndex, uint64 slot) external view returns (bool);
                      /**
                       * @notice This function verifies that the withdrawal credentials of the podOwner are pointed to
                       * this contract. It also verifies the current (not 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 oracleBlockNumber is the Beacon Chain blockNumber whose state root the `proof` will be proven against.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the bytes that prove the 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 verifyWithdrawalCredentialsAndBalance(
                          uint64 oracleBlockNumber,
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs memory proofs,
                          bytes32[] calldata validatorFields
                      ) external;
                      
                      /**
                       * @notice This function records an overcommitment of stake to EigenLayer on behalf of a certain ETH validator.
                       *         If successful, the overcommitted balance is penalized (available for withdrawal whenever the pod's balance allows).
                       *         The ETH validator's shares in the enshrined beaconChainETH strategy are also removed from the StrategyManager and undelegated.
                       * @param oracleBlockNumber The oracleBlockNumber whose state root the `proof` will be proven against.
                       *        Must be within `VERIFY_OVERCOMMITTED_WINDOW_BLOCKS` of the current block.
                       * @param validatorIndex is the index of the validator being proven, refer to consensus specs 
                       * @param proofs is the proof of the validator's balance and validatorFields in the balance tree and the balanceRoot to prove for
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *                                    the StrategyManager in case it must be removed from the list of the podOwners strategies
                       * @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 verifyOvercommittedStake(
                          uint40 validatorIndex,
                          BeaconChainProofs.ValidatorFieldsAndBalanceProofs calldata proofs,
                          bytes32[] calldata validatorFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      ) external;
                      /**
                       * @notice This function records a full withdrawal on behalf of one of the Ethereum validators for this EigenPod
                       * @param withdrawalProofs is the information needed to check the veracity of the block number and withdrawal being proven
                       * @param validatorFieldsProof is the proof of the validator's fields in the validator tree
                       * @param withdrawalFields are the fields of the withdrawal being proven
                       * @param validatorFields are the fields of the validator being proven
                       * @param beaconChainETHStrategyIndex is the index of the beaconChainETHStrategy for the pod owner for the callback to 
                       *        the EigenPodManager to the StrategyManager in case it must be removed from the podOwner's list of strategies
                       */
                      function verifyAndProcessWithdrawal(
                          BeaconChainProofs.WithdrawalProofs calldata withdrawalProofs, 
                          bytes calldata validatorFieldsProof,
                          bytes32[] calldata validatorFields,
                          bytes32[] calldata withdrawalFields,
                          uint256 beaconChainETHStrategyIndex,
                          uint64 oracleBlockNumber
                      ) external;
                      /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                      function withdrawBeforeRestaking() external;
                  }// SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  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 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  import "../interfaces/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 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.8.12;
                  /**
                   * @title Constants shared between 'EigenPod' and 'EigenPodManager' contracts.
                   * @author Layr Labs, Inc.
                   * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                   */
                  abstract contract EigenPodPausingConstants {
                      /// @notice Index for flag that pauses creation of new EigenPods when set. See EigenPodManager code for details.
                      uint8 internal constant PAUSED_NEW_EIGENPODS = 0;
                      /// @notice Index for flag that pauses the `withdrawRestakedBeaconChainETH` function *of the EigenPodManager* when set. See EigenPodManager code for details.
                      uint8 internal constant PAUSED_WITHDRAW_RESTAKED_ETH = 1;
                      /// @notice Index for flag that pauses the `verifyCorrectWithdrawalCredentials` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_CREDENTIALS = 2;
                      /// @notice Index for flag that pauses the `verifyOvercommittedStake` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_OVERCOMMITTED = 3;
                      /// @notice Index for flag that pauses the `verifyBeaconChainFullWithdrawal` function *of the EigenPods* when set. see EigenPod code for details.
                      uint8 internal constant PAUSED_EIGENPODS_VERIFY_WITHDRAWAL = 4;
                  }// 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: BUSL-1.1
                  // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
                  pragma solidity =0.8.12;
                  /**
                   * @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 Merkle {
                      /**
                       * @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 (uint 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 (uint 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.12;
                  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.8.12;
                  /**
                   * @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 Largest blockNumber that has been confirmed by the oracle.
                      function latestConfirmedOracleBlockNumber() external view returns(uint64);
                      /// @notice Mapping: Beacon Chain blockNumber => the Beacon Chain state root at the specified blockNumber.
                      /// @dev This will return `bytes32(0)` if the state root at the specified blockNumber is not yet confirmed.
                      function beaconStateRootAtBlockNumber(uint64 blockNumber) external view returns(bytes32);
                      /// @notice Mapping: address => whether or not the address is in the set of oracle signers.
                      function isOracleSigner(address _oracleSigner) external view returns(bool);
                      /// @notice Mapping: Beacon Chain blockNumber => oracle signer address => whether or not the oracle signer has voted on the state root at the blockNumber.
                      function hasVoted(uint64 blockNumber, address oracleSigner) external view returns(bool);
                      /// @notice Mapping: Beacon Chain blockNumber => state root => total number of oracle signer votes for the state root at the blockNumber. 
                      function stateRootVotes(uint64 blockNumber, bytes32 stateRoot) external view returns(uint256);
                      /// @notice Total number of members of the set of oracle signers.
                      function totalOracleSigners() external view returns(uint256);
                      /**
                       * @notice Number of oracle signers that must vote for a state root in order for the state root to be confirmed.
                       * Adjustable by this contract's owner through use of the `setThreshold` function.
                       * @dev We note that there is an edge case -- when the threshold is adjusted downward, if a state root already has enough votes to meet the *new* threshold,
                       * the state root must still receive one additional vote from an oracle signer to be confirmed. This behavior is intended, to minimize unexpected root confirmations.
                       */
                      function threshold() external view returns(uint256);
                      /**
                       * @notice Owner-only function used to modify the value of the `threshold` variable.
                       * @param _threshold Desired new value for the `threshold` variable. Function will revert if this is set to zero.
                       */
                      function setThreshold(uint256 _threshold) external;
                      /**
                       * @notice Owner-only function used to add a signer to the set of oracle signers.
                       * @param _oracleSigners Array of address to be added to the set.
                       * @dev Function will have no effect on the i-th input address if `_oracleSigners[i]`is already in the set of oracle signers.
                       */
                      function addOracleSigners(address[] memory _oracleSigners) external;
                      /**
                       * @notice Owner-only function used to remove a signer from the set of oracle signers.
                       * @param _oracleSigners Array of address to be removed from the set.
                       * @dev Function will have no effect on the i-th input address if `_oracleSigners[i]`is already not in the set of oracle signers.
                       */
                      function removeOracleSigners(address[] memory _oracleSigners) external;
                      /**
                       * @notice Called by a member of the set of oracle signers to assert that the Beacon Chain state root is `stateRoot` at `blockNumber`.
                       * @dev The state root will be finalized once the total number of votes *for this exact state root at this exact blockNumber* meets the `threshold` value.
                       * @param blockNumber The Beacon Chain blockNumber of interest.
                       * @param stateRoot The Beacon Chain state root that the caller asserts was the correct root, at the specified `blockNumber`.
                       */
                      function voteForBeaconChainStateRoot(uint64 blockNumber, bytes32 stateRoot) external;
                  }// SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @title Interface for the `PauserRegistry` contract.
                   * @author Layr Labs, Inc.
                   * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                   */
                  interface IPauserRegistry {
                      /// @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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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);
                  }
                  // SPDX-License-Identifier: BUSL-1.1
                  pragma solidity =0.8.12;
                  /**
                   * @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 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 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 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 getMiddlewareTimesIndexBlock(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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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: 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: BUSL-1.1
                  pragma solidity =0.8.12;
                  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);
                  }