ETH Price: $2,490.84 (+2.21%)

Transaction Decoder

Block:
20725122 at Sep-11-2024 04:56:11 AM +UTC
Transaction Fee:
0.000348805876850101 ETH $0.87
Gas Used:
174,779 Gas / 1.995696719 Gwei

Emitted Events:

352 GnosisSafeProxy.0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d( 0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d, 0x00000000000000000000000010586b9b7ff0f842913a57539f8167ce143c3011, 00000000000000000000000000000000000000000000000000084fed9270b9f3 )
353 TransparentUpgradeableProxy.0xdb884544f85d0a8333a7bb3e000e9652aafebb3ecfa5e3fbe8e60a72ad317cca( 0xdb884544f85d0a8333a7bb3e000e9652aafebb3ecfa5e3fbe8e60a72ad317cca, 000000000000000000000000d22fb2d2c09c108c44b622c37f6d2f4bc9f85668, 00000000000000000000000000000000000000000000000000084fed9270b9f3, 0000000000000000000000000000000000000000000000000000000000000001 )
354 TransparentUpgradeableProxy.0xd742cda664e1fb0906a48ed895215860dca3e7168cc6c91163dd3d6fdcaf9cea( 0xd742cda664e1fb0906a48ed895215860dca3e7168cc6c91163dd3d6fdcaf9cea, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000084fed9270b9f3, 00000000000000000000000010586b9b7ff0f842913a57539f8167ce143c3011 )
355 TransparentUpgradeableProxy.0xe58c4cce7527014b5d0a844d7d3066720af0dad3bb06c8410e27c4afc25ada7e( 0xe58c4cce7527014b5d0a844d7d3066720af0dad3bb06c8410e27c4afc25ada7e, 000000000000000000000000000000000000000000000000004acf5a25f68994, 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee )
356 TransparentUpgradeableProxy.0xdaaff7644a104b05650ed8d2c1c7b17d856e8f2fc89e4c159a1b0700d96d7b30( 0xdaaff7644a104b05650ed8d2c1c7b17d856e8f2fc89e4c159a1b0700d96d7b30, 0000000000000000000000000000000000000000000000000000000000000000 )
357 TransparentUpgradeableProxy.0x37b126de65aa5399e8d0bd72abf6da65483a54fd7cc7be425ee940c10479374f( 0x37b126de65aa5399e8d0bd72abf6da65483a54fd7cc7be425ee940c10479374f, 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, 000000000000000000000000000000000000000000000000004acf5a25f68994 )
358 TransparentUpgradeableProxy.0xb8b27d0db504fa5d914f1fd330347096e88d5ff94b6c612d32797e7c12a8f66f( 0xb8b27d0db504fa5d914f1fd330347096e88d5ff94b6c612d32797e7c12a8f66f, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000004acf5a25f68994 )

Account State Difference:

  Address   Before After State Difference Code
0x10586b9b...E143C3011
0x5efc9D10...Be42C8918 12,392.645540558159330405 Eth12,392.666597692526839289 Eth0.021057134367508884
0xD22FB2d2...Bc9f85668 891.365101279781743717 Eth891.367440961378133592 Eth0.002339681596389875
(Flashbots: Builder 2)
13.554465242202492751 Eth
Nonce: 17250
13.530719620361743891 Eth
Nonce: 17251
0.02374562184074886

Execution Trace

ETH 0.023396815963898759 TransparentUpgradeableProxy.CALL( )
  • ETH 0.023396815963898759 RewardHandler.DELEGATECALL( )
    • ETH 0.023396815963898759 TransparentUpgradeableProxy.CALL( )
      • ETH 0.023396815963898759 DepositQueue.DELEGATECALL( )
        • ETH 0.002339681596389875 TransparentUpgradeableProxy.CALL( )
          • ETH 0.002339681596389875 PaymentSplitter.DELEGATECALL( )
            • ETH 0.002339681596389875 GnosisSafeProxy.CALL( )
            • TransparentUpgradeableProxy.7be71216( )
              • WithdrawQueue.getWithdrawDeficit( _asset=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ) => ( 20550816820283175169455 )
              • ETH 0.021057134367508884 TransparentUpgradeableProxy.CALL( )
                • ETH 0.021057134367508884 WithdrawQueue.DELEGATECALL( )
                  • TransparentUpgradeableProxy.STATICCALL( )
                    File 1 of 10: TransparentUpgradeableProxy
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.5.0-rc.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 v4.4.1 (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 initializating the storage of the proxy like a Solidity constructor.
                         */
                        constructor(address _logic, bytes memory _data) payable {
                            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                            _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.5.0-rc.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 virtual 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.5.0-rc.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 overriden 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 internall 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 overriden should call `super._beforeFallback()`.
                         */
                        function _beforeFallback() internal virtual {}
                    }
                    // 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 v4.4.1 (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) {
                            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                            _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.5.0-rc.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
                                    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/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) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                         */
                        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                         */
                        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                         */
                        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                    }
                    

                    File 2 of 10: GnosisSafeProxy
                    // SPDX-License-Identifier: LGPL-3.0-only
                    pragma solidity >=0.7.0 <0.9.0;
                    
                    /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
                    /// @author Richard Meissner - <[email protected]>
                    interface IProxy {
                        function masterCopy() external view returns (address);
                    }
                    
                    /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
                    /// @author Stefan George - <[email protected]>
                    /// @author Richard Meissner - <[email protected]>
                    contract GnosisSafeProxy {
                        // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                        // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                        address internal singleton;
                    
                        /// @dev Constructor function sets address of singleton contract.
                        /// @param _singleton Singleton address.
                        constructor(address _singleton) {
                            require(_singleton != address(0), "Invalid singleton address provided");
                            singleton = _singleton;
                        }
                    
                        /// @dev Fallback function forwards all transactions and returns all received return data.
                        fallback() external payable {
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                                // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                                if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                                    mstore(0, _singleton)
                                    return(0, 0x20)
                                }
                                calldatacopy(0, 0, calldatasize())
                                let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                                returndatacopy(0, 0, returndatasize())
                                if eq(success, 0) {
                                    revert(0, returndatasize())
                                }
                                return(0, returndatasize())
                            }
                        }
                    }
                    
                    /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                    /// @author Stefan George - <[email protected]>
                    contract GnosisSafeProxyFactory {
                        event ProxyCreation(GnosisSafeProxy proxy, address singleton);
                    
                        /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                        /// @param singleton Address of singleton contract.
                        /// @param data Payload for message call sent to new proxy contract.
                        function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                            proxy = new GnosisSafeProxy(singleton);
                            if (data.length > 0)
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                        revert(0, 0)
                                    }
                                }
                            emit ProxyCreation(proxy, singleton);
                        }
                    
                        /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                        function proxyRuntimeCode() public pure returns (bytes memory) {
                            return type(GnosisSafeProxy).runtimeCode;
                        }
                    
                        /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                        function proxyCreationCode() public pure returns (bytes memory) {
                            return type(GnosisSafeProxy).creationCode;
                        }
                    
                        /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                        ///      This method is only meant as an utility to be called from other methods
                        /// @param _singleton Address of singleton contract.
                        /// @param initializer Payload for message call sent to new proxy contract.
                        /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                        function deployProxyWithNonce(
                            address _singleton,
                            bytes memory initializer,
                            uint256 saltNonce
                        ) internal returns (GnosisSafeProxy proxy) {
                            // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                            bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                            bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                            }
                            require(address(proxy) != address(0), "Create2 call failed");
                        }
                    
                        /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                        /// @param _singleton Address of singleton contract.
                        /// @param initializer Payload for message call sent to new proxy contract.
                        /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                        function createProxyWithNonce(
                            address _singleton,
                            bytes memory initializer,
                            uint256 saltNonce
                        ) public returns (GnosisSafeProxy proxy) {
                            proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                            if (initializer.length > 0)
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                        revert(0, 0)
                                    }
                                }
                            emit ProxyCreation(proxy, _singleton);
                        }
                    
                        /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                        /// @param _singleton Address of singleton contract.
                        /// @param initializer Payload for message call sent to new proxy contract.
                        /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                        /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                        function createProxyWithCallback(
                            address _singleton,
                            bytes memory initializer,
                            uint256 saltNonce,
                            IProxyCreationCallback callback
                        ) public returns (GnosisSafeProxy proxy) {
                            uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                            proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                            if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                        }
                    
                        /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                        ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                        ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                        /// @param _singleton Address of singleton contract.
                        /// @param initializer Payload for message call sent to new proxy contract.
                        /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                        function calculateCreateProxyWithNonceAddress(
                            address _singleton,
                            bytes calldata initializer,
                            uint256 saltNonce
                        ) external returns (GnosisSafeProxy proxy) {
                            proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                            revert(string(abi.encodePacked(proxy)));
                        }
                    }
                    
                    interface IProxyCreationCallback {
                        function proxyCreated(
                            GnosisSafeProxy proxy,
                            address _singleton,
                            bytes calldata initializer,
                            uint256 saltNonce
                        ) external;
                    }

                    File 3 of 10: TransparentUpgradeableProxy
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.5.0-rc.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 v4.4.1 (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 initializating the storage of the proxy like a Solidity constructor.
                         */
                        constructor(address _logic, bytes memory _data) payable {
                            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                            _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.5.0-rc.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 virtual 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.5.0-rc.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 overriden 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 internall 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 overriden should call `super._beforeFallback()`.
                         */
                        function _beforeFallback() internal virtual {}
                    }
                    // 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 v4.4.1 (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) {
                            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                            _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.5.0-rc.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
                                    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/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) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                         */
                        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                         */
                        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                         */
                        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                    }
                    

                    File 4 of 10: TransparentUpgradeableProxy
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.5.0-rc.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 v4.4.1 (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 initializating the storage of the proxy like a Solidity constructor.
                         */
                        constructor(address _logic, bytes memory _data) payable {
                            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                            _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.5.0-rc.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 virtual 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.5.0-rc.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 overriden 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 internall 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 overriden should call `super._beforeFallback()`.
                         */
                        function _beforeFallback() internal virtual {}
                    }
                    // 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 v4.4.1 (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) {
                            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                            _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.5.0-rc.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
                                    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/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) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                         */
                        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                         */
                        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                         */
                        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                    }
                    

                    File 5 of 10: TransparentUpgradeableProxy
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.5.0-rc.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 v4.4.1 (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 initializating the storage of the proxy like a Solidity constructor.
                         */
                        constructor(address _logic, bytes memory _data) payable {
                            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                            _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.5.0-rc.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 virtual 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.5.0-rc.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 overriden 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 internall 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 overriden should call `super._beforeFallback()`.
                         */
                        function _beforeFallback() internal virtual {}
                    }
                    // 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 v4.4.1 (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) {
                            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                            _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.5.0-rc.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
                                    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/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) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                         */
                        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                         */
                        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                         */
                        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                    }
                    

                    File 6 of 10: RewardHandler
                    // 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) (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: 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: BUSL-1.1
                    pragma solidity 0.8.19;
                    /// @dev Error for 0x0 address inputs
                    error InvalidZeroInput();    
                    /// @dev Error for already added items to a list
                    error AlreadyAdded();    
                    /// @dev Error for not found items in a list
                    error NotFound();    
                    /// @dev Error for hitting max TVL
                    error MaxTVLReached();
                    /// @dev Error for caller not having permissions
                    error NotRestakeManagerAdmin();
                    /// @dev Error for call not coming from deposit queue contract
                    error NotDepositQueue();
                    /// @dev Error for contract being paused
                    error ContractPaused(); 
                    /// @dev Error for exceeding max basis points (100%)
                    error OverMaxBasisPoints();
                    /// @dev Error for invalid token decimals for collateral tokens (must be 18)
                    error InvalidTokenDecimals(uint8 expected, uint8 actual);
                    /// @dev Error when withdraw is already completed
                    error WithdrawAlreadyCompleted();
                    /// @dev Error when a different address tries to complete withdraw
                    error NotOriginalWithdrawCaller(address expectedCaller);
                    /// @dev Error when caller does not have OD admin role
                    error NotOperatorDelegatorAdmin();
                    /// @dev Error when caller does not have Oracle Admin role
                    error NotOracleAdmin();
                    /// @dev Error when caller is not RestakeManager contract
                    error NotRestakeManager();
                    /// @dev Errror when caller does not have ETH Restake Admin role
                    error NotNativeEthRestakeAdmin();
                    /// @dev Error when delegation address was already set - cannot be set again
                    error DelegateAddressAlreadySet();
                    /// @dev Error when caller does not have ERC20 Rewards Admin role
                    error NotERC20RewardsAdmin();
                    /// @dev Error when ending ETH fails
                    error TransferFailed();
                    /// @dev Error when caller does not have ETH Minter Burner Admin role
                    error NotEzETHMinterBurner();
                    /// @dev Error when caller does not have Token Admin role
                    error NotTokenAdmin();
                    /// @dev Error when price oracle is not configured
                    error OracleNotFound();
                    /// @dev Error when price oracle data is stale
                    error OraclePriceExpired();
                    /// @dev Error when array lengths do not match
                    error MismatchedArrayLengths();
                    /// @dev Error when caller does not have Deposit Withdraw Pauser role
                    error NotDepositWithdrawPauser();
                    /// @dev Error when an individual token TVL is over the max
                    error MaxTokenTVLReached();
                    /// @dev Error when Oracle price is invalid
                    error InvalidOraclePrice();
                    /// @dev Error when calling an invalid function
                    error NotImplemented();
                    /// @dev Error when calculating token amounts is invalid
                    error InvalidTokenAmount();
                    //SPDX-License-Identifier: MIT
                    pragma solidity 0.8.19;
                    interface IRoleManager {
                        /// @dev Determines if the specified address has permissions to manage RoleManager
                        /// @param potentialAddress Address to check
                        function isRoleManagerAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to mint or burn ezETH tokens
                        /// @param potentialAddress Address to check
                        function isEzETHMinterBurner(address potentialAddress)
                            external
                            view
                            returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the OperatorDelgator Contracts
                        /// @param potentialAddress Address to check
                        function isOperatorDelegatorAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Oracle Contract config
                        /// @param potentialAddress Address to check
                        function isOracleAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Restake Manager
                        /// @param potentialAddress Address to check
                        function isRestakeManagerAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Token Contract
                        /// @param potentialAddress Address to check
                        function isTokenAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);
                        
                        /// @dev Determines if the specified address has permission to trigger restaking of native ETH
                        /// @param potentialAddress Address to check
                        function isNativeEthRestakeAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);        
                        /// @dev Determines if the specified address has permission to sweep and deposit ERC20 Rewards
                        /// @param potentialAddress Address to check
                        function isERC20RewardsAdmin(address potentialAddress)
                            external
                            view
                            returns (bool);        
                        
                        /// @dev Determines if the specified address has permission to pause deposits and withdraws
                        /// @param potentialAddress Address to check
                        function isDepositWithdrawPauser(address potentialAddress)
                            external
                            view
                            returns (bool);
                    }
                    //SPDX-License-Identifier: MIT
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                    import "./RewardHandlerStorage.sol";
                    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                    import "../Errors/Errors.sol";
                    /**
                     * @author  Renzo Protocol
                     * @title   RewardHandler
                     * @dev     Handles native ETH rewards deposited on the execution layer from validator nodes.  Forwards them
                     * to the DepositQueue contract for restaking.
                     * @notice  .
                     */
                    contract RewardHandler is Initializable, ReentrancyGuardUpgradeable, RewardHandlerStorageV1 {
                        /// @dev Allows only a whitelisted address to trigger native ETH staking
                        modifier onlyNativeEthRestakeAdmin() {
                            if(!roleManager.isNativeEthRestakeAdmin(msg.sender)) revert NotNativeEthRestakeAdmin();
                            _;
                        }
                        /// @dev Allows only a whitelisted address to configure the contract
                        modifier onlyRestakeManagerAdmin() {
                            if(!roleManager.isRestakeManagerAdmin(msg.sender)) revert NotRestakeManagerAdmin();
                            _;
                        }
                        event RewardDestinationUpdated(
                            address rewardDestination        
                        );
                        /// @dev Prevents implementation contract from being initialized.
                        /// @custom:oz-upgrades-unsafe-allow constructor
                        constructor() {
                            _disableInitializers();
                        }
                        /// @dev Initializes the contract with initial vars
                        function initialize(IRoleManager _roleManager, address _rewardDestination) public initializer {
                            __ReentrancyGuard_init();
                            if(address(_roleManager) == address(0x0)) revert InvalidZeroInput(); 
                            if(address(_rewardDestination) == address(0x0)) revert InvalidZeroInput(); 
                            
                            roleManager = _roleManager;   
                            rewardDestination = _rewardDestination;
                            emit RewardDestinationUpdated(_rewardDestination);
                        }
                        /// @dev Forwards all native ETH rewards to the DepositQueue contract
                        /// Handle ETH sent to this contract from outside the protocol that trigger contract execution - e.g. rewards
                        receive() external payable nonReentrant {
                            _forwardETH();
                        }
                        /// @dev Forwards all native ETH rewards to the DepositQueue contract
                        /// Handle ETH sent to this contract from validator nodes that do not trigger contract execution - e.g. rewards
                        function forwardRewards() external nonReentrant onlyNativeEthRestakeAdmin {
                            _forwardETH();
                        }
                        function _forwardETH() internal {
                            uint256 balance = address(this).balance;
                            if(balance == 0) {
                              return;
                            }
                            (bool success, ) = rewardDestination.call{value: balance}("");
                            if(!success) revert TransferFailed();
                        }
                        function setRewardDestination(address _rewardDestination) external nonReentrant onlyRestakeManagerAdmin {
                            if(address(_rewardDestination) == address(0x0)) revert InvalidZeroInput(); 
                            rewardDestination = _rewardDestination;
                            emit RewardDestinationUpdated(_rewardDestination);
                        }
                    }// SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "../Permissions/IRoleManager.sol";
                    abstract contract RewardHandlerStorageV1 {    
                        /// @dev reference to the RoleManager contract
                        IRoleManager public roleManager;
                        /// @dev the address of the depositQueue contract
                        address public rewardDestination;
                    }
                    

                    File 7 of 10: DepositQueue
                    // 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) (security/ReentrancyGuard.sol)
                    pragma solidity ^0.8.0;
                    import {Initializable} from "../proxy/utils/Initializable.sol";
                    /**
                     * @dev Contract module that helps prevent reentrant calls to a function.
                     *
                     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                     * available, which can be applied to functions to make sure there are no nested
                     * (reentrant) calls to them.
                     *
                     * Note that because there is a single `nonReentrant` guard, functions marked as
                     * `nonReentrant` may not call one another. This can be worked around by making
                     * those functions `private`, and then adding `external` `nonReentrant` entry
                     * points to them.
                     *
                     * TIP: If you would like to learn more about reentrancy and alternative ways
                     * to protect against it, check out our blog post
                     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                     */
                    abstract contract ReentrancyGuardUpgradeable is Initializable {
                        // Booleans are more expensive than uint256 or any type that takes up a full
                        // word because each write operation emits an extra SLOAD to first read the
                        // slot's contents, replace the bits taken up by the boolean, and then write
                        // back. This is the compiler's defense against contract upgrades and
                        // pointer aliasing, and it cannot be disabled.
                        // The values being non-zero value makes deployment a bit more expensive,
                        // but in exchange the refund on every call to nonReentrant will be lower in
                        // amount. Since refunds are capped to a percentage of the total
                        // transaction's gas, it is best to keep them low in cases like this one, to
                        // increase the likelihood of the full refund coming into effect.
                        uint256 private constant _NOT_ENTERED = 1;
                        uint256 private constant _ENTERED = 2;
                        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: 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 (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.4) (token/ERC20/extensions/IERC20Permit.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                     *
                     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                     * need to send a transaction, and thus is not required to hold Ether at all.
                     *
                     * ==== Security Considerations
                     *
                     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
                     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
                     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
                     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
                     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
                     * generally recommended is:
                     *
                     * ```solidity
                     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
                     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
                     *     doThing(..., value);
                     * }
                     *
                     * function doThing(..., uint256 value) public {
                     *     token.safeTransferFrom(msg.sender, address(this), value);
                     *     ...
                     * }
                     * ```
                     *
                     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
                     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
                     * {SafeERC20-safeTransferFrom}).
                     *
                     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
                     * contracts should have entry points that don't rely on permit.
                     */
                    interface IERC20Permit {
                        /**
                         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                         * given ``owner``'s signed approval.
                         *
                         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                         * ordering also apply here.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `deadline` must be a timestamp in the future.
                         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                         * over the EIP712-formatted function arguments.
                         * - the signature must use ``owner``'s current nonce (see {nonces}).
                         *
                         * For more information on the signature format, see the
                         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                         * section].
                         *
                         * CAUTION: See Security Considerations above.
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external;
                        /**
                         * @dev Returns the current nonce for `owner`. This value must be
                         * included whenever a signature is generated for {permit}.
                         *
                         * Every successful call to {permit} increases ``owner``'s nonce by one. This
                         * prevents a signature from being used multiple times.
                         */
                        function nonces(address owner) external view returns (uint256);
                        /**
                         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                         */
                        // solhint-disable-next-line func-name-mixedcase
                        function DOMAIN_SEPARATOR() external view returns (bytes32);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.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 (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                    pragma solidity ^0.8.0;
                    import "../IERC20.sol";
                    import "../extensions/IERC20Permit.sol";
                    import "../../../utils/Address.sol";
                    /**
                     * @title SafeERC20
                     * @dev Wrappers around ERC20 operations that throw on failure (when the token
                     * contract returns false). Tokens that return no value (and instead revert or
                     * throw on failure) are also supported, non-reverting calls are assumed to be
                     * successful.
                     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                     */
                    library SafeERC20 {
                        using Address for address;
                        /**
                         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeTransfer(IERC20 token, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                        }
                        /**
                         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                         */
                        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                        }
                        /**
                         * @dev Deprecated. This function has issues similar to the ones found in
                         * {IERC20-approve}, and its usage is discouraged.
                         *
                         * Whenever possible, use {safeIncreaseAllowance} and
                         * {safeDecreaseAllowance} instead.
                         */
                        function safeApprove(IERC20 token, address spender, uint256 value) internal {
                            // safeApprove should only be called when setting an initial allowance,
                            // or when resetting it to zero. To increase and decrease it, use
                            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                            require(
                                (value == 0) || (token.allowance(address(this), spender) == 0),
                                "SafeERC20: approve from non-zero to non-zero allowance"
                            );
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                        }
                        /**
                         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                        }
                        /**
                         * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            unchecked {
                                uint256 oldAllowance = token.allowance(address(this), spender);
                                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                            }
                        }
                        /**
                         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                         * to be set to zero before setting it to a non-zero value, such as USDT.
                         */
                        function forceApprove(IERC20 token, address spender, uint256 value) internal {
                            bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                            if (!_callOptionalReturnBool(token, approvalCall)) {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                                _callOptionalReturn(token, approvalCall);
                            }
                        }
                        /**
                         * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                         * Revert on invalid signature.
                         */
                        function safePermit(
                            IERC20Permit token,
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            uint256 nonceBefore = token.nonces(owner);
                            token.permit(owner, spender, value, deadline, v, r, s);
                            uint256 nonceAfter = token.nonces(owner);
                            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         */
                        function _callOptionalReturn(IERC20 token, bytes memory data) private {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                            // the target address contains contract code and also asserts for success in the low-level call.
                            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                            require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         *
                         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                         */
                        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                            // and not revert is the subcall reverts.
                            (bool success, bytes memory returndata) = address(token).call(data);
                            return
                                success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                        }
                    }
                    // 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: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "../EigenLayer/interfaces/IStrategyManager.sol";
                    import "../EigenLayer/interfaces/IDelegationManager.sol";
                    import "../EigenLayer/interfaces/IEigenPod.sol";
                    interface IOperatorDelegator {
                        function getTokenBalanceFromStrategy(IERC20 token) external view returns (uint256);
                        function deposit(IERC20 _token, uint256 _tokenAmount) external returns (uint256 shares);
                        // Note: Withdraws disabled for this release
                        // function startWithdrawal(IERC20 _token, uint256 _tokenAmount) external returns (bytes32);
                        // function completeWithdrawal(
                        //     IStrategyManager.DeprecatedStruct_QueuedWithdrawal calldata _withdrawal,
                        //     IERC20 _token,
                        //     uint256 _middlewareTimesIndex,
                        //     address _sendToAddress
                        // ) external;
                        function getStakedETHBalance() external view returns (uint256);
                        function stakeEth(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        function eigenPod() external view returns (IEigenPod);
                        function pendingUnstakedDelayedWithdrawalAmount() external view returns (uint256);
                        function delegateAddress() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                    import "./DepositQueueStorage.sol";
                    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                    import "../Errors/Errors.sol";
                    contract DepositQueue is Initializable, ReentrancyGuardUpgradeable, DepositQueueStorageV2 {
                        using SafeERC20 for IERC20;
                        address public constant IS_NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                        event RewardsDeposited(IERC20 token, uint256 amount);
                        event FeeConfigUpdated(address feeAddress, uint256 feeBasisPoints);
                        event RestakeManagerUpdated(IRestakeManager restakeManager);
                        event ETHDepositedFromProtocol(uint256 amount);
                        event ETHStakedFromQueue(
                            IOperatorDelegator operatorDelegator,
                            bytes pubkey,
                            uint256 amountStaked,
                            uint256 amountQueued
                        );
                        event ProtocolFeesPaid(IERC20 token, uint256 amount, address destination);
                        event GasRefunded(address admin, uint256 gasRefunded);
                        /// @dev Event emitted when withdrawQueue is updated
                        event WithdrawQueueUpdated(address oldWithdrawQueue, address newWithdrawQueue);
                        /// @dev Event emitted when withdrawQueue buffer is filled for specified token
                        event BufferFilled(address token, uint256 amount);
                        /// @dev Event emitted when Full Withdrawal ETH is Received from Operator Delegators
                        event FullWithdrawalETHReceived(uint256 amount);
                        /// @dev Allows only a whitelisted address to configure the contract
                        modifier onlyRestakeManagerAdmin() {
                            if (!roleManager.isRestakeManagerAdmin(msg.sender)) revert NotRestakeManagerAdmin();
                            _;
                        }
                        /// @dev Allows only the RestakeManager address to call functions
                        modifier onlyRestakeManager() {
                            if (msg.sender != address(restakeManager)) revert NotRestakeManager();
                            _;
                        }
                        /// @dev Allows only a whitelisted address to trigger native ETH staking
                        modifier onlyNativeEthRestakeAdmin() {
                            if (!roleManager.isNativeEthRestakeAdmin(msg.sender)) revert NotNativeEthRestakeAdmin();
                            _;
                        }
                        /// @dev Allows only a whitelisted address to trigger ERC20 rewards sweeping
                        modifier onlyERC20RewardsAdmin() {
                            if (!roleManager.isERC20RewardsAdmin(msg.sender)) revert NotERC20RewardsAdmin();
                            _;
                        }
                        /// @dev Prevents implementation contract from being initialized.
                        /// @custom:oz-upgrades-unsafe-allow constructor
                        constructor() {
                            _disableInitializers();
                        }
                        /// @dev Initializes the contract with initial vars
                        function initialize(IRoleManager _roleManager) public initializer {
                            __ReentrancyGuard_init();
                            if (address(_roleManager) == address(0x0)) revert InvalidZeroInput();
                            roleManager = _roleManager;
                        }
                        /**
                         * @notice  Sets the withdrawal queue contract
                         * @dev     permissioned call (onlyRestakeManagerAdmin)
                         * @param   _withdrawQueue  new withdraw Queue contract address
                         */
                        function setWithdrawQueue(IWithdrawQueue _withdrawQueue) external onlyRestakeManagerAdmin {
                            if (address(_withdrawQueue) == address(0)) revert InvalidZeroInput();
                            emit WithdrawQueueUpdated(address(withdrawQueue), address(_withdrawQueue));
                            withdrawQueue = _withdrawQueue;
                        }
                        /// @dev Sets the config for fees - if either value is set to 0 then fees are disabled
                        function setFeeConfig(
                            address _feeAddress,
                            uint256 _feeBasisPoints
                        ) external onlyRestakeManagerAdmin {
                            // Verify address is set if basis points are non-zero
                            if (_feeBasisPoints > 0) {
                                if (_feeAddress == address(0x0)) revert InvalidZeroInput();
                            }
                            // Verify basis points are not over 100%
                            if (_feeBasisPoints > 10000) revert OverMaxBasisPoints();
                            feeAddress = _feeAddress;
                            feeBasisPoints = _feeBasisPoints;
                            emit FeeConfigUpdated(_feeAddress, _feeBasisPoints);
                        }
                        /// @dev Sets the address of the RestakeManager contract
                        function setRestakeManager(IRestakeManager _restakeManager) external onlyRestakeManagerAdmin {
                            if (address(_restakeManager) == address(0x0)) revert InvalidZeroInput();
                            restakeManager = _restakeManager;
                            emit RestakeManagerUpdated(_restakeManager);
                        }
                        /// @dev Handle ETH sent to the protocol through the RestakeManager - e.g. user deposits
                        /// ETH will be stored here until used for a validator deposit
                        /// Fill the ETH withdraw buffer if required
                        function depositETHFromProtocol() external payable onlyRestakeManager {
                            _checkAndFillETHWithdrawBuffer(msg.value);
                            emit ETHDepositedFromProtocol(msg.value);
                        }
                        /**
                         * @notice  Fill up ERC20 withdraw buffer
                         * @dev     permissioned call (onlyRestakeManager)
                         * @param   _asset  address of asset to fill up the buffer for
                         * @param   _amount  amount of token to fill up the buffer with
                         */
                        function fillERC20withdrawBuffer(address _asset, uint256 _amount) external nonReentrant {
                            if (_amount == 0 || _asset == address(0)) revert InvalidZeroInput();
                            // safeTransfer from restake manager to this address
                            IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
                            // approve the token amount for withdraw queue
                            IERC20(_asset).safeIncreaseAllowance(address(withdrawQueue), _amount);
                            // call the withdraw queue to fill up the buffer
                            withdrawQueue.fillERC20WithdrawBuffer(_asset, _amount);
                        }
                        /**
                         * @notice  accepts full withdrawal ETH from Operator Delegators
                         * @notice  WARNING: users should not send ETH directly by this function
                         * @dev     check and fill ETH withdraw bufffer if required
                         */
                        function forwardFullWithdrawalETH() external payable nonReentrant {
                            // Check and fill ETH withdraw buffer if required
                            _checkAndFillETHWithdrawBuffer(msg.value);
                            emit FullWithdrawalETHReceived(msg.value);
                        }
                        /// @dev Handle ETH sent to this contract from outside the protocol - e.g. rewards
                        /// ETH will be stored here until used for a validator deposit
                        /// This should receive ETH from scenarios like Execution Layer Rewards and MEV from native staking
                        /// Users should NOT send ETH directly to this contract unless they want to donate to existing ezETH holders
                        /// Checks the ETH withdraw Queue and fills up if required
                        receive() external payable nonReentrant {
                            uint256 feeAmount = 0;
                            // Take protocol cut of rewards if enabled
                            if (feeAddress != address(0x0) && feeBasisPoints > 0) {
                                feeAmount = (msg.value * feeBasisPoints) / 10000;
                                (bool success, ) = feeAddress.call{ value: feeAmount }("");
                                if (!success) revert TransferFailed();
                                emit ProtocolFeesPaid(IERC20(address(0x0)), feeAmount, feeAddress);
                            }
                            // update remaining rewards
                            uint256 remainingRewards = msg.value - feeAmount;
                            // Check and fill ETH withdraw buffer if required
                            _checkAndFillETHWithdrawBuffer(remainingRewards);
                            // Emit the rewards event
                            emit RewardsDeposited(IERC20(address(0x0)), remainingRewards);
                        }
                        /// @dev Function called by ETH Restake Admin to start the restaking process in Native ETH
                        /// Only callable by a permissioned account
                        function stakeEthFromQueue(
                            IOperatorDelegator operatorDelegator,
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external onlyNativeEthRestakeAdmin {
                            uint256 gasBefore = gasleft();
                            // Send the ETH and the params through to the restake manager
                            restakeManager.stakeEthInOperatorDelegator{ value: 32 ether }(
                                operatorDelegator,
                                pubkey,
                                signature,
                                depositDataRoot
                            );
                            emit ETHStakedFromQueue(operatorDelegator, pubkey, 32 ether, address(this).balance);
                            // Refund the gas to the Admin address if enough ETH available
                            _refundGas(gasBefore);
                        }
                        /// @dev Function called by ETH Restake Admin to start the restaking process in Native ETH
                        /// Only callable by a permissioned account
                        /// Can stake multiple validators with 1 tx
                        function stakeEthFromQueueMulti(
                            IOperatorDelegator[] calldata operatorDelegators,
                            bytes[] calldata pubkeys,
                            bytes[] calldata signatures,
                            bytes32[] calldata depositDataRoots
                        ) external onlyNativeEthRestakeAdmin nonReentrant {
                            uint256 gasBefore = gasleft();
                            // Verify all arrays are the same length
                            if (
                                operatorDelegators.length != pubkeys.length ||
                                operatorDelegators.length != signatures.length ||
                                operatorDelegators.length != depositDataRoots.length
                            ) revert MismatchedArrayLengths();
                            // Iterate through the arrays and stake each one
                            uint256 arrayLength = operatorDelegators.length;
                            for (uint256 i = 0; i < arrayLength; ) {
                                // Send the ETH and the params through to the restake manager
                                restakeManager.stakeEthInOperatorDelegator{ value: 32 ether }(
                                    operatorDelegators[i],
                                    pubkeys[i],
                                    signatures[i],
                                    depositDataRoots[i]
                                );
                                emit ETHStakedFromQueue(
                                    operatorDelegators[i],
                                    pubkeys[i],
                                    32 ether,
                                    address(this).balance
                                );
                                unchecked {
                                    ++i;
                                }
                            }
                            // Refund the gas to the Admin address if enough ETH available
                            _refundGas(gasBefore);
                        }
                        /// @dev Sweeps any accumulated ERC20 tokens in this contract to the RestakeManager
                        /// Only callable by a permissioned account
                        function sweepERC20(IERC20 token) external onlyERC20RewardsAdmin {
                            uint256 balance = IERC20(token).balanceOf(address(this));
                            if (balance > 0) {
                                uint256 feeAmount = 0;
                                // Sweep fees if configured
                                if (feeAddress != address(0x0) && feeBasisPoints > 0) {
                                    feeAmount = (balance * feeBasisPoints) / 10000;
                                    IERC20(token).safeTransfer(feeAddress, feeAmount);
                                    emit ProtocolFeesPaid(token, feeAmount, feeAddress);
                                }
                                // Approve and deposit the rewards
                                token.safeIncreaseAllowance(address(restakeManager), balance - feeAmount);
                                restakeManager.depositTokenRewardsFromProtocol(token, balance - feeAmount);
                                // Emit the rewards event
                                emit RewardsDeposited(IERC20(address(token)), balance - feeAmount);
                            }
                        }
                        /**
                         * @notice Internal function used to refund gas to admin accounts if enough balance
                         * @param initialGas Initial Gas available
                         */
                        function _refundGas(uint256 initialGas) internal {
                            uint256 gasUsed = (initialGas - gasleft()) * block.basefee;
                            uint256 gasRefund = address(this).balance >= gasUsed ? gasUsed : address(this).balance;
                            (bool success, ) = payable(msg.sender).call{ value: gasRefund }("");
                            if (!success) revert TransferFailed();
                            emit GasRefunded(msg.sender, gasRefund);
                        }
                        /**
                         * @notice  Check if WithdrawBuffer Needs to be filled
                         */
                        function _checkAndFillETHWithdrawBuffer(uint256 _amount) internal {
                            // Check the withdraw buffer and fill if below buffer target
                            uint256 bufferToFill = withdrawQueue.getWithdrawDeficit(IS_NATIVE);
                            if (bufferToFill > 0) {
                                bufferToFill = (_amount <= bufferToFill) ? _amount : bufferToFill;
                                // fill withdraw buffer from received ETH
                                withdrawQueue.fillEthWithdrawBuffer{ value: bufferToFill }();
                                emit BufferFilled(IS_NATIVE, bufferToFill);
                            }
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "../Permissions/IRoleManager.sol";
                    import "../Withdraw/IWithdrawQueue.sol";
                    import "../IRestakeManager.sol";
                    import "./IDepositQueue.sol";
                    abstract contract DepositQueueStorageV1 is IDepositQueue {
                        /// @dev reference to the RoleManager contract
                        IRoleManager public roleManager;
                        /// @dev the address of the RestakeManager contract
                        IRestakeManager public restakeManager;
                        /// @dev the address where fees will be sent - must be non zero to enable fees
                        address public feeAddress;
                        /// @dev the basis points to charge for fees - 100 basis points = 1%
                        uint256 public feeBasisPoints;
                        /// Note: Deprecated, not getting used anymore
                        /// @dev the total amount the protocol has earned - token address => amount
                        mapping(address => uint256) public totalEarned;
                    }
                    abstract contract DepositQueueStorageV2 is DepositQueueStorageV1 {
                        IWithdrawQueue public withdrawQueue;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "../Withdraw/IWithdrawQueue.sol";
                    interface IDepositQueue {
                        function depositETHFromProtocol() external payable;
                        function totalEarned(address tokenAddress) external view returns (uint256);
                        function forwardFullWithdrawalETH() external payable;
                        function withdrawQueue() external view returns (IWithdrawQueue);
                        function fillERC20withdrawBuffer(address _asset, uint256 _amount) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title Interface for the BeaconStateOracle contract.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IBeaconChainOracle {
                        /// @notice The block number to state root mapping.
                        function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    interface IDelayedWithdrawalRouter {
                        // struct used to pack data into a single storage slot
                        struct DelayedWithdrawal {
                            uint224 amount;
                            uint32 blockCreated;
                        }
                        // struct used to store a single users delayedWithdrawal data
                        struct UserDelayedWithdrawals {
                            uint256 delayedWithdrawalsCompleted;
                            DelayedWithdrawal[] delayedWithdrawals;
                        }
                        /// @notice event for delayedWithdrawal creation
                        event DelayedWithdrawalCreated(
                            address podOwner,
                            address recipient,
                            uint256 amount,
                            uint256 index
                        );
                        /// @notice event for the claiming of delayedWithdrawals
                        event DelayedWithdrawalsClaimed(
                            address recipient,
                            uint256 amountClaimed,
                            uint256 delayedWithdrawalsCompleted
                        );
                        /// @notice Emitted when the `withdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
                        /**
                         * @notice Creates an delayed withdrawal for `msg.value` to the `recipient`.
                         * @dev Only callable by the `podOwner`'s EigenPod contract.
                         */
                        function createDelayedWithdrawal(address podOwner, address recipient) external payable;
                        /**
                         * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period.
                         * @param recipient The address to claim delayedWithdrawals for.
                         * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                         */
                        function claimDelayedWithdrawals(
                            address recipient,
                            uint256 maxNumberOfWithdrawalsToClaim
                        ) external;
                        /**
                         * @notice Called in order to withdraw delayed withdrawals made to the caller that have passed the `withdrawalDelayBlocks` period.
                         * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                         */
                        function claimDelayedWithdrawals(uint256 maxNumberOfWithdrawalsToClaim) external;
                        /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable.
                        function setWithdrawalDelayBlocks(uint256 newValue) external;
                        /// @notice Getter function for the mapping `_userWithdrawals`
                        function userWithdrawals(address user) external view returns (UserDelayedWithdrawals memory);
                        /// @notice Getter function to get all delayedWithdrawals of the `user`
                        function getUserDelayedWithdrawals(
                            address user
                        ) external view returns (DelayedWithdrawal[] memory);
                        /// @notice Getter function to get all delayedWithdrawals that are currently claimable by the `user`
                        function getClaimableUserDelayedWithdrawals(
                            address user
                        ) external view returns (DelayedWithdrawal[] memory);
                        /// @notice Getter function for fetching the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array
                        function userDelayedWithdrawalByIndex(
                            address user,
                            uint256 index
                        ) external view returns (DelayedWithdrawal memory);
                        /// @notice Getter function for fetching the length of the delayedWithdrawals array of a specific user
                        function userWithdrawalsLength(address user) external view returns (uint256);
                        /// @notice Convenience function for checking whether or not the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array is currently claimable
                        function canClaimDelayedWithdrawal(address user, uint256 index) external view returns (bool);
                        /**
                         * @notice Delay enforced by this contract for completing any delayedWithdrawal. Measured in blocks, and adjustable by this contract's owner,
                         * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
                         */
                        function withdrawalDelayBlocks() external view returns (uint256);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "./IStrategy.sol";
                    import "./ISignatureUtils.sol";
                    import "./IStrategyManager.sol";
                    /**
                     * @title DelegationManager
                     * @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 operators to specify parameters related to stakers who delegate to them
                     * - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
                     * - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
                     */
                    interface IDelegationManager is ISignatureUtils {
                        // Access to public vars - hack locally
                        function beaconChainETHStrategy() external returns (IStrategy);
                        function pendingWithdrawals(bytes32 withdrawalRoot) external view returns (bool);
                        function getDelegatableShares(
                            address staker
                        ) external view returns (IStrategy[] memory, uint256[] memory);
                        // @notice Struct used for storing information about a single operator who has registered with EigenLayer
                        struct OperatorDetails {
                            // @notice address to receive the rewards that the operator earns via serving applications built on EigenLayer.
                            address earningsReceiver;
                            /**
                             * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
                             * @dev Signature verification follows these rules:
                             * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
                             * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
                             * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
                             */
                            address delegationApprover;
                            /**
                             * @notice A minimum delay -- measured in blocks -- enforced between:
                             * 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
                             * and
                             * 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
                             * @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
                             * then they are only allowed to either increase this value or keep it the same.
                             */
                            uint32 stakerOptOutWindowBlocks;
                        }
                        /**
                         * @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator.
                         * @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function.
                         */
                        struct StakerDelegation {
                            // the staker who is delegating
                            address staker;
                            // the operator being delegated to
                            address operator;
                            // the staker's nonce
                            uint256 nonce;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        /**
                         * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
                         * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
                         */
                        struct DelegationApproval {
                            // the staker who is delegating
                            address staker;
                            // the operator being delegated to
                            address operator;
                            // the operator's provided salt
                            bytes32 salt;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        /**
                         * 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. 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 Withdrawal {
                            // The address that originated the Withdrawal
                            address staker;
                            // The address that the staker was delegated to at the time that the Withdrawal was created
                            address delegatedTo;
                            // The address that can complete the Withdrawal + will receive funds when completing the withdrawal
                            address withdrawer;
                            // Nonce used to guarantee that otherwise identical withdrawals have unique hashes
                            uint256 nonce;
                            // Block number when the Withdrawal was created
                            uint32 startBlock;
                            // Array of strategies that the Withdrawal contains
                            IStrategy[] strategies;
                            // Array containing the amount of shares in each Strategy in the `strategies` array
                            uint256[] shares;
                        }
                        struct QueuedWithdrawalParams {
                            // Array of strategies that the QueuedWithdrawal contains
                            IStrategy[] strategies;
                            // Array containing the amount of shares in each Strategy in the `strategies` array
                            uint256[] shares;
                            // The address of the withdrawer
                            address withdrawer;
                        }
                        // @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails.
                        event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails);
                        /// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails
                        event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails);
                        /**
                         * @notice Emitted when @param operator indicates that they are updating their MetadataURI string
                         * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
                         */
                        event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);
                        /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
                        event OperatorSharesIncreased(
                            address indexed operator,
                            address staker,
                            IStrategy strategy,
                            uint256 shares
                        );
                        /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
                        event OperatorSharesDecreased(
                            address indexed operator,
                            address staker,
                            IStrategy strategy,
                            uint256 shares
                        );
                        /// @notice Emitted when @param staker delegates to @param operator.
                        event StakerDelegated(address indexed staker, address indexed operator);
                        /// @notice Emitted when @param staker undelegates from @param operator.
                        event StakerUndelegated(address indexed staker, address indexed operator);
                        /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
                        event StakerForceUndelegated(address indexed staker, address indexed operator);
                        /**
                         * @notice Emitted when a new withdrawal is queued.
                         * @param withdrawalRoot Is the hash of the `withdrawal`.
                         * @param withdrawal Is the withdrawal itself.
                         */
                        event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal);
                        /// @notice Emitted when a queued withdrawal is completed
                        event WithdrawalCompleted(bytes32 withdrawalRoot);
                        /// @notice Emitted when a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager
                        event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot);
                        /// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
                        /// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event StrategyWithdrawalDelayBlocksSet(
                            IStrategy strategy,
                            uint256 previousValue,
                            uint256 newValue
                        );
                        /**
                         * @notice Registers the caller as an operator in EigenLayer.
                         * @param registeringOperatorDetails is the `OperatorDetails` for the operator.
                         * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
                         *
                         * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
                         * @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
                         * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
                         */
                        function registerAsOperator(
                            OperatorDetails calldata registeringOperatorDetails,
                            string calldata metadataURI
                        ) external;
                        /**
                         * @notice Updates an operator's stored `OperatorDetails`.
                         * @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
                         *
                         * @dev The caller must have previously registered as an operator in EigenLayer.
                         * @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
                         */
                        function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external;
                        /**
                         * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
                         * @param metadataURI The URI for metadata associated with an operator
                         * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
                         */
                        function updateOperatorMetadataURI(string calldata metadataURI) external;
                        /**
                         * @notice Caller delegates their stake to an operator.
                         * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer.
                         * @param approverSignatureAndExpiry Verifies the operator approves of this delegation
                         * @param approverSalt A unique single use value tied to an individual signature.
                         * @dev The approverSignatureAndExpiry is used in the event that:
                         *          1) the operator's `delegationApprover` address is set to a non-zero value.
                         *                  AND
                         *          2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
                         *             or their delegationApprover is the `msg.sender`, then approval is assumed.
                         * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
                         * in this case to save on complexity + gas costs
                         */
                        function delegateTo(
                            address operator,
                            SignatureWithExpiry memory approverSignatureAndExpiry,
                            bytes32 approverSalt
                        ) external;
                        /**
                         * @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
                         * @param staker The account delegating stake to an `operator` account
                         * @param operator The account (`staker`) is delegating its assets to for use in serving applications built on EigenLayer.
                         * @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
                         * @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
                         * @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
                         *
                         * @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
                         * @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
                         * @dev the operator's `delegationApprover` address is set to a non-zero value.
                         * @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
                         * is the `msg.sender`, then approval is assumed.
                         * @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
                         * @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
                         * in this case to save on complexity + gas costs
                         */
                        function delegateToBySignature(
                            address staker,
                            address operator,
                            SignatureWithExpiry memory stakerSignatureAndExpiry,
                            SignatureWithExpiry memory approverSignatureAndExpiry,
                            bytes32 approverSalt
                        ) external;
                        /**
                         * @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager
                         * and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary.
                         * @param staker The account to be undelegated.
                         * @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0).
                         *
                         * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
                         * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
                         * @dev Reverts if the `staker` is already undelegated.
                         */
                        function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot);
                        /**
                         * Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
                         * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
                         * their operator.
                         *
                         * All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
                         */
                        function queueWithdrawals(
                            QueuedWithdrawalParams[] calldata queuedWithdrawalParams
                        ) external returns (bytes32[] memory);
                        /**
                         * @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
                         * @param withdrawal The Withdrawal 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 `withdrawal.strategies` array.
                         * 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 withdrawal will be withdrawn from the specified strategies themselves
                         * and sent to the caller, through calls to `withdrawal.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`
                         * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
                         * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
                         * any other strategies, which will be transferred to the withdrawer.
                         */
                        function completeQueuedWithdrawal(
                            Withdrawal calldata withdrawal,
                            IERC20[] calldata tokens,
                            uint256 middlewareTimesIndex,
                            bool receiveAsTokens
                        ) external;
                        /**
                         * @notice Array-ified version of `completeQueuedWithdrawal`.
                         * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
                         * @param withdrawals The Withdrawals to complete.
                         * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
                         * @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
                         * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
                         * @dev See `completeQueuedWithdrawal` for relevant dev tags
                         */
                        function completeQueuedWithdrawals(
                            Withdrawal[] calldata withdrawals,
                            IERC20[][] calldata tokens,
                            uint256[] calldata middlewareTimesIndexes,
                            bool[] calldata receiveAsTokens
                        ) external;
                        /**
                         * @notice Increases a staker's delegated share balance in a strategy.
                         * @param staker The address to increase the delegated shares for their operator.
                         * @param strategy The strategy in which to increase the delegated shares.
                         * @param shares The number of shares to increase.
                         *
                         * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
                         * @dev Callable only by the StrategyManager or EigenPodManager.
                         */
                        function increaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
                        /**
                         * @notice Decreases a staker's delegated share balance in a strategy.
                         * @param staker The address to increase the delegated shares for their operator.
                         * @param strategy The strategy in which to decrease the delegated shares.
                         * @param shares The number of shares to decrease.
                         *
                         * @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
                         * @dev Callable only by the StrategyManager or EigenPodManager.
                         */
                        function decreaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
                        /**
                         * @notice returns the address of the operator that `staker` is delegated to.
                         * @notice Mapping: staker => operator whom the staker is currently delegated to.
                         * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
                         */
                        function delegatedTo(address staker) external view returns (address);
                        /**
                         * @notice Returns the OperatorDetails struct associated with an `operator`.
                         */
                        function operatorDetails(address operator) external view returns (OperatorDetails memory);
                        /*
                         * @notice Returns the earnings receiver address for an operator
                         */
                        function earningsReceiver(address operator) external view returns (address);
                        /**
                         * @notice Returns the delegationApprover account for an operator
                         */
                        function delegationApprover(address operator) external view returns (address);
                        /**
                         * @notice Returns the stakerOptOutWindowBlocks for an operator
                         */
                        function stakerOptOutWindowBlocks(address operator) external view returns (uint256);
                        /**
                         * @notice Given array of strategies, returns array of shares for the operator
                         */
                        function getOperatorShares(
                            address operator,
                            IStrategy[] memory strategies
                        ) external view returns (uint256[] memory);
                        /**
                         * @notice Given a list of strategies, return the minimum number of blocks that must pass to withdraw
                         * from all the inputted strategies. Return value is >= minWithdrawalDelayBlocks as this is the global min withdrawal delay.
                         * @param strategies The strategies to check withdrawal delays for
                         */
                        function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256);
                        /**
                         * @notice returns the total number of shares in `strategy` that are delegated to `operator`.
                         * @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
                         * @dev By design, the following invariant should hold for each Strategy:
                         * (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
                         * = sum (delegateable shares of all stakers delegated to the operator)
                         */
                        function operatorShares(address operator, IStrategy strategy) external view returns (uint256);
                        /**
                         * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
                         */
                        function isDelegated(address staker) external view returns (bool);
                        /**
                         * @notice Returns true is an operator has previously registered for delegation.
                         */
                        function isOperator(address operator) external view returns (bool);
                        /// @notice Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked
                        function stakerNonce(address staker) external view returns (uint256);
                        /**
                         * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
                         * @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's
                         * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
                         */
                        function delegationApproverSaltIsSpent(
                            address _delegationApprover,
                            bytes32 salt
                        ) external view returns (bool);
                        /**
                         * @notice Minimum delay enforced by this contract for completing queued withdrawals. 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).
                         * Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum number of blocks that must pass
                         * to withdraw a strategy is MAX(minWithdrawalDelayBlocks, strategyWithdrawalDelayBlocks[strategy])
                         */
                        function minWithdrawalDelayBlocks() external view returns (uint256);
                        /**
                         * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. 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 strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256);
                        /**
                         * @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
                         * @param staker The signing staker
                         * @param operator The operator who is being delegated to
                         * @param expiry The desired expiry time of the staker's signature
                         */
                        function calculateCurrentStakerDelegationDigestHash(
                            address staker,
                            address operator,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /**
                         * @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
                         * @param staker The signing staker
                         * @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
                         * @param operator The operator who is being delegated to
                         * @param expiry The desired expiry time of the staker's signature
                         */
                        function calculateStakerDelegationDigestHash(
                            address staker,
                            uint256 _stakerNonce,
                            address operator,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /**
                         * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
                         * @param staker The account delegating their stake
                         * @param operator The account receiving delegated stake
                         * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
                         * @param approverSalt A unique and single use value associated with the approver signature.
                         * @param expiry Time after which the approver's signature becomes invalid
                         */
                        function calculateDelegationApprovalDigestHash(
                            address staker,
                            address operator,
                            address _delegationApprover,
                            bytes32 approverSalt,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the contract's domain
                        function DOMAIN_TYPEHASH() external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract
                        function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
                        function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
                        /**
                         * @notice Getter function for the current EIP-712 domain separator for this contract.
                         *
                         * @dev The domain separator will change in the event of a fork that changes the ChainID.
                         * @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
                         * for more detailed information please read EIP-712.
                         */
                        function domainSeparator() external view returns (bytes32);
                        /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
                        /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
                        function cumulativeWithdrawalsQueued(address staker) external view returns (uint256);
                        /// @notice Returns the keccak256 hash of `withdrawal`.
                        function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32);
                        function migrateQueuedWithdrawals(
                            IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue
                        ) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "../libraries/BeaconChainProofs.sol";
                    import "./IEigenPodManager.sol";
                    import "./IBeaconChainOracle.sol";
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "./IDelayedWithdrawalRouter.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 {
                        // Hack to allow the external visibility of the public variable for default getter in EigenPod
                        function delayedWithdrawalRouter() external returns (IDelayedWithdrawalRouter);
                        enum VALIDATOR_STATUS {
                            INACTIVE, // doesnt exist
                            ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
                            WITHDRAWN // withdrawn from the Beacon Chain
                        }
                        struct ValidatorInfo {
                            // index of the validator in the beacon chain
                            uint64 validatorIndex;
                            // amount of beacon chain ETH restaked on EigenLayer in gwei
                            uint64 restakedBalanceGwei;
                            //timestamp of the validator's most recent balance update
                            uint64 mostRecentBalanceUpdateTimestamp;
                            // status of the validator
                            VALIDATOR_STATUS status;
                        }
                        /**
                         * @notice struct used to store amounts related to proven withdrawals in memory. Used to help
                         * manage stack depth and optimize the number of external calls, when batching withdrawal operations.
                         */
                        struct VerifiedWithdrawal {
                            // amount to send to a podOwner from a proven withdrawal
                            uint256 amountToSendGwei;
                            // difference in shares to be recorded in the eigenPodManager, as a result of the withdrawal
                            int256 sharesDeltaGwei;
                        }
                        enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
                            REDEEMED,
                            PENDING,
                            FAILED
                        }
                        /// @notice Emitted when an ETH validator stakes via this eigenPod
                        event EigenPodStaked(bytes pubkey);
                        /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
                        event ValidatorRestaked(uint40 validatorIndex);
                        /// @notice Emitted when an ETH validator's  balance is proven to be updated.  Here newValidatorBalanceGwei
                        //  is the validator's balance that is credited on EigenLayer.
                        event ValidatorBalanceUpdated(
                            uint40 validatorIndex,
                            uint64 balanceTimestamp,
                            uint64 newValidatorBalanceGwei
                        );
                        /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
                        event FullWithdrawalRedeemed(
                            uint40 validatorIndex,
                            uint64 withdrawalTimestamp,
                            address indexed recipient,
                            uint64 withdrawalAmountGwei
                        );
                        /// @notice Emitted when a partial withdrawal claim is successfully redeemed
                        event PartialWithdrawalRedeemed(
                            uint40 validatorIndex,
                            uint64 withdrawalTimestamp,
                            address indexed recipient,
                            uint64 partialWithdrawalAmountGwei
                        );
                        /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
                        event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
                        /// @notice Emitted when podOwner enables restaking
                        event RestakingActivated(address indexed podOwner);
                        /// @notice Emitted when ETH is received via the `receive` fallback
                        event NonBeaconChainETHReceived(uint256 amountReceived);
                        /// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
                        event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);
                        /// @notice The max amount of eth, in gwei, that can be restaked per validator
                        function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);
                        /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
                        function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);
                        /// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
                        function nonBeaconChainETHBalanceWei() external view returns (uint256);
                        /// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
                        function initialize(address owner) external;
                        /// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
                        function stake(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        /**
                         * @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address
                         * @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain.
                         * @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `withdrawableRestakedExecutionLayerGwei` exceeds the
                         * `amountWei` input (when converted to GWEI).
                         * @dev Reverts if `amountWei` is not a whole Gwei amount
                         */
                        function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;
                        /// @notice The single EigenPodManager for EigenLayer
                        function eigenPodManager() external view returns (IEigenPodManager);
                        /// @notice The owner of this EigenPod
                        function podOwner() external view returns (address);
                        /// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
                        function hasRestaked() external view returns (bool);
                        /**
                         * @notice The latest timestamp at which the pod owner withdrew the balance of the pod, via calling `withdrawBeforeRestaking`.
                         * @dev This variable is only updated when the `withdrawBeforeRestaking` function is called, which can only occur before `hasRestaked` is set to true for this pod.
                         * Proofs for this pod are only valid against Beacon Chain state roots corresponding to timestamps after the stored `mostRecentWithdrawalTimestamp`.
                         */
                        function mostRecentWithdrawalTimestamp() external view returns (uint64);
                        /// @notice Returns the validatorInfo struct for the provided pubkeyHash
                        function validatorPubkeyHashToInfo(
                            bytes32 validatorPubkeyHash
                        ) external view returns (ValidatorInfo memory);
                        /// @notice Returns the validatorInfo struct for the provided pubkey
                        function validatorPubkeyToInfo(
                            bytes calldata validatorPubkey
                        ) external view returns (ValidatorInfo memory);
                        ///@notice mapping that tracks proven withdrawals
                        function provenWithdrawal(
                            bytes32 validatorPubkeyHash,
                            uint64 slot
                        ) external view returns (bool);
                        /// @notice This returns the status of a given validator
                        function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS);
                        /// @notice This returns the status of a given validator pubkey
                        function validatorStatus(
                            bytes calldata validatorPubkey
                        ) external view returns (VALIDATOR_STATUS);
                        /**
                         * @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to
                         * this contract. It also verifies the effective balance  of the validator.  It verifies the provided proof of the ETH validator against the beacon chain state
                         * root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer.
                         * @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against.
                         * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
                         * @param withdrawalCredentialProofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials
                         * against a beacon chain state root
                         * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                         * for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                         */
                        function verifyWithdrawalCredentials(
                            uint64 oracleTimestamp,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            uint40[] calldata validatorIndices,
                            bytes[] calldata withdrawalCredentialProofs,
                            bytes32[][] calldata validatorFields
                        ) external;
                        /**
                         * @notice This function records an update (either increase or decrease) in the pod's balance in the StrategyManager.  
                                   It also verifies a merkle proof of the validator's current beacon chain balance.  
                         * @param oracleTimestamp The oracleTimestamp whose state root the `proof` will be proven against.
                         *        Must be within `VERIFY_BALANCE_UPDATE_WINDOW_SECONDS` of the current block.
                         * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs 
                         * @param validatorFieldsProofs proofs against the `beaconStateRoot` for each validator in `validatorFields`
                         * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                         * @dev For more details on the Beacon Chain spec, see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                         */
                        function verifyBalanceUpdates(
                            uint64 oracleTimestamp,
                            uint40[] calldata validatorIndices,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            bytes[] calldata validatorFieldsProofs,
                            bytes32[][] calldata validatorFields
                        ) external;
                        /**
                         * @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod
                         * @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against
                         * @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven
                         * @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree
                         * @param withdrawalFields are the fields of the withdrawals being proven
                         * @param validatorFields are the fields of the validators being proven
                         */
                        function verifyAndProcessWithdrawals(
                            uint64 oracleTimestamp,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            BeaconChainProofs.WithdrawalProof[] calldata withdrawalProofs,
                            bytes[] calldata validatorFieldsProofs,
                            bytes32[][] calldata validatorFields,
                            bytes32[][] calldata withdrawalFields
                        ) external;
                        /**
                         * @notice Called by the pod owner to activate restaking by withdrawing
                         * all existing ETH from the pod and preventing further withdrawals via
                         * "withdrawBeforeRestaking()"
                         */
                        function activateRestaking() external;
                        /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                        function withdrawBeforeRestaking() external;
                        /// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei
                        function withdrawNonBeaconChainETHBalanceWei(
                            address recipient,
                            uint256 amountToWithdraw
                        ) external;
                        /// @notice called by owner of a pod to remove any ERC20s deposited in the pod
                        function recoverTokens(
                            IERC20[] memory tokenList,
                            uint256[] memory amountsToWithdraw,
                            address recipient
                        ) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
                    import "./IETHPOSDeposit.sol";
                    import "./IStrategyManager.sol";
                    import "./IEigenPod.sol";
                    import "./IBeaconChainOracle.sol";
                    import "./IPausable.sol";
                    import "./ISlasher.sol";
                    import "./IStrategy.sol";
                    /**
                     * @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IEigenPodManager is IPausable {
                        /// @notice Emitted to notify the update of the beaconChainOracle address
                        event BeaconOracleUpdated(address indexed newOracleAddress);
                        /// @notice Emitted to notify the deployment of an EigenPod
                        event PodDeployed(address indexed eigenPod, address indexed podOwner);
                        /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
                        event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);
                        /// @notice Emitted when the balance of an EigenPod is updated
                        event PodSharesUpdated(address indexed podOwner, int256 sharesDelta);
                        /// @notice Emitted when a withdrawal of beacon chain ETH is completed
                        event BeaconChainETHWithdrawalCompleted(
                            address indexed podOwner,
                            uint256 shares,
                            uint96 nonce,
                            address delegatedAddress,
                            address withdrawer,
                            bytes32 withdrawalRoot
                        );
                        event DenebForkTimestampUpdated(uint64 newValue);
                        /**
                         * @notice Creates an EigenPod for the sender.
                         * @dev Function will revert if the `msg.sender` already has an EigenPod.
                         * @dev Returns EigenPod address
                         */
                        function createPod() external returns (address);
                        /**
                         * @notice Stakes for a new beacon chain validator on the sender's EigenPod.
                         * Also creates an EigenPod for the sender if they don't have one already.
                         * @param pubkey The 48 bytes public key of the beacon chain validator.
                         * @param signature The validator's signature of the deposit data.
                         * @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
                         */
                        function stake(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        /**
                         * @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager
                         * to ensure that delegated shares are also tracked correctly
                         * @param podOwner is the pod owner whose balance is being updated.
                         * @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
                         * @dev Callable only by the podOwner's EigenPod contract.
                         * @dev Reverts if `sharesDelta` is not a whole Gwei amount
                         */
                        function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
                        /**
                         * @notice Updates the oracle contract that provides the beacon chain state root
                         * @param newBeaconChainOracle is the new oracle contract being pointed to
                         * @dev Callable only by the owner of this contract (i.e. governance)
                         */
                        function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external;
                        /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
                        function ownerToPod(address podOwner) external view returns (IEigenPod);
                        /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
                        function getPod(address podOwner) external view returns (IEigenPod);
                        /// @notice The ETH2 Deposit Contract
                        function ethPOS() external view returns (IETHPOSDeposit);
                        /// @notice Beacon proxy to which the EigenPods point
                        function eigenPodBeacon() external view returns (IBeacon);
                        /// @notice Oracle contract that provides updates to the beacon chain's state
                        function beaconChainOracle() external view returns (IBeaconChainOracle);
                        /// @notice Returns the beacon block root at `timestamp`. Reverts if the Beacon block root at `timestamp` has not yet been finalized.
                        function getBlockRootAtTimestamp(uint64 timestamp) external view returns (bytes32);
                        /// @notice EigenLayer's StrategyManager contract
                        function strategyManager() external view returns (IStrategyManager);
                        /// @notice EigenLayer's Slasher contract
                        function slasher() external view returns (ISlasher);
                        /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
                        function hasPod(address podOwner) external view returns (bool);
                        /// @notice Returns the number of EigenPods that have been created
                        function numPods() external view returns (uint256);
                        /**
                         * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
                         * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
                         * decrease between the pod owner queuing and completing a withdrawal.
                         * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
                         * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
                         * as the withdrawal "paying off the deficit".
                         */
                        function podOwnerShares(address podOwner) external view returns (int256);
                        /// @notice returns canonical, virtual beaconChainETH strategy
                        function beaconChainETHStrategy() external view returns (IStrategy);
                        /**
                         * @notice Used by the DelegationManager to remove a pod owner's shares while they're in the withdrawal queue.
                         * Simply decreases the `podOwner`'s shares by `shares`, down to a minimum of zero.
                         * @dev This function reverts if it would result in `podOwnerShares[podOwner]` being less than zero, i.e. it is forbidden for this function to
                         * result in the `podOwner` incurring a "share deficit". This behavior prevents a Staker from queuing a withdrawal which improperly removes excessive
                         * shares from the operator to whom the staker is delegated.
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function removeShares(address podOwner, uint256 shares) external;
                        /**
                         * @notice Increases the `podOwner`'s shares by `shares`, paying off deficit if possible.
                         * Used by the DelegationManager to award a pod owner shares on exiting the withdrawal queue
                         * @dev Returns the number of shares added to `podOwnerShares[podOwner]` above zero, which will be less than the `shares` input
                         * in the event that the podOwner has an existing shares deficit (i.e. `podOwnerShares[podOwner]` starts below zero)
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function addShares(address podOwner, uint256 shares) external returns (uint256);
                        /**
                         * @notice Used by the DelegationManager to complete a withdrawal, sending tokens to some destination address
                         * @dev Prioritizes decreasing the podOwner's share deficit, if they have one
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;
                        /**
                         * @notice the deneb hard fork timestamp used to determine which proof path to use for proving a withdrawal
                         */
                        function denebForkTimestamp() external view returns (uint64);
                        /**
                         * setting the deneb hard fork timestamp by the eigenPodManager owner
                         * @dev this function is designed to be called twice.  Once, it is set to type(uint64).max
                         * prior to the actual deneb fork timestamp being set, and then the second time it is set
                         * to the actual deneb fork timestamp.
                         */
                        function setDenebForkTimestamp(uint64 newDenebForkTimestamp) external;
                    }
                    // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                    // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                    // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                    // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                    // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                    // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                    // SPDX-License-Identifier: CC0-1.0
                    pragma solidity >=0.5.0;
                    // This interface is designed to be compatible with the Vyper version.
                    /// @notice This is the Ethereum 2.0 deposit contract interface.
                    /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
                    interface IETHPOSDeposit {
                        /// @notice A processed deposit event.
                        event DepositEvent(
                            bytes pubkey,
                            bytes withdrawal_credentials,
                            bytes amount,
                            bytes signature,
                            bytes index
                        );
                        /// @notice Submit a Phase 0 DepositData object.
                        /// @param pubkey A BLS12-381 public key.
                        /// @param withdrawal_credentials Commitment to a public key for withdrawals.
                        /// @param signature A BLS12-381 signature.
                        /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
                        /// Used as a protection against malformed input.
                        function deposit(
                            bytes calldata pubkey,
                            bytes calldata withdrawal_credentials,
                            bytes calldata signature,
                            bytes32 deposit_data_root
                        ) external payable;
                        /// @notice Query the current deposit root hash.
                        /// @return The deposit root hash.
                        function get_deposit_root() external view returns (bytes32);
                        /// @notice Query the current deposit count.
                        /// @return The deposit count encoded as a little endian 64-bit number.
                        function get_deposit_count() external view returns (bytes memory);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "../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 Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
                        event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
                        /// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
                        event Paused(address indexed account, uint256 newPausedStatus);
                        /// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
                        event Unpaused(address indexed account, uint256 newPausedStatus);
                        /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
                        function pauserRegistry() external view returns (IPauserRegistry);
                        /**
                         * @notice This function is used to pause an EigenLayer contract's functionality.
                         * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
                         * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                         * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
                         */
                        function pause(uint256 newPausedStatus) external;
                        /**
                         * @notice Alias for `pause(type(uint256).max)`.
                         */
                        function pauseAll() external;
                        /**
                         * @notice This function is used to unpause an EigenLayer contract's functionality.
                         * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
                         * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                         * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
                         */
                        function unpause(uint256 newPausedStatus) external;
                        /// @notice Returns the current paused status as a uint256.
                        function paused() external view returns (uint256);
                        /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
                        function paused(uint8 index) external view returns (bool);
                        /// @notice Allows the unpauser to set a new pauser registry
                        function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title Interface for the `PauserRegistry` contract.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IPauserRegistry {
                        event PauserStatusChanged(address pauser, bool canPause);
                        event UnpauserChanged(address previousUnpauser, address newUnpauser);
                        /// @notice Mapping of addresses to whether they hold the pauser role.
                        function isPauser(address pauser) external view returns (bool);
                        /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
                        function unpauser() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title The interface for common signature utilities.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface ISignatureUtils {
                        // @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management.
                        struct SignatureWithExpiry {
                            // the signature itself, formatted as a single bytes object
                            bytes signature;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        // @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management.
                        struct SignatureWithSaltAndExpiry {
                            // the signature itself, formatted as a single bytes object
                            bytes signature;
                            // the salt used to generate the signature
                            bytes32 salt;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "./IStrategyManager.sol";
                    import "./IDelegationManager.sol";
                    /**
                     * @title Interface for the primary 'slashing' contract for EigenLayer.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     * @notice See the `Slasher` contract itself for implementation details.
                     */
                    interface ISlasher {
                        // struct used to store information about the current state of an operator's obligations to middlewares they are serving
                        struct MiddlewareTimes {
                            // The update block for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
                            uint32 stalestUpdateBlock;
                            // The latest 'serveUntilBlock' from all of the middleware that the operator is serving
                            uint32 latestServeUntilBlock;
                        }
                        // struct used to store details relevant to a single middleware that an operator has opted-in to serving
                        struct MiddlewareDetails {
                            // the block at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
                            uint32 registrationMayBeginAtBlock;
                            // the block before which the contract is allowed to slash the user
                            uint32 contractCanSlashOperatorUntilBlock;
                            // the block at which the middleware's view of the operator's stake was most recently updated
                            uint32 latestUpdateBlock;
                        }
                        /// @notice Emitted when a middleware times is added to `operator`'s array.
                        event MiddlewareTimesAdded(
                            address operator,
                            uint256 index,
                            uint32 stalestUpdateBlock,
                            uint32 latestServeUntilBlock
                        );
                        /// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
                        event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
                        /// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilBlock`.
                        event SlashingAbilityRevoked(
                            address indexed operator,
                            address indexed contractAddress,
                            uint32 contractCanSlashOperatorUntilBlock
                        );
                        /**
                         * @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
                         * @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
                         */
                        event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
                        /// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within EigenLayer.
                        event FrozenStatusReset(address indexed previouslySlashedAddress);
                        /**
                         * @notice Gives the `contractAddress` permission to slash the funds of the caller.
                         * @dev Typically, this function must be called prior to registering for a middleware.
                         */
                        function optIntoSlashing(address contractAddress) external;
                        /**
                         * @notice Used for 'slashing' a certain operator.
                         * @param toBeFrozen The operator to be frozen.
                         * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
                         * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
                         */
                        function freezeOperator(address toBeFrozen) external;
                        /**
                         * @notice Removes the 'frozen' status from each of the `frozenAddresses`
                         * @dev Callable only by the contract owner (i.e. governance).
                         */
                        function resetFrozenStatus(address[] calldata frozenAddresses) external;
                        /**
                         * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
                         *         is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                         * @dev adds the middleware's slashing contract to the operator's linked list
                         */
                        function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) external;
                        /**
                         * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
                         *         to make sure the operator's stake at updateBlock is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param updateBlock the block for which the stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable
                         * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
                         * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
                         *      but it is anticipated to be rare and not detrimental.
                         */
                        function recordStakeUpdate(
                            address operator,
                            uint32 updateBlock,
                            uint32 serveUntilBlock,
                            uint256 insertAfter
                        ) external;
                        /**
                         * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
                         *         is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                         * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
                         * slash `operator` once `serveUntil` is reached
                         */
                        function recordLastStakeUpdateAndRevokeSlashingAbility(
                            address operator,
                            uint32 serveUntilBlock
                        ) external;
                        /// @notice The StrategyManager contract of EigenLayer
                        function strategyManager() external view returns (IStrategyManager);
                        /// @notice The DelegationManager contract of EigenLayer
                        function delegation() external view returns (IDelegationManager);
                        /**
                         * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
                         * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
                         * and the staker's status is reset (to 'unfrozen').
                         * @param staker The staker of interest.
                         * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
                         * to an operator who has their status set to frozen. Otherwise returns 'false'.
                         */
                        function isFrozen(address staker) external view returns (bool);
                        /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
                        function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
                        /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`.
                        function contractCanSlashOperatorUntilBlock(
                            address operator,
                            address serviceContract
                        ) external view returns (uint32);
                        /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake
                        function latestUpdateBlock(
                            address operator,
                            address serviceContract
                        ) external view returns (uint32);
                        /// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
                        function getCorrectValueForInsertAfter(
                            address operator,
                            uint32 updateBlock
                        ) external view returns (uint256);
                        /**
                         * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used
                         * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
                         * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
                         * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event
                         * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
                         * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
                         * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`.
                         * @param withdrawalStartBlock The block number at which the withdrawal was initiated.
                         * @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
                         * @dev The correct `middlewareTimesIndex` input should be computable off-chain.
                         */
                        function canWithdraw(
                            address operator,
                            uint32 withdrawalStartBlock,
                            uint256 middlewareTimesIndex
                        ) external returns (bool);
                        /**
                         * operator =>
                         *  [
                         *      (
                         *          the least recent update block of all of the middlewares it's serving/served,
                         *          latest time that the stake bonded at that update needed to serve until
                         *      )
                         *  ]
                         */
                        function operatorToMiddlewareTimes(
                            address operator,
                            uint256 arrayIndex
                        ) external view returns (MiddlewareTimes memory);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
                        function middlewareTimesLength(address operator) external view returns (uint256);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`.
                        function getMiddlewareTimesIndexStalestUpdateBlock(
                            address operator,
                            uint32 index
                        ) external view returns (uint32);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
                        function getMiddlewareTimesIndexServeUntilBlock(
                            address operator,
                            uint32 index
                        ) external view returns (uint32);
                        /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
                        function operatorWhitelistedContractsLinkedListSize(
                            address operator
                        ) external view returns (uint256);
                        /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
                        function operatorWhitelistedContractsLinkedListEntry(
                            address operator,
                            address node
                        ) external view returns (bool, uint256, uint256);
                    }
                    // SPDX-License-Identifier: 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 `recipient`'s address
                         * @param recipient 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 recipient, 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 convenience function for fetching the current total shares of `user` in this strategy, by
                         * querying the `strategyManager` contract
                         */
                        function shares(address user) external view 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.5.0;
                    import "./IStrategy.sol";
                    import "./ISlasher.sol";
                    import "./IDelegationManager.sol";
                    import "./IEigenPodManager.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 {
                        // Access to public vars - hack locally
                        function stakerStrategyList(address staker, uint256 index) external view returns (IStrategy);
                        function strategyIsWhitelistedForDeposit(IStrategy _strategy) external view returns (bool);
                        /**
                         * @notice Emitted when a new deposit occurs on behalf of `staker`.
                         * @param staker Is the staker who is depositing funds into EigenLayer.
                         * @param strategy Is the strategy that `staker` has deposited into.
                         * @param token Is the token that `staker` deposited.
                         * @param shares Is the number of new shares `staker` has been granted in `strategy`.
                         */
                        event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares);
                        /// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner
                        event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value);
                        /// @notice Emitted when the `strategyWhitelister` is changed
                        event StrategyWhitelisterChanged(address previousAddress, address newAddress);
                        /// @notice Emitted when a strategy is added to the approved list of strategies for deposit
                        event StrategyAddedToDepositWhitelist(IStrategy strategy);
                        /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
                        event StrategyRemovedFromDepositWhitelist(IStrategy strategy);
                        /**
                         * @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 staker
                         * @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 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 staker
                         * @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 if thirdPartyTransfersForbidden is set to true for this strategy
                         *
                         *  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 Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
                        function removeShares(address staker, IStrategy strategy, uint256 shares) external;
                        /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
                        function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external;
                        /// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient
                        function withdrawSharesAsTokens(
                            address recipient,
                            IStrategy strategy,
                            uint256 shares,
                            IERC20 token
                        ) external;
                        /// @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 staker's deposits and corresponding shares
                         * @return (staker's strategies, shares in these strategies)
                         */
                        function getDeposits(
                            address staker
                        ) 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 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)
                         * @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy
                         */
                        function addStrategiesToDepositWhitelist(
                            IStrategy[] calldata strategiesToWhitelist,
                            bool[] calldata thirdPartyTransfersForbiddenValues
                        ) 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 EigenPodManager contract of EigenLayer
                        function eigenPodManager() external view returns (IEigenPodManager);
                        /// @notice Returns the address of the `strategyWhitelister`
                        function strategyWhitelister() external view returns (address);
                        /**
                         * @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling
                         * depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker.
                         */
                        function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool);
                        // LIMITED BACKWARDS-COMPATIBILITY FOR DEPRECATED FUNCTIONALITY
                        // packed struct for queued withdrawals; helps deal with stack-too-deep errors
                        struct DeprecatedStruct_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 DeprecatedStruct_QueuedWithdrawal {
                            IStrategy[] strategies;
                            uint256[] shares;
                            address staker;
                            DeprecatedStruct_WithdrawerAndNonce withdrawerAndNonce;
                            uint32 withdrawalStartBlock;
                            address delegatedAddress;
                        }
                        function migrateQueuedWithdrawal(
                            DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal
                        ) external returns (bool, bytes32);
                        function calculateWithdrawalRoot(
                            DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal
                        ) external pure returns (bytes32);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity =0.8.19;
                    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 BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT = 3;
                        uint256 internal constant BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT = 4;
                        uint256 internal constant BEACON_STATE_FIELD_TREE_HEIGHT = 5;
                        uint256 internal constant VALIDATOR_FIELD_TREE_HEIGHT = 3;
                        //Note: changed in the deneb hard fork from 4->5
                        uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB = 5;
                        uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA = 4;
                        // SLOTS_PER_HISTORICAL_ROOT = 2**13, so tree height is 13
                        uint256 internal constant BLOCK_ROOTS_TREE_HEIGHT = 13;
                        //HISTORICAL_ROOTS_LIMIT = 2**24, so tree height is 24
                        uint256 internal constant HISTORICAL_SUMMARIES_TREE_HEIGHT = 24;
                        //Index of block_summary_root in historical_summary container
                        uint256 internal constant BLOCK_SUMMARY_ROOT_INDEX = 0;
                        // tree height for hash tree of an individual withdrawal container
                        uint256 internal constant WITHDRAWAL_FIELD_TREE_HEIGHT = 2;
                        uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;
                        // MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
                        uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
                        //in beacon block body https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconblockbody
                        uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
                        // in beacon block header https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
                        uint256 internal constant SLOT_INDEX = 0;
                        uint256 internal constant STATE_ROOT_INDEX = 3;
                        uint256 internal constant BODY_ROOT_INDEX = 4;
                        // in beacon state https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
                        uint256 internal constant VALIDATOR_TREE_ROOT_INDEX = 11;
                        uint256 internal constant HISTORICAL_SUMMARIES_INDEX = 27;
                        // in validator https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                        uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
                        uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
                        uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
                        uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;
                        // in execution payload header
                        uint256 internal constant TIMESTAMP_INDEX = 9;
                        //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;
                        //Misc Constants
                        /// @notice The number of slots each epoch in the beacon chain
                        uint64 internal constant SLOTS_PER_EPOCH = 32;
                        /// @notice The number of seconds in a slot in the beacon chain
                        uint64 internal constant SECONDS_PER_SLOT = 12;
                        /// @notice Number of seconds per epoch: 384 == 32 slots/epoch * 12 seconds/slot
                        uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;
                        bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
                        /// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal
                        struct WithdrawalProof {
                            bytes withdrawalProof;
                            bytes slotProof;
                            bytes executionPayloadProof;
                            bytes timestampProof;
                            bytes historicalSummaryBlockRootProof;
                            uint64 blockRootIndex;
                            uint64 historicalSummaryIndex;
                            uint64 withdrawalIndex;
                            bytes32 blockRoot;
                            bytes32 slotRoot;
                            bytes32 timestampRoot;
                            bytes32 executionPayloadRoot;
                        }
                        /// @notice This struct contains the root and proof for verifying the state root against the oracle block root
                        struct StateRootProof {
                            bytes32 beaconStateRoot;
                            bytes proof;
                        }
                        /**
                         * @notice This function verifies merkle proofs of the fields of a certain validator against a beacon chain state root
                         * @param validatorIndex the index of the proven validator
                         * @param beaconStateRoot is the beacon chain state root to be proven against.
                         * @param validatorFieldsProof is the data used in proving the validator's fields
                         * @param validatorFields the claimed fields of the validator
                         */
                        function verifyValidatorFields(
                            bytes32 beaconStateRoot,
                            bytes32[] calldata validatorFields,
                            bytes calldata validatorFieldsProof,
                            uint40 validatorIndex
                        ) internal view {
                            require(
                                validatorFields.length == 2 ** VALIDATOR_FIELD_TREE_HEIGHT,
                                "BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"
                            );
                            /**
                             * Note: the length of the validator merkle proof is BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1.
                             * There is an additional layer added by hashing the root with the length of the validator list
                             */
                            require(
                                validatorFieldsProof.length ==
                                    32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyValidatorFields: Proof has incorrect length"
                            );
                            uint256 index = (VALIDATOR_TREE_ROOT_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) |
                                uint256(validatorIndex);
                            // merkleize the validatorFields to get the leaf to prove
                            bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields);
                            // verify the proof of the validatorRoot against the beaconStateRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: validatorFieldsProof,
                                    root: beaconStateRoot,
                                    leaf: validatorRoot,
                                    index: index
                                }),
                                "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof"
                            );
                        }
                        /**
                         * @notice This function verifies the latestBlockHeader against the state root. the latestBlockHeader is
                         * a tracked in the beacon state.
                         * @param beaconStateRoot is the beacon chain state root to be proven against.
                         * @param stateRootProof is the provided merkle proof
                         * @param latestBlockRoot is hashtree root of the latest block header in the beacon state
                         */
                        function verifyStateRootAgainstLatestBlockRoot(
                            bytes32 latestBlockRoot,
                            bytes32 beaconStateRoot,
                            bytes calldata stateRootProof
                        ) internal view {
                            require(
                                stateRootProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Proof has incorrect length"
                            );
                            //Next we verify the slot against the blockRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: stateRootProof,
                                    root: latestBlockRoot,
                                    leaf: beaconStateRoot,
                                    index: STATE_ROOT_INDEX
                                }),
                                "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Invalid latest block header root merkle proof"
                            );
                        }
                        /**
                         * @notice This function verifies the slot and the withdrawal fields for a given withdrawal
                         * @param withdrawalProof is the provided set of merkle proofs
                         * @param withdrawalFields is the serialized withdrawal container to be proven
                         */
                        function verifyWithdrawal(
                            bytes32 beaconStateRoot,
                            bytes32[] calldata withdrawalFields,
                            WithdrawalProof calldata withdrawalProof,
                            uint64 denebForkTimestamp
                        ) internal view {
                            require(
                                withdrawalFields.length == 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length"
                            );
                            require(
                                withdrawalProof.blockRootIndex < 2 ** BLOCK_ROOTS_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: blockRootIndex is too large"
                            );
                            require(
                                withdrawalProof.withdrawalIndex < 2 ** WITHDRAWALS_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
                            );
                            require(
                                withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
                            );
                            //Note: post deneb hard fork, the number of exection payload header fields increased from 15->17, adding an extra level to the tree height
                            uint256 executionPayloadHeaderFieldTreeHeight = (getWithdrawalTimestamp(withdrawalProof) <
                                denebForkTimestamp)
                                ? EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA
                                : EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB;
                            require(
                                withdrawalProof.withdrawalProof.length ==
                                    32 * (executionPayloadHeaderFieldTreeHeight + WITHDRAWALS_TREE_HEIGHT + 1),
                                "BeaconChainProofs.verifyWithdrawal: withdrawalProof has incorrect length"
                            );
                            require(
                                withdrawalProof.executionPayloadProof.length ==
                                    32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyWithdrawal: executionPayloadProof has incorrect length"
                            );
                            require(
                                withdrawalProof.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyWithdrawal: slotProof has incorrect length"
                            );
                            require(
                                withdrawalProof.timestampProof.length == 32 * (executionPayloadHeaderFieldTreeHeight),
                                "BeaconChainProofs.verifyWithdrawal: timestampProof has incorrect length"
                            );
                            require(
                                withdrawalProof.historicalSummaryBlockRootProof.length ==
                                    32 *
                                        (BEACON_STATE_FIELD_TREE_HEIGHT +
                                            (HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) +
                                            1 +
                                            (BLOCK_ROOTS_TREE_HEIGHT)),
                                "BeaconChainProofs.verifyWithdrawal: historicalSummaryBlockRootProof has incorrect length"
                            );
                            /**
                             * Note: Here, the "1" in "1 + (BLOCK_ROOTS_TREE_HEIGHT)" signifies that extra step of choosing the "block_root_summary" within the individual
                             * "historical_summary". Everywhere else it signifies merkelize_with_mixin, where the length of an array is hashed with the root of the array,
                             * but not here.
                             */
                            uint256 historicalBlockHeaderIndex = (HISTORICAL_SUMMARIES_INDEX <<
                                ((HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) + 1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                (uint256(withdrawalProof.historicalSummaryIndex) << (1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                (BLOCK_SUMMARY_ROOT_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)) |
                                uint256(withdrawalProof.blockRootIndex);
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.historicalSummaryBlockRootProof,
                                    root: beaconStateRoot,
                                    leaf: withdrawalProof.blockRoot,
                                    index: historicalBlockHeaderIndex
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid historicalsummary merkle proof"
                            );
                            //Next we verify the slot against the blockRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.slotProof,
                                    root: withdrawalProof.blockRoot,
                                    leaf: withdrawalProof.slotRoot,
                                    index: SLOT_INDEX
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid slot merkle proof"
                            );
                            {
                                // Next we verify the executionPayloadRoot against the blockRoot
                                uint256 executionPayloadIndex = (BODY_ROOT_INDEX <<
                                    (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)) | EXECUTION_PAYLOAD_INDEX;
                                require(
                                    Merkle.verifyInclusionSha256({
                                        proof: withdrawalProof.executionPayloadProof,
                                        root: withdrawalProof.blockRoot,
                                        leaf: withdrawalProof.executionPayloadRoot,
                                        index: executionPayloadIndex
                                    }),
                                    "BeaconChainProofs.verifyWithdrawal: Invalid executionPayload merkle proof"
                                );
                            }
                            // Next we verify the timestampRoot against the executionPayload root
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.timestampProof,
                                    root: withdrawalProof.executionPayloadRoot,
                                    leaf: withdrawalProof.timestampRoot,
                                    index: TIMESTAMP_INDEX
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid timestamp merkle proof"
                            );
                            {
                                /**
                                 * Next we verify the withdrawal fields against the executionPayloadRoot:
                                 * First we compute the withdrawal_index, then we merkleize the
                                 * withdrawalFields container to calculate the withdrawalRoot.
                                 *
                                 * Note: Merkleization of the withdrawals root tree uses MerkleizeWithMixin, i.e., the length of the array is hashed with the root of
                                 * the array.  Thus we shift the WITHDRAWALS_INDEX over by WITHDRAWALS_TREE_HEIGHT + 1 and not just WITHDRAWALS_TREE_HEIGHT.
                                 */
                                uint256 withdrawalIndex = (WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1)) |
                                    uint256(withdrawalProof.withdrawalIndex);
                                bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
                                require(
                                    Merkle.verifyInclusionSha256({
                                        proof: withdrawalProof.withdrawalProof,
                                        root: withdrawalProof.executionPayloadRoot,
                                        leaf: withdrawalRoot,
                                        index: withdrawalIndex
                                    }),
                                    "BeaconChainProofs.verifyWithdrawal: Invalid withdrawal merkle proof"
                                );
                            }
                        }
                        /**
                         * @notice This function replicates the ssz hashing of a validator's pubkey, outlined below:
                         *  hh := ssz.NewHasher()
                         *  hh.PutBytes(validatorPubkey[:])
                         *  validatorPubkeyHash := hh.Hash()
                         *  hh.Reset()
                         */
                        function hashValidatorBLSPubkey(
                            bytes memory validatorPubkey
                        ) internal pure returns (bytes32 pubkeyHash) {
                            require(validatorPubkey.length == 48, "Input should be 48 bytes in length");
                            return sha256(abi.encodePacked(validatorPubkey, bytes16(0)));
                        }
                        /**
                         * @dev Retrieve the withdrawal timestamp
                         */
                        function getWithdrawalTimestamp(
                            WithdrawalProof memory withdrawalProof
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalProof.timestampRoot);
                        }
                        /**
                         * @dev Converts the withdrawal's slot to an epoch
                         */
                        function getWithdrawalEpoch(
                            WithdrawalProof memory withdrawalProof
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalProof.slotRoot) / SLOTS_PER_EPOCH;
                        }
                        /**
                         * Indices for validator fields (refer to consensus specs):
                         * 0: pubkey
                         * 1: withdrawal credentials
                         * 2: effective balance
                         * 3: slashed?
                         * 4: activation elligibility epoch
                         * 5: activation epoch
                         * 6: exit epoch
                         * 7: withdrawable epoch
                         */
                        /**
                         * @dev Retrieves a validator's pubkey hash
                         */
                        function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) {
                            return validatorFields[VALIDATOR_PUBKEY_INDEX];
                        }
                        function getWithdrawalCredentials(
                            bytes32[] memory validatorFields
                        ) internal pure returns (bytes32) {
                            return validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
                        }
                        /**
                         * @dev Retrieves a validator's effective balance (in gwei)
                         */
                        function getEffectiveBalanceGwei(
                            bytes32[] memory validatorFields
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
                        }
                        /**
                         * @dev Retrieves a validator's withdrawable epoch
                         */
                        function getWithdrawableEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
                        }
                        /**
                         * Indices for withdrawal fields (refer to consensus specs):
                         * 0: withdrawal index
                         * 1: validator index
                         * 2: execution address
                         * 3: withdrawal amount
                         */
                        /**
                         * @dev Retrieves a withdrawal's validator index
                         */
                        function getValidatorIndex(bytes32[] memory withdrawalFields) internal pure returns (uint40) {
                            return
                                uint40(
                                    Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_INDEX_INDEX])
                                );
                        }
                        /**
                         * @dev Retrieves a withdrawal's withdrawal amount (in gwei)
                         */
                        function getWithdrawalAmountGwei(
                            bytes32[] memory withdrawalFields
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    library Endian {
                        /**
                         * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
                         * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
                         * @return n The big endian-formatted uint64
                         * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
                         * through a right-shift/shr operation.
                         */
                        function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) {
                            // the number needs to be stored in little-endian encoding (ie in bytes 0-8)
                            n = uint64(uint256(lenum >> 192));
                            return
                                (n >> 56) |
                                ((0x00FF000000000000 & n) >> 40) |
                                ((0x0000FF0000000000 & n) >> 24) |
                                ((0x000000FF00000000 & n) >> 8) |
                                ((0x00000000FF000000 & n) << 8) |
                                ((0x0000000000FF0000 & n) << 24) |
                                ((0x000000000000FF00 & n) << 40) |
                                ((0x00000000000000FF & n) << 56);
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev These functions deal with verification of Merkle Tree proofs.
                     *
                     * The tree and the proofs can be generated using our
                     * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
                     * You will find a quickstart guide in the readme.
                     *
                     * WARNING: You should avoid using leaf values that are 64 bytes long prior to
                     * hashing, or use a hash function other than keccak256 for hashing leaves.
                     * This is because the concatenation of a sorted pair of internal nodes in
                     * the merkle tree could be reinterpreted as a leaf value.
                     * OpenZeppelin's JavaScript library generates merkle trees that are safe
                     * against this attack out of the box.
                     */
                    library 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 (uint256 i = 0; i < numNodesInLayer; i++) {
                                layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
                            }
                            //the next layer above has half as many nodes
                            numNodesInLayer /= 2;
                            //while we haven't computed the root
                            while (numNodesInLayer != 0) {
                                //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
                                for (uint256 i = 0; i < numNodesInLayer; i++) {
                                    layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
                                }
                                //the next layer above has half as many nodes
                                numNodesInLayer /= 2;
                            }
                            //the first node in the layer is the root
                            return layer[0];
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    /// @dev Error for 0x0 address inputs
                    error InvalidZeroInput();
                    /// @dev Error for already added items to a list
                    error AlreadyAdded();
                    /// @dev Error for not found items in a list
                    error NotFound();
                    /// @dev Error for hitting max TVL
                    error MaxTVLReached();
                    /// @dev Error for caller not having permissions
                    error NotRestakeManagerAdmin();
                    /// @dev Error for call not coming from deposit queue contract
                    error NotDepositQueue();
                    /// @dev Error for contract being paused
                    error ContractPaused();
                    /// @dev Error for exceeding max basis points (100%)
                    error OverMaxBasisPoints();
                    /// @dev Error for invalid token decimals for collateral tokens (must be 18)
                    error InvalidTokenDecimals(uint8 expected, uint8 actual);
                    /// @dev Error when withdraw is already completed
                    error WithdrawAlreadyCompleted();
                    /// @dev Error when a different address tries to complete withdraw
                    error NotOriginalWithdrawCaller(address expectedCaller);
                    /// @dev Error when caller does not have OD admin role
                    error NotOperatorDelegatorAdmin();
                    /// @dev Error when caller does not have Oracle Admin role
                    error NotOracleAdmin();
                    /// @dev Error when caller is not RestakeManager contract
                    error NotRestakeManager();
                    /// @dev Errror when caller does not have ETH Restake Admin role
                    error NotNativeEthRestakeAdmin();
                    /// @dev Error when delegation address was already set - cannot be set again
                    error DelegateAddressAlreadySet();
                    /// @dev Error when caller does not have ERC20 Rewards Admin role
                    error NotERC20RewardsAdmin();
                    /// @dev Error when sending ETH fails
                    error TransferFailed();
                    /// @dev Error when caller does not have ETH Minter Burner Admin role
                    error NotEzETHMinterBurner();
                    /// @dev Error when caller does not have Token Admin role
                    error NotTokenAdmin();
                    /// @dev Error when price oracle is not configured
                    error OracleNotFound();
                    /// @dev Error when price oracle data is stale
                    error OraclePriceExpired();
                    /// @dev Error when array lengths do not match
                    error MismatchedArrayLengths();
                    /// @dev Error when caller does not have Deposit Withdraw Pauser role
                    error NotDepositWithdrawPauser();
                    /// @dev Error when an individual token TVL is over the max
                    error MaxTokenTVLReached();
                    /// @dev Error when Oracle price is invalid
                    error InvalidOraclePrice();
                    /// @dev Error when calling an invalid function
                    error NotImplemented();
                    /// @dev Error when calculating token amounts is invalid
                    error InvalidTokenAmount();
                    /// @dev Error when timestamp is invalid - likely in the past
                    error InvalidTimestamp(uint256 timestamp);
                    /// @dev Error when trade does not meet minimum output amount
                    error InsufficientOutputAmount();
                    /// @dev Error when the token received over the bridge is not the one expected
                    error InvalidTokenReceived();
                    /// @dev Error when the origin address is not whitelisted
                    error InvalidOrigin();
                    /// @dev Error when the sender is not expected
                    error InvalidSender(address expectedSender, address actualSender);
                    /// @dev error when function returns 0 amount
                    error InvalidZeroOutput();
                    /// @dev error when xRenzoBridge does not have enough balance to pay for fee
                    error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees);
                    /// @dev error when source chain is not expected
                    error InvalidSourceChain(uint64 expectedCCIPChainSelector, uint64 actualCCIPChainSelector);
                    /// @dev Error when an unauthorized address tries to call the bridge function on the L2
                    error UnauthorizedBridgeSweeper();
                    /// @dev Error when caller does not have BRIDGE_ADMIN role
                    error NotBridgeAdmin();
                    /// @dev Error when caller does not have PRICE_FEED_SENDER role
                    error NotPriceFeedSender();
                    /// @dev Error for connext price Feed unauthorised call
                    error UnAuthorisedCall();
                    /// @dev Error for no price feed configured on L2
                    error PriceFeedNotAvailable();
                    /// @dev Error for invalid bridge fee share configuration
                    error InvalidBridgeFeeShare(uint256 bridgeFee);
                    /// @dev Error for invalid sweep batch size
                    error InvalidSweepBatchSize(uint256 batchSize);
                    /// @dev Error when caller does not have Withdraw Queue admin role
                    error NotWithdrawQueueAdmin();
                    /// @dev Error when caller try to withdraw more than Buffer
                    error NotEnoughWithdrawBuffer();
                    /// @dev Error when caller try to claim withdraw before cooldown period
                    error EarlyClaim();
                    /// @dev Error when caller try to withdraw for unsupported asset
                    error UnsupportedWithdrawAsset();
                    /// @dev Error when caller try to claim invalidWithdrawIndex
                    error InvalidWithdrawIndex();
                    /// @dev Error when TVL was expected to be 0
                    error InvalidTVL();
                    /// @dev Error when incorrect BeaconChainStrategy is set for LST in completeQueuedWithdrawal
                    error IncorrectStrategy();
                    /// @dev Error when adding new OperatorDelegator which is not delegated
                    error OperatoDelegatorNotDelegated();
                    /// @dev Error when emergency tracking already tracked withdrawal
                    error WithdrawalAlreadyTracked();
                    /// @dev Error when emergency tracking already completed withdrawal
                    error WithdrawalAlreadyCompleted();
                    /// @dev Error when caller does not have Emergency Withdraw Tracking Admin role
                    error NotEmergencyWithdrawTrackingAdmin();
                    /// @dev Error when strategy does not have specified underlying
                    error InvalidStrategy();
                    /// @dev Error when strategy already set and hold non zero token balance
                    error NonZeroUnderlyingStrategyExist();
                    /// @dev Error when caller tried to claim queued withdrawal when not filled
                    error QueuedWithdrawalNotFilled();
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "./Delegation/IOperatorDelegator.sol";
                    import "./Deposits/IDepositQueue.sol";
                    interface IRestakeManager {
                        function stakeEthInOperatorDelegator(
                            IOperatorDelegator operatorDelegator,
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        function depositTokenRewardsFromProtocol(IERC20 _token, uint256 _amount) external;
                        function depositQueue() external view returns (IDepositQueue);
                        function calculateTVLs() external view returns (uint256[][] memory, uint256[] memory, uint256);
                        function depositETH() external payable;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    interface IRoleManager {
                        /// @dev Determines if the specified address has permissions to manage RoleManager
                        /// @param potentialAddress Address to check
                        function isRoleManagerAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to mint or burn ezETH tokens
                        /// @param potentialAddress Address to check
                        function isEzETHMinterBurner(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the OperatorDelgator Contracts
                        /// @param potentialAddress Address to check
                        function isOperatorDelegatorAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Oracle Contract config
                        /// @param potentialAddress Address to check
                        function isOracleAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Restake Manager
                        /// @param potentialAddress Address to check
                        function isRestakeManagerAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Token Contract
                        /// @param potentialAddress Address to check
                        function isTokenAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to trigger restaking of native ETH
                        /// @param potentialAddress Address to check
                        function isNativeEthRestakeAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to sweep and deposit ERC20 Rewards
                        /// @param potentialAddress Address to check
                        function isERC20RewardsAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to pause deposits and withdraws
                        /// @param potentialAddress Address to check
                        function isDepositWithdrawPauser(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to set whitelisted origin in xRenzoBridge
                        /// @param potentialAddress Address to check
                        function isBridgeAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determined if the specified address has permission to send price feed of ezETH to L2
                        /// @param potentialAddress Address to check
                        function isPriceFeedSender(address potentialAddress) external view returns (bool);
                        /// @dev Determine if the specified address haas permission to update Withdraw Queue params
                        /// @param potentialAddress Address to check
                        function isWithdrawQueueAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determine if the specified address has permission to track emergency pending queued withdrawals
                        /// @param potentialAddress Address to check
                        function isEmergencyWithdrawTrackingAdmin(
                            address potentialAddress
                        ) external view returns (bool);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    interface IWithdrawQueue {
                        /// @dev To get available value to withdraw from buffer
                        /// @param _asset address of token
                        function getAvailableToWithdraw(address _asset) external view returns (uint256);
                        /// @dev To get the withdraw buffer target of given asset
                        /// @param _asset address of token
                        function withdrawalBufferTarget(address _asset) external view returns (uint256);
                        /// @dev To get the current Target Buffer Deficit
                        /// @param _asset address of token
                        function getWithdrawDeficit(address _asset) external view returns (uint256);
                        /// @dev Fill ERC20 Withdraw Buffer
                        /// @param _asset the token address to fill the respective buffer
                        /// @param _amount  amount of token to fill with
                        function fillERC20WithdrawBuffer(address _asset, uint256 _amount) external;
                        /// @dev to get the withdrawRequests for particular user
                        /// @param _user address of the user
                        function withdrawRequests(address _user) external view returns (uint256[] memory);
                        /// @dev Fill ETH Withdraw buffer
                        function fillEthWithdrawBuffer() external payable;
                    }
                    

                    File 8 of 10: PaymentSplitter
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
                    pragma solidity ^0.8.0;
                    import "../utils/ContextUpgradeable.sol";
                    import {Initializable} from "../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. Can only be called by the current owner.
                         *
                         * NOTE: Renouncing ownership will leave the contract without an owner,
                         * thereby disabling any functionality that is only available to the owner.
                         */
                        function renounceOwnership() public virtual onlyOwner {
                            _transferOwnership(address(0));
                        }
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Can only be called by the current owner.
                         */
                        function transferOwnership(address newOwner) public virtual onlyOwner {
                            require(newOwner != address(0), "Ownable: new owner is the zero address");
                            _transferOwnership(newOwner);
                        }
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Internal function without access restriction.
                         */
                        function _transferOwnership(address newOwner) internal virtual {
                            address oldOwner = _owner;
                            _owner = newOwner;
                            emit OwnershipTransferred(oldOwner, newOwner);
                        }
                        /**
                         * @dev This empty reserved space is put in place to allow future versions to add new
                         * variables without shifting down storage in the inheritance chain.
                         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                         */
                        uint256[49] private __gap;
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.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) (security/ReentrancyGuard.sol)
                    pragma solidity ^0.8.0;
                    import {Initializable} from "../proxy/utils/Initializable.sol";
                    /**
                     * @dev Contract module that helps prevent reentrant calls to a function.
                     *
                     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                     * available, which can be applied to functions to make sure there are no nested
                     * (reentrant) calls to them.
                     *
                     * Note that because there is a single `nonReentrant` guard, functions marked as
                     * `nonReentrant` may not call one another. This can be worked around by making
                     * those functions `private`, and then adding `external` `nonReentrant` entry
                     * points to them.
                     *
                     * TIP: If you would like to learn more about reentrancy and alternative ways
                     * to protect against it, check out our blog post
                     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                     */
                    abstract contract ReentrancyGuardUpgradeable is Initializable {
                        // Booleans are more expensive than uint256 or any type that takes up a full
                        // word because each write operation emits an extra SLOAD to first read the
                        // slot's contents, replace the bits taken up by the boolean, and then write
                        // back. This is the compiler's defense against contract upgrades and
                        // pointer aliasing, and it cannot be disabled.
                        // The values being non-zero value makes deployment a bit more expensive,
                        // but in exchange the refund on every call to nonReentrant will be lower in
                        // amount. Since refunds are capped to a percentage of the total
                        // transaction's gas, it is best to keep them low in cases like this one, to
                        // increase the likelihood of the full refund coming into effect.
                        uint256 private constant _NOT_ENTERED = 1;
                        uint256 private constant _ENTERED = 2;
                        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: 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 (last updated v4.9.4) (utils/Context.sol)
                    pragma solidity ^0.8.0;
                    import {Initializable} from "../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;
                        }
                        function _contextSuffixLength() internal view virtual returns (uint256) {
                            return 0;
                        }
                        /**
                         * @dev This empty reserved space is put in place to allow future versions to add new
                         * variables without shifting down storage in the inheritance chain.
                         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                         */
                        uint256[50] private __gap;
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                     *
                     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                     * need to send a transaction, and thus is not required to hold Ether at all.
                     *
                     * ==== Security Considerations
                     *
                     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
                     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
                     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
                     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
                     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
                     * generally recommended is:
                     *
                     * ```solidity
                     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
                     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
                     *     doThing(..., value);
                     * }
                     *
                     * function doThing(..., uint256 value) public {
                     *     token.safeTransferFrom(msg.sender, address(this), value);
                     *     ...
                     * }
                     * ```
                     *
                     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
                     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
                     * {SafeERC20-safeTransferFrom}).
                     *
                     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
                     * contracts should have entry points that don't rely on permit.
                     */
                    interface IERC20Permit {
                        /**
                         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                         * given ``owner``'s signed approval.
                         *
                         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                         * ordering also apply here.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `deadline` must be a timestamp in the future.
                         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                         * over the EIP712-formatted function arguments.
                         * - the signature must use ``owner``'s current nonce (see {nonces}).
                         *
                         * For more information on the signature format, see the
                         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                         * section].
                         *
                         * CAUTION: See Security Considerations above.
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external;
                        /**
                         * @dev Returns the current nonce for `owner`. This value must be
                         * included whenever a signature is generated for {permit}.
                         *
                         * Every successful call to {permit} increases ``owner``'s nonce by one. This
                         * prevents a signature from being used multiple times.
                         */
                        function nonces(address owner) external view returns (uint256);
                        /**
                         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                         */
                        // solhint-disable-next-line func-name-mixedcase
                        function DOMAIN_SEPARATOR() external view returns (bytes32);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.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 (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                    pragma solidity ^0.8.0;
                    import "../IERC20.sol";
                    import "../extensions/IERC20Permit.sol";
                    import "../../../utils/Address.sol";
                    /**
                     * @title SafeERC20
                     * @dev Wrappers around ERC20 operations that throw on failure (when the token
                     * contract returns false). Tokens that return no value (and instead revert or
                     * throw on failure) are also supported, non-reverting calls are assumed to be
                     * successful.
                     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                     */
                    library SafeERC20 {
                        using Address for address;
                        /**
                         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeTransfer(IERC20 token, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                        }
                        /**
                         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                         */
                        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                        }
                        /**
                         * @dev Deprecated. This function has issues similar to the ones found in
                         * {IERC20-approve}, and its usage is discouraged.
                         *
                         * Whenever possible, use {safeIncreaseAllowance} and
                         * {safeDecreaseAllowance} instead.
                         */
                        function safeApprove(IERC20 token, address spender, uint256 value) internal {
                            // safeApprove should only be called when setting an initial allowance,
                            // or when resetting it to zero. To increase and decrease it, use
                            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                            require(
                                (value == 0) || (token.allowance(address(this), spender) == 0),
                                "SafeERC20: approve from non-zero to non-zero allowance"
                            );
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                        }
                        /**
                         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                        }
                        /**
                         * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            unchecked {
                                uint256 oldAllowance = token.allowance(address(this), spender);
                                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                            }
                        }
                        /**
                         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                         * to be set to zero before setting it to a non-zero value, such as USDT.
                         */
                        function forceApprove(IERC20 token, address spender, uint256 value) internal {
                            bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                            if (!_callOptionalReturnBool(token, approvalCall)) {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                                _callOptionalReturn(token, approvalCall);
                            }
                        }
                        /**
                         * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                         * Revert on invalid signature.
                         */
                        function safePermit(
                            IERC20Permit token,
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            uint256 nonceBefore = token.nonces(owner);
                            token.permit(owner, spender, value, deadline, v, r, s);
                            uint256 nonceAfter = token.nonces(owner);
                            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         */
                        function _callOptionalReturn(IERC20 token, bytes memory data) private {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                            // the target address contains contract code and also asserts for success in the low-level call.
                            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                            require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         *
                         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                         */
                        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                            // and not revert is the subcall reverts.
                            (bool success, bytes memory returndata) = address(token).call(data);
                            return
                                success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                        }
                    }
                    // 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: BUSL-1.1
                    pragma solidity 0.8.19;
                    /// @dev Error for 0x0 address inputs
                    error InvalidZeroInput();
                    /// @dev Error for already added items to a list
                    error AlreadyAdded();
                    /// @dev Error for not found items in a list
                    error NotFound();
                    /// @dev Error for hitting max TVL
                    error MaxTVLReached();
                    /// @dev Error for caller not having permissions
                    error NotRestakeManagerAdmin();
                    /// @dev Error for call not coming from deposit queue contract
                    error NotDepositQueue();
                    /// @dev Error for contract being paused
                    error ContractPaused();
                    /// @dev Error for exceeding max basis points (100%)
                    error OverMaxBasisPoints();
                    /// @dev Error for invalid token decimals for collateral tokens (must be 18)
                    error InvalidTokenDecimals(uint8 expected, uint8 actual);
                    /// @dev Error when withdraw is already completed
                    error WithdrawAlreadyCompleted();
                    /// @dev Error when a different address tries to complete withdraw
                    error NotOriginalWithdrawCaller(address expectedCaller);
                    /// @dev Error when caller does not have OD admin role
                    error NotOperatorDelegatorAdmin();
                    /// @dev Error when caller does not have Oracle Admin role
                    error NotOracleAdmin();
                    /// @dev Error when caller is not RestakeManager contract
                    error NotRestakeManager();
                    /// @dev Errror when caller does not have ETH Restake Admin role
                    error NotNativeEthRestakeAdmin();
                    /// @dev Error when delegation address was already set - cannot be set again
                    error DelegateAddressAlreadySet();
                    /// @dev Error when caller does not have ERC20 Rewards Admin role
                    error NotERC20RewardsAdmin();
                    /// @dev Error when sending ETH fails
                    error TransferFailed();
                    /// @dev Error when caller does not have ETH Minter Burner Admin role
                    error NotEzETHMinterBurner();
                    /// @dev Error when caller does not have Token Admin role
                    error NotTokenAdmin();
                    /// @dev Error when price oracle is not configured
                    error OracleNotFound();
                    /// @dev Error when price oracle data is stale
                    error OraclePriceExpired();
                    /// @dev Error when array lengths do not match
                    error MismatchedArrayLengths();
                    /// @dev Error when caller does not have Deposit Withdraw Pauser role
                    error NotDepositWithdrawPauser();
                    /// @dev Error when an individual token TVL is over the max
                    error MaxTokenTVLReached();
                    /// @dev Error when Oracle price is invalid
                    error InvalidOraclePrice();
                    /// @dev Error when calling an invalid function
                    error NotImplemented();
                    /// @dev Error when calculating token amounts is invalid
                    error InvalidTokenAmount();
                    /// @dev Error when timestamp is invalid - likely in the past
                    error InvalidTimestamp(uint256 timestamp);
                    /// @dev Error when trade does not meet minimum output amount
                    error InsufficientOutputAmount();
                    /// @dev Error when the token received over the bridge is not the one expected
                    error InvalidTokenReceived();
                    /// @dev Error when the origin address is not whitelisted
                    error InvalidOrigin();
                    /// @dev Error when the sender is not expected
                    error InvalidSender(address expectedSender, address actualSender);
                    /// @dev error when function returns 0 amount
                    error InvalidZeroOutput();
                    /// @dev error when xRenzoBridge does not have enough balance to pay for fee
                    error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees);
                    /// @dev error when source chain is not expected
                    error InvalidSourceChain(uint64 expectedCCIPChainSelector, uint64 actualCCIPChainSelector);
                    /// @dev Error when an unauthorized address tries to call the bridge function on the L2
                    error UnauthorizedBridgeSweeper();
                    /// @dev Error when caller does not have BRIDGE_ADMIN role
                    error NotBridgeAdmin();
                    /// @dev Error when caller does not have PRICE_FEED_SENDER role
                    error NotPriceFeedSender();
                    /// @dev Error for connext price Feed unauthorised call
                    error UnAuthorisedCall();
                    /// @dev Error for no price feed configured on L2
                    error PriceFeedNotAvailable();
                    /// @dev Error for invalid bridge fee share configuration
                    error InvalidBridgeFeeShare(uint256 bridgeFee);
                    /// @dev Error for invalid sweep batch size
                    error InvalidSweepBatchSize(uint256 batchSize);
                    /// @dev Error when caller does not have Withdraw Queue admin role
                    error NotWithdrawQueueAdmin();
                    /// @dev Error when caller try to withdraw more than Buffer
                    error NotEnoughWithdrawBuffer();
                    /// @dev Error when caller try to claim withdraw before cooldown period
                    error EarlyClaim();
                    /// @dev Error when caller try to withdraw for unsupported asset
                    error UnsupportedWithdrawAsset();
                    /// @dev Error when caller try to claim invalidWithdrawIndex
                    error InvalidWithdrawIndex();
                    /// @dev Error when TVL was expected to be 0
                    error InvalidTVL();
                    /// @dev Error when incorrect BeaconChainStrategy is set for LST in completeQueuedWithdrawal
                    error IncorrectStrategy();
                    /// @dev Error when adding new OperatorDelegator which is not delegated
                    error OperatoDelegatorNotDelegated();
                    /// @dev Error when emergency tracking already tracked withdrawal
                    error WithdrawalAlreadyTracked();
                    /// @dev Error when emergency tracking already completed withdrawal
                    error WithdrawalAlreadyCompleted();
                    /// @dev Error when caller does not have Emergency Withdraw Tracking Admin role
                    error NotEmergencyWithdrawTrackingAdmin();
                    /// @dev Error when strategy does not have specified underlying
                    error InvalidStrategy();
                    /// @dev Error when strategy already set and hold non zero token balance
                    error NonZeroUnderlyingStrategyExist();
                    /// @dev Error when caller tried to claim queued withdrawal when not filled
                    error QueuedWithdrawalNotFilled();
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
                    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                    import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
                    import "./PaymentSplitterStorage.sol";
                    import "../Errors/Errors.sol";
                    /**
                     * @author  Renzo Protocol
                     * @title   PaymentSplitter
                     * @dev     Handles native ETH payments to be split among recipients.
                     *          A list of payment addresses and their corresponding amount to be paid out are tracked.
                     *          As ETH payments come in, they are split among the recipients until the amount to be paid is completed.
                     *          After all recipients are paid, any new ETH is sent to the fallback address.
                     *          ERC20 tokens are simply forwarded to the fallback address and can be triggered by any address.
                     * @notice  .
                     */
                    contract PaymentSplitter is
                        Initializable,
                        OwnableUpgradeable,
                        ReentrancyGuardUpgradeable,
                        PaymentSplitterStorageV1
                    {
                        using SafeERC20 for IERC20;
                        event RecipientAdded(address recipient, uint256 amountOwed);
                        event RecipientRemoved(address recipient, uint256 amountOwed);
                        event RecipientAmountIncreased(address recipient, uint256 amountOwed, uint256 increaseAmount);
                        event RecipientAmountDecreased(address recipient, uint256 amountOwed, uint256 decreaseAmount);
                        event RecipientPaid(address recipient, uint256 amountPaid, bool success);
                        uint256 private constant DUST_AMOUNT = 1_000_000 gwei;
                        /// @dev Prevents implementation contract from being initialized.
                        /// @custom:oz-upgrades-unsafe-allow constructor
                        constructor() {
                            _disableInitializers();
                        }
                        /**
                         * @notice  Initializes the contract with initial vars
                         * @dev     Contract init
                         * @param   _fallbackPaymentAddress  The address where all funds will be sent after recipients are fully paid
                         */
                        function initialize(address _fallbackPaymentAddress) public initializer {
                            // Initialize inherited classes
                            __Ownable_init();
                            __ReentrancyGuard_init();
                            fallbackPaymentAddress = _fallbackPaymentAddress;
                        }
                        /**
                         * @notice  Recipient Length
                         * @dev     view function for getting recipient length
                         * @return  uint256  .
                         */
                        function getRecipientLength() public view returns (uint256) {
                            return recipients.length;
                        }
                        /**
                         * @notice  Forwards all ERC20 tokens to the fallback address
                         * @dev     Can be called by any address
                         *          If specified token balance is zero, reverts
                         *          Note that this is just a convenience function to handle any ERC20 tokens accidentally sent to this address,
                         *              and this is not expected to be used in normal operation
                         * @param   token  IERC20 that was sent to this contract
                         */
                        function forwardERC20(IERC20 token) public {
                            uint256 balance = token.balanceOf(address(this));
                            if (balance == 0) {
                                revert InvalidTokenReceived();
                            }
                            token.safeTransfer(fallbackPaymentAddress, balance);
                        }
                        /**
                         * @notice  Adds a recipient and amount owed to the list
                         * @dev     Only callable by the owner
                         *          Any new payments coming in should start to be forwarded to the new recipient after this call
                         *          Cannot add the same recipient twice
                         * @param   _recipient  Recipient address to add
                         * @param   _initialAmountOwed  Initial amount owed to the recipient - can be set to 0
                         */
                        function addRecipient(address _recipient, uint256 _initialAmountOwed) public onlyOwner {
                            // First iterate over the list to check to ensure the recipient is not already in the list
                            for (uint256 i = 0; i < recipients.length; i++) {
                                if (recipients[i] == _recipient) {
                                    revert AlreadyAdded();
                                }
                            }
                            // Push to the list and set the amount owed
                            recipients.push(_recipient);
                            amountOwed[_recipient] = _initialAmountOwed;
                            // Emit the event
                            emit RecipientAdded(_recipient, _initialAmountOwed);
                        }
                        /**
                         * @notice  Removes a recipient from the list of payout addresses
                         * @dev     Only callable by the owner
                         *          Any new payments coming after this will not get forwarded to this recipient
                         *          If the recipient is not in the list, reverts
                         * @param   _recipient  Recipient address to remove
                         */
                        function removeRecipient(address _recipient) public onlyOwner {
                            // First iterate over the list to check to ensure the recipient is in the list
                            for (uint256 i = 0; i < recipients.length; i++) {
                                if (recipients[i] == _recipient) {
                                    emit RecipientRemoved(_recipient, amountOwed[_recipient]);
                                    // Remove the recipient from the list
                                    recipients[i] = recipients[recipients.length - 1];
                                    recipients.pop();
                                    delete amountOwed[_recipient];
                                    return;
                                }
                            }
                            revert NotFound();
                        }
                        /**
                         * @notice  Increases the amount owed to a recipient
                         * @dev     Only callable by the owner
                         *          If the recipient is not in the list, reverts
                         * @param   _recipient  Recipient address to increase the amount owed
                         * @param   _amount  Amount to add to outstanding balance owed
                         */
                        function addToRecipientAmountOwed(address _recipient, uint256 _amount) public onlyOwner {
                            // Iterate over the recipient list to find the recipient
                            for (uint256 i = 0; i < recipients.length; i++) {
                                if (recipients[i] == _recipient) {
                                    amountOwed[_recipient] += _amount;
                                    // Emit event
                                    emit RecipientAmountIncreased(_recipient, amountOwed[_recipient], _amount);
                                    return;
                                }
                            }
                            revert NotFound();
                        }
                        /**
                         * @notice  Decreases the amount owed to a recipient
                         * @dev     Only callable by the owner
                         *          If the recipient is not in the list, reverts
                         *          If the amount to decrease is greater than the amount owed, sets amount owed to 0
                         * @param   _recipient  Recipient address to decrease the amount owed
                         * @param   _amount  Amount to subtract from the outstanding balance owed
                         */
                        function subtractFromRecipientAmountOwed(address _recipient, uint256 _amount) public onlyOwner {
                            // Iterate over the recipient list to find the recipient
                            for (uint256 i = 0; i < recipients.length; i++) {
                                if (recipients[i] == _recipient) {
                                    // Check for higher amount to decrease than amount owed
                                    if (_amount >= amountOwed[_recipient]) {
                                        // Just set to 0 if the amount to decrease is greater than or equal to the amount owed
                                        amountOwed[_recipient] = 0;
                                    } else {
                                        // Subtract the amount from the amount owed
                                        amountOwed[_recipient] -= _amount;
                                    }
                                    // Emit event
                                    emit RecipientAmountDecreased(_recipient, amountOwed[_recipient], _amount);
                                    return;
                                }
                            }
                            revert NotFound();
                        }
                        /**
                         * @notice  Pay out the recipients when ETH comes in
                         * @dev     Any new payments coming in will be split among the recipients
                         *          Allows dust to be sent to recipients, but not to fallback address
                         *          Non Reentrant
                         */
                        receive() external payable nonReentrant {
                            // Always use the balance of the address in case there was a rounding error or leftover amount from the last payout
                            uint256 amountLeftToPay = address(this).balance;
                            if (amountLeftToPay == 0) {
                                return;
                            }
                            // Iterate over the recipients and pay them out
                            for (uint256 i = 0; i < recipients.length; i++) {
                                // First get the amount to pay this recipient based on the number of payment addresses left in the list
                                uint256 amountToPay = amountLeftToPay / (recipients.length - i);
                                // Check if the amount owed is less than the amount to pay
                                if (amountOwed[recipients[i]] < amountToPay) {
                                    amountToPay = amountOwed[recipients[i]];
                                }
                                // Continue to the next one if the amount to pay is zero
                                if (amountToPay == 0) {
                                    continue;
                                }
                                // Send the funds but ignore the return value to prevent others from not being paid
                                (bool success, ) = recipients[i].call{ value: amountToPay }("");
                                // If successful update the amount owed and the amount left to pay
                                if (success) {
                                    // Subtract the amount sent to the amount owed
                                    amountOwed[recipients[i]] -= amountToPay;
                                    // Subtract the amount sent from the total amount left to pay to other addresses
                                    amountLeftToPay -= amountToPay;
                                    // Track the total paid out to this recipient
                                    totalAmountPaid[recipients[i]] += amountToPay;
                                }
                                // Emit event
                                emit RecipientPaid(recipients[i], amountToPay, success);
                            }
                            // If there is any amount left to pay, send it to the fallback address
                            // ignore dust amounts due to division rounding or small left over amounts - they will get sent the next time this function is called
                            if (amountLeftToPay > DUST_AMOUNT) {
                                // Send the funds but ignore the return value to prevent others from not being paid
                                (bool success, ) = fallbackPaymentAddress.call{ value: amountLeftToPay }("");
                                // If success, track the amount paid to the fallback
                                if (success) {
                                    totalAmountPaid[fallbackPaymentAddress] += amountLeftToPay;
                                }
                                // Emit event
                                emit RecipientPaid(fallbackPaymentAddress, amountLeftToPay, success);
                            }
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    abstract contract PaymentSplitterStorageV1 {
                        /// @dev the address to send funds once recipients are paid
                        address public fallbackPaymentAddress;
                        /// @dev tracks the total amount paid out to specific addresses
                        mapping(address => uint256) public totalAmountPaid;
                        /// @dev tracks the amount owed to specific addresses
                        mapping(address => uint256) public amountOwed;
                        /// @dev list of addresses to pay out for iteration
                        address[] public recipients;
                    }
                    

                    File 9 of 10: WithdrawQueue
                    // 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.7.0) (security/Pausable.sol)
                    pragma solidity ^0.8.0;
                    import "../utils/ContextUpgradeable.sol";
                    import {Initializable} from "../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 {Initializable} from "../proxy/utils/Initializable.sol";
                    /**
                     * @dev Contract module that helps prevent reentrant calls to a function.
                     *
                     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                     * available, which can be applied to functions to make sure there are no nested
                     * (reentrant) calls to them.
                     *
                     * Note that because there is a single `nonReentrant` guard, functions marked as
                     * `nonReentrant` may not call one another. This can be worked around by making
                     * those functions `private`, and then adding `external` `nonReentrant` entry
                     * points to them.
                     *
                     * TIP: If you would like to learn more about reentrancy and alternative ways
                     * to protect against it, check out our blog post
                     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                     */
                    abstract contract ReentrancyGuardUpgradeable is Initializable {
                        // Booleans are more expensive than uint256 or any type that takes up a full
                        // word because each write operation emits an extra SLOAD to first read the
                        // slot's contents, replace the bits taken up by the boolean, and then write
                        // back. This is the compiler's defense against contract upgrades and
                        // pointer aliasing, and it cannot be disabled.
                        // The values being non-zero value makes deployment a bit more expensive,
                        // but in exchange the refund on every call to nonReentrant will be lower in
                        // amount. Since refunds are capped to a percentage of the total
                        // transaction's gas, it is best to keep them low in cases like this one, to
                        // increase the likelihood of the full refund coming into effect.
                        uint256 private constant _NOT_ENTERED = 1;
                        uint256 private constant _ENTERED = 2;
                        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: 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 IERC20Upgradeable {
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to {approve}. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `to`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address to, uint256 amount) external returns (bool);
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through {transferFrom}. This is
                         * zero by default.
                         *
                         * This value changes when {approve} or {transferFrom} are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * IMPORTANT: Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an {Approval} event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                        /**
                         * @dev Moves `amount` tokens from `from` to `to` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transferFrom(address from, address to, uint256 amount) external returns (bool);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.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 (last updated v4.9.4) (utils/Context.sol)
                    pragma solidity ^0.8.0;
                    import {Initializable} from "../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;
                        }
                        function _contextSuffixLength() internal view virtual returns (uint256) {
                            return 0;
                        }
                        /**
                         * @dev This empty reserved space is put in place to allow future versions to add new
                         * variables without shifting down storage in the inheritance chain.
                         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                         */
                        uint256[50] private __gap;
                    }
                    // SPDX-License-Identifier: 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.4) (token/ERC20/extensions/IERC20Permit.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                     *
                     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                     * need to send a transaction, and thus is not required to hold Ether at all.
                     *
                     * ==== Security Considerations
                     *
                     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
                     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
                     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
                     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
                     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
                     * generally recommended is:
                     *
                     * ```solidity
                     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
                     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
                     *     doThing(..., value);
                     * }
                     *
                     * function doThing(..., uint256 value) public {
                     *     token.safeTransferFrom(msg.sender, address(this), value);
                     *     ...
                     * }
                     * ```
                     *
                     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
                     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
                     * {SafeERC20-safeTransferFrom}).
                     *
                     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
                     * contracts should have entry points that don't rely on permit.
                     */
                    interface IERC20Permit {
                        /**
                         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                         * given ``owner``'s signed approval.
                         *
                         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                         * ordering also apply here.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `deadline` must be a timestamp in the future.
                         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                         * over the EIP712-formatted function arguments.
                         * - the signature must use ``owner``'s current nonce (see {nonces}).
                         *
                         * For more information on the signature format, see the
                         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                         * section].
                         *
                         * CAUTION: See Security Considerations above.
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external;
                        /**
                         * @dev Returns the current nonce for `owner`. This value must be
                         * included whenever a signature is generated for {permit}.
                         *
                         * Every successful call to {permit} increases ``owner``'s nonce by one. This
                         * prevents a signature from being used multiple times.
                         */
                        function nonces(address owner) external view returns (uint256);
                        /**
                         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                         */
                        // solhint-disable-next-line func-name-mixedcase
                        function DOMAIN_SEPARATOR() external view returns (bytes32);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.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 (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                    pragma solidity ^0.8.0;
                    import "../IERC20.sol";
                    import "../extensions/IERC20Permit.sol";
                    import "../../../utils/Address.sol";
                    /**
                     * @title SafeERC20
                     * @dev Wrappers around ERC20 operations that throw on failure (when the token
                     * contract returns false). Tokens that return no value (and instead revert or
                     * throw on failure) are also supported, non-reverting calls are assumed to be
                     * successful.
                     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                     */
                    library SafeERC20 {
                        using Address for address;
                        /**
                         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeTransfer(IERC20 token, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                        }
                        /**
                         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                         */
                        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                        }
                        /**
                         * @dev Deprecated. This function has issues similar to the ones found in
                         * {IERC20-approve}, and its usage is discouraged.
                         *
                         * Whenever possible, use {safeIncreaseAllowance} and
                         * {safeDecreaseAllowance} instead.
                         */
                        function safeApprove(IERC20 token, address spender, uint256 value) internal {
                            // safeApprove should only be called when setting an initial allowance,
                            // or when resetting it to zero. To increase and decrease it, use
                            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                            require(
                                (value == 0) || (token.allowance(address(this), spender) == 0),
                                "SafeERC20: approve from non-zero to non-zero allowance"
                            );
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                        }
                        /**
                         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                        }
                        /**
                         * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful.
                         */
                        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                            unchecked {
                                uint256 oldAllowance = token.allowance(address(this), spender);
                                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                            }
                        }
                        /**
                         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                         * to be set to zero before setting it to a non-zero value, such as USDT.
                         */
                        function forceApprove(IERC20 token, address spender, uint256 value) internal {
                            bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                            if (!_callOptionalReturnBool(token, approvalCall)) {
                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                                _callOptionalReturn(token, approvalCall);
                            }
                        }
                        /**
                         * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                         * Revert on invalid signature.
                         */
                        function safePermit(
                            IERC20Permit token,
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            uint256 nonceBefore = token.nonces(owner);
                            token.permit(owner, spender, value, deadline, v, r, s);
                            uint256 nonceAfter = token.nonces(owner);
                            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         */
                        function _callOptionalReturn(IERC20 token, bytes memory data) private {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                            // the target address contains contract code and also asserts for success in the low-level call.
                            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                            require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         *
                         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                         */
                        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                            // and not revert is the subcall reverts.
                            (bool success, bytes memory returndata) = address(token).call(data);
                            return
                                success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                        }
                    }
                    // 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: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "../EigenLayer/interfaces/IStrategyManager.sol";
                    import "../EigenLayer/interfaces/IDelegationManager.sol";
                    import "../EigenLayer/interfaces/IEigenPod.sol";
                    interface IOperatorDelegator {
                        function getTokenBalanceFromStrategy(IERC20 token) external view returns (uint256);
                        function deposit(IERC20 _token, uint256 _tokenAmount) external returns (uint256 shares);
                        // Note: Withdraws disabled for this release
                        // function startWithdrawal(IERC20 _token, uint256 _tokenAmount) external returns (bytes32);
                        // function completeWithdrawal(
                        //     IStrategyManager.DeprecatedStruct_QueuedWithdrawal calldata _withdrawal,
                        //     IERC20 _token,
                        //     uint256 _middlewareTimesIndex,
                        //     address _sendToAddress
                        // ) external;
                        function getStakedETHBalance() external view returns (uint256);
                        function stakeEth(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        function eigenPod() external view returns (IEigenPod);
                        function pendingUnstakedDelayedWithdrawalAmount() external view returns (uint256);
                        function delegateAddress() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "../Withdraw/IWithdrawQueue.sol";
                    interface IDepositQueue {
                        function depositETHFromProtocol() external payable;
                        function totalEarned(address tokenAddress) external view returns (uint256);
                        function forwardFullWithdrawalETH() external payable;
                        function withdrawQueue() external view returns (IWithdrawQueue);
                        function fillERC20withdrawBuffer(address _asset, uint256 _amount) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title Interface for the BeaconStateOracle contract.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IBeaconChainOracle {
                        /// @notice The block number to state root mapping.
                        function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    interface IDelayedWithdrawalRouter {
                        // struct used to pack data into a single storage slot
                        struct DelayedWithdrawal {
                            uint224 amount;
                            uint32 blockCreated;
                        }
                        // struct used to store a single users delayedWithdrawal data
                        struct UserDelayedWithdrawals {
                            uint256 delayedWithdrawalsCompleted;
                            DelayedWithdrawal[] delayedWithdrawals;
                        }
                        /// @notice event for delayedWithdrawal creation
                        event DelayedWithdrawalCreated(
                            address podOwner,
                            address recipient,
                            uint256 amount,
                            uint256 index
                        );
                        /// @notice event for the claiming of delayedWithdrawals
                        event DelayedWithdrawalsClaimed(
                            address recipient,
                            uint256 amountClaimed,
                            uint256 delayedWithdrawalsCompleted
                        );
                        /// @notice Emitted when the `withdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
                        /**
                         * @notice Creates an delayed withdrawal for `msg.value` to the `recipient`.
                         * @dev Only callable by the `podOwner`'s EigenPod contract.
                         */
                        function createDelayedWithdrawal(address podOwner, address recipient) external payable;
                        /**
                         * @notice Called in order to withdraw delayed withdrawals made to the `recipient` that have passed the `withdrawalDelayBlocks` period.
                         * @param recipient The address to claim delayedWithdrawals for.
                         * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                         */
                        function claimDelayedWithdrawals(
                            address recipient,
                            uint256 maxNumberOfWithdrawalsToClaim
                        ) external;
                        /**
                         * @notice Called in order to withdraw delayed withdrawals made to the caller that have passed the `withdrawalDelayBlocks` period.
                         * @param maxNumberOfWithdrawalsToClaim Used to limit the maximum number of withdrawals to loop through claiming.
                         */
                        function claimDelayedWithdrawals(uint256 maxNumberOfWithdrawalsToClaim) external;
                        /// @notice Owner-only function for modifying the value of the `withdrawalDelayBlocks` variable.
                        function setWithdrawalDelayBlocks(uint256 newValue) external;
                        /// @notice Getter function for the mapping `_userWithdrawals`
                        function userWithdrawals(address user) external view returns (UserDelayedWithdrawals memory);
                        /// @notice Getter function to get all delayedWithdrawals of the `user`
                        function getUserDelayedWithdrawals(
                            address user
                        ) external view returns (DelayedWithdrawal[] memory);
                        /// @notice Getter function to get all delayedWithdrawals that are currently claimable by the `user`
                        function getClaimableUserDelayedWithdrawals(
                            address user
                        ) external view returns (DelayedWithdrawal[] memory);
                        /// @notice Getter function for fetching the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array
                        function userDelayedWithdrawalByIndex(
                            address user,
                            uint256 index
                        ) external view returns (DelayedWithdrawal memory);
                        /// @notice Getter function for fetching the length of the delayedWithdrawals array of a specific user
                        function userWithdrawalsLength(address user) external view returns (uint256);
                        /// @notice Convenience function for checking whether or not the delayedWithdrawal at the `index`th entry from the `_userWithdrawals[user].delayedWithdrawals` array is currently claimable
                        function canClaimDelayedWithdrawal(address user, uint256 index) external view returns (bool);
                        /**
                         * @notice Delay enforced by this contract for completing any delayedWithdrawal. Measured in blocks, and adjustable by this contract's owner,
                         * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
                         */
                        function withdrawalDelayBlocks() external view returns (uint256);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "./IStrategy.sol";
                    import "./ISignatureUtils.sol";
                    import "./IStrategyManager.sol";
                    /**
                     * @title DelegationManager
                     * @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 operators to specify parameters related to stakers who delegate to them
                     * - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
                     * - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
                     */
                    interface IDelegationManager is ISignatureUtils {
                        // Access to public vars - hack locally
                        function beaconChainETHStrategy() external returns (IStrategy);
                        function pendingWithdrawals(bytes32 withdrawalRoot) external view returns (bool);
                        function getDelegatableShares(
                            address staker
                        ) external view returns (IStrategy[] memory, uint256[] memory);
                        // @notice Struct used for storing information about a single operator who has registered with EigenLayer
                        struct OperatorDetails {
                            // @notice address to receive the rewards that the operator earns via serving applications built on EigenLayer.
                            address earningsReceiver;
                            /**
                             * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
                             * @dev Signature verification follows these rules:
                             * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
                             * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
                             * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
                             */
                            address delegationApprover;
                            /**
                             * @notice A minimum delay -- measured in blocks -- enforced between:
                             * 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
                             * and
                             * 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
                             * @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
                             * then they are only allowed to either increase this value or keep it the same.
                             */
                            uint32 stakerOptOutWindowBlocks;
                        }
                        /**
                         * @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator.
                         * @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function.
                         */
                        struct StakerDelegation {
                            // the staker who is delegating
                            address staker;
                            // the operator being delegated to
                            address operator;
                            // the staker's nonce
                            uint256 nonce;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        /**
                         * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
                         * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
                         */
                        struct DelegationApproval {
                            // the staker who is delegating
                            address staker;
                            // the operator being delegated to
                            address operator;
                            // the operator's provided salt
                            bytes32 salt;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        /**
                         * 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. 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 Withdrawal {
                            // The address that originated the Withdrawal
                            address staker;
                            // The address that the staker was delegated to at the time that the Withdrawal was created
                            address delegatedTo;
                            // The address that can complete the Withdrawal + will receive funds when completing the withdrawal
                            address withdrawer;
                            // Nonce used to guarantee that otherwise identical withdrawals have unique hashes
                            uint256 nonce;
                            // Block number when the Withdrawal was created
                            uint32 startBlock;
                            // Array of strategies that the Withdrawal contains
                            IStrategy[] strategies;
                            // Array containing the amount of shares in each Strategy in the `strategies` array
                            uint256[] shares;
                        }
                        struct QueuedWithdrawalParams {
                            // Array of strategies that the QueuedWithdrawal contains
                            IStrategy[] strategies;
                            // Array containing the amount of shares in each Strategy in the `strategies` array
                            uint256[] shares;
                            // The address of the withdrawer
                            address withdrawer;
                        }
                        // @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails.
                        event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails);
                        /// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails
                        event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails);
                        /**
                         * @notice Emitted when @param operator indicates that they are updating their MetadataURI string
                         * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
                         */
                        event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);
                        /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
                        event OperatorSharesIncreased(
                            address indexed operator,
                            address staker,
                            IStrategy strategy,
                            uint256 shares
                        );
                        /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
                        event OperatorSharesDecreased(
                            address indexed operator,
                            address staker,
                            IStrategy strategy,
                            uint256 shares
                        );
                        /// @notice Emitted when @param staker delegates to @param operator.
                        event StakerDelegated(address indexed staker, address indexed operator);
                        /// @notice Emitted when @param staker undelegates from @param operator.
                        event StakerUndelegated(address indexed staker, address indexed operator);
                        /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
                        event StakerForceUndelegated(address indexed staker, address indexed operator);
                        /**
                         * @notice Emitted when a new withdrawal is queued.
                         * @param withdrawalRoot Is the hash of the `withdrawal`.
                         * @param withdrawal Is the withdrawal itself.
                         */
                        event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal);
                        /// @notice Emitted when a queued withdrawal is completed
                        event WithdrawalCompleted(bytes32 withdrawalRoot);
                        /// @notice Emitted when a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager
                        event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot);
                        /// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
                        /// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
                        event StrategyWithdrawalDelayBlocksSet(
                            IStrategy strategy,
                            uint256 previousValue,
                            uint256 newValue
                        );
                        /**
                         * @notice Registers the caller as an operator in EigenLayer.
                         * @param registeringOperatorDetails is the `OperatorDetails` for the operator.
                         * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
                         *
                         * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
                         * @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
                         * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
                         */
                        function registerAsOperator(
                            OperatorDetails calldata registeringOperatorDetails,
                            string calldata metadataURI
                        ) external;
                        /**
                         * @notice Updates an operator's stored `OperatorDetails`.
                         * @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
                         *
                         * @dev The caller must have previously registered as an operator in EigenLayer.
                         * @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
                         */
                        function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external;
                        /**
                         * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
                         * @param metadataURI The URI for metadata associated with an operator
                         * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
                         */
                        function updateOperatorMetadataURI(string calldata metadataURI) external;
                        /**
                         * @notice Caller delegates their stake to an operator.
                         * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer.
                         * @param approverSignatureAndExpiry Verifies the operator approves of this delegation
                         * @param approverSalt A unique single use value tied to an individual signature.
                         * @dev The approverSignatureAndExpiry is used in the event that:
                         *          1) the operator's `delegationApprover` address is set to a non-zero value.
                         *                  AND
                         *          2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
                         *             or their delegationApprover is the `msg.sender`, then approval is assumed.
                         * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
                         * in this case to save on complexity + gas costs
                         */
                        function delegateTo(
                            address operator,
                            SignatureWithExpiry memory approverSignatureAndExpiry,
                            bytes32 approverSalt
                        ) external;
                        /**
                         * @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
                         * @param staker The account delegating stake to an `operator` account
                         * @param operator The account (`staker`) is delegating its assets to for use in serving applications built on EigenLayer.
                         * @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
                         * @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
                         * @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
                         *
                         * @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
                         * @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
                         * @dev the operator's `delegationApprover` address is set to a non-zero value.
                         * @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
                         * is the `msg.sender`, then approval is assumed.
                         * @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
                         * @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
                         * in this case to save on complexity + gas costs
                         */
                        function delegateToBySignature(
                            address staker,
                            address operator,
                            SignatureWithExpiry memory stakerSignatureAndExpiry,
                            SignatureWithExpiry memory approverSignatureAndExpiry,
                            bytes32 approverSalt
                        ) external;
                        /**
                         * @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager
                         * and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary.
                         * @param staker The account to be undelegated.
                         * @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0).
                         *
                         * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
                         * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
                         * @dev Reverts if the `staker` is already undelegated.
                         */
                        function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot);
                        /**
                         * Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
                         * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
                         * their operator.
                         *
                         * All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
                         */
                        function queueWithdrawals(
                            QueuedWithdrawalParams[] calldata queuedWithdrawalParams
                        ) external returns (bytes32[] memory);
                        /**
                         * @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
                         * @param withdrawal The Withdrawal 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 `withdrawal.strategies` array.
                         * 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 withdrawal will be withdrawn from the specified strategies themselves
                         * and sent to the caller, through calls to `withdrawal.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`
                         * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
                         * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
                         * any other strategies, which will be transferred to the withdrawer.
                         */
                        function completeQueuedWithdrawal(
                            Withdrawal calldata withdrawal,
                            IERC20[] calldata tokens,
                            uint256 middlewareTimesIndex,
                            bool receiveAsTokens
                        ) external;
                        /**
                         * @notice Array-ified version of `completeQueuedWithdrawal`.
                         * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
                         * @param withdrawals The Withdrawals to complete.
                         * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
                         * @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
                         * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
                         * @dev See `completeQueuedWithdrawal` for relevant dev tags
                         */
                        function completeQueuedWithdrawals(
                            Withdrawal[] calldata withdrawals,
                            IERC20[][] calldata tokens,
                            uint256[] calldata middlewareTimesIndexes,
                            bool[] calldata receiveAsTokens
                        ) external;
                        /**
                         * @notice Increases a staker's delegated share balance in a strategy.
                         * @param staker The address to increase the delegated shares for their operator.
                         * @param strategy The strategy in which to increase the delegated shares.
                         * @param shares The number of shares to increase.
                         *
                         * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
                         * @dev Callable only by the StrategyManager or EigenPodManager.
                         */
                        function increaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
                        /**
                         * @notice Decreases a staker's delegated share balance in a strategy.
                         * @param staker The address to increase the delegated shares for their operator.
                         * @param strategy The strategy in which to decrease the delegated shares.
                         * @param shares The number of shares to decrease.
                         *
                         * @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
                         * @dev Callable only by the StrategyManager or EigenPodManager.
                         */
                        function decreaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
                        /**
                         * @notice returns the address of the operator that `staker` is delegated to.
                         * @notice Mapping: staker => operator whom the staker is currently delegated to.
                         * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
                         */
                        function delegatedTo(address staker) external view returns (address);
                        /**
                         * @notice Returns the OperatorDetails struct associated with an `operator`.
                         */
                        function operatorDetails(address operator) external view returns (OperatorDetails memory);
                        /*
                         * @notice Returns the earnings receiver address for an operator
                         */
                        function earningsReceiver(address operator) external view returns (address);
                        /**
                         * @notice Returns the delegationApprover account for an operator
                         */
                        function delegationApprover(address operator) external view returns (address);
                        /**
                         * @notice Returns the stakerOptOutWindowBlocks for an operator
                         */
                        function stakerOptOutWindowBlocks(address operator) external view returns (uint256);
                        /**
                         * @notice Given array of strategies, returns array of shares for the operator
                         */
                        function getOperatorShares(
                            address operator,
                            IStrategy[] memory strategies
                        ) external view returns (uint256[] memory);
                        /**
                         * @notice Given a list of strategies, return the minimum number of blocks that must pass to withdraw
                         * from all the inputted strategies. Return value is >= minWithdrawalDelayBlocks as this is the global min withdrawal delay.
                         * @param strategies The strategies to check withdrawal delays for
                         */
                        function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256);
                        /**
                         * @notice returns the total number of shares in `strategy` that are delegated to `operator`.
                         * @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
                         * @dev By design, the following invariant should hold for each Strategy:
                         * (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
                         * = sum (delegateable shares of all stakers delegated to the operator)
                         */
                        function operatorShares(address operator, IStrategy strategy) external view returns (uint256);
                        /**
                         * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
                         */
                        function isDelegated(address staker) external view returns (bool);
                        /**
                         * @notice Returns true is an operator has previously registered for delegation.
                         */
                        function isOperator(address operator) external view returns (bool);
                        /// @notice Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked
                        function stakerNonce(address staker) external view returns (uint256);
                        /**
                         * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
                         * @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's
                         * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
                         */
                        function delegationApproverSaltIsSpent(
                            address _delegationApprover,
                            bytes32 salt
                        ) external view returns (bool);
                        /**
                         * @notice Minimum delay enforced by this contract for completing queued withdrawals. 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).
                         * Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum number of blocks that must pass
                         * to withdraw a strategy is MAX(minWithdrawalDelayBlocks, strategyWithdrawalDelayBlocks[strategy])
                         */
                        function minWithdrawalDelayBlocks() external view returns (uint256);
                        /**
                         * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. 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 strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256);
                        /**
                         * @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
                         * @param staker The signing staker
                         * @param operator The operator who is being delegated to
                         * @param expiry The desired expiry time of the staker's signature
                         */
                        function calculateCurrentStakerDelegationDigestHash(
                            address staker,
                            address operator,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /**
                         * @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
                         * @param staker The signing staker
                         * @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
                         * @param operator The operator who is being delegated to
                         * @param expiry The desired expiry time of the staker's signature
                         */
                        function calculateStakerDelegationDigestHash(
                            address staker,
                            uint256 _stakerNonce,
                            address operator,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /**
                         * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
                         * @param staker The account delegating their stake
                         * @param operator The account receiving delegated stake
                         * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
                         * @param approverSalt A unique and single use value associated with the approver signature.
                         * @param expiry Time after which the approver's signature becomes invalid
                         */
                        function calculateDelegationApprovalDigestHash(
                            address staker,
                            address operator,
                            address _delegationApprover,
                            bytes32 approverSalt,
                            uint256 expiry
                        ) external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the contract's domain
                        function DOMAIN_TYPEHASH() external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract
                        function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32);
                        /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
                        function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
                        /**
                         * @notice Getter function for the current EIP-712 domain separator for this contract.
                         *
                         * @dev The domain separator will change in the event of a fork that changes the ChainID.
                         * @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
                         * for more detailed information please read EIP-712.
                         */
                        function domainSeparator() external view returns (bytes32);
                        /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
                        /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
                        function cumulativeWithdrawalsQueued(address staker) external view returns (uint256);
                        /// @notice Returns the keccak256 hash of `withdrawal`.
                        function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32);
                        function migrateQueuedWithdrawals(
                            IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue
                        ) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "../libraries/BeaconChainProofs.sol";
                    import "./IEigenPodManager.sol";
                    import "./IBeaconChainOracle.sol";
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "./IDelayedWithdrawalRouter.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 {
                        // Hack to allow the external visibility of the public variable for default getter in EigenPod
                        function delayedWithdrawalRouter() external returns (IDelayedWithdrawalRouter);
                        enum VALIDATOR_STATUS {
                            INACTIVE, // doesnt exist
                            ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
                            WITHDRAWN // withdrawn from the Beacon Chain
                        }
                        struct ValidatorInfo {
                            // index of the validator in the beacon chain
                            uint64 validatorIndex;
                            // amount of beacon chain ETH restaked on EigenLayer in gwei
                            uint64 restakedBalanceGwei;
                            //timestamp of the validator's most recent balance update
                            uint64 mostRecentBalanceUpdateTimestamp;
                            // status of the validator
                            VALIDATOR_STATUS status;
                        }
                        /**
                         * @notice struct used to store amounts related to proven withdrawals in memory. Used to help
                         * manage stack depth and optimize the number of external calls, when batching withdrawal operations.
                         */
                        struct VerifiedWithdrawal {
                            // amount to send to a podOwner from a proven withdrawal
                            uint256 amountToSendGwei;
                            // difference in shares to be recorded in the eigenPodManager, as a result of the withdrawal
                            int256 sharesDeltaGwei;
                        }
                        enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
                            REDEEMED,
                            PENDING,
                            FAILED
                        }
                        /// @notice Emitted when an ETH validator stakes via this eigenPod
                        event EigenPodStaked(bytes pubkey);
                        /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
                        event ValidatorRestaked(uint40 validatorIndex);
                        /// @notice Emitted when an ETH validator's  balance is proven to be updated.  Here newValidatorBalanceGwei
                        //  is the validator's balance that is credited on EigenLayer.
                        event ValidatorBalanceUpdated(
                            uint40 validatorIndex,
                            uint64 balanceTimestamp,
                            uint64 newValidatorBalanceGwei
                        );
                        /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
                        event FullWithdrawalRedeemed(
                            uint40 validatorIndex,
                            uint64 withdrawalTimestamp,
                            address indexed recipient,
                            uint64 withdrawalAmountGwei
                        );
                        /// @notice Emitted when a partial withdrawal claim is successfully redeemed
                        event PartialWithdrawalRedeemed(
                            uint40 validatorIndex,
                            uint64 withdrawalTimestamp,
                            address indexed recipient,
                            uint64 partialWithdrawalAmountGwei
                        );
                        /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
                        event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
                        /// @notice Emitted when podOwner enables restaking
                        event RestakingActivated(address indexed podOwner);
                        /// @notice Emitted when ETH is received via the `receive` fallback
                        event NonBeaconChainETHReceived(uint256 amountReceived);
                        /// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
                        event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);
                        /// @notice The max amount of eth, in gwei, that can be restaked per validator
                        function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);
                        /// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
                        function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);
                        /// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
                        function nonBeaconChainETHBalanceWei() external view returns (uint256);
                        /// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
                        function initialize(address owner) external;
                        /// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
                        function stake(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        /**
                         * @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address
                         * @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain.
                         * @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `withdrawableRestakedExecutionLayerGwei` exceeds the
                         * `amountWei` input (when converted to GWEI).
                         * @dev Reverts if `amountWei` is not a whole Gwei amount
                         */
                        function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;
                        /// @notice The single EigenPodManager for EigenLayer
                        function eigenPodManager() external view returns (IEigenPodManager);
                        /// @notice The owner of this EigenPod
                        function podOwner() external view returns (address);
                        /// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
                        function hasRestaked() external view returns (bool);
                        /**
                         * @notice The latest timestamp at which the pod owner withdrew the balance of the pod, via calling `withdrawBeforeRestaking`.
                         * @dev This variable is only updated when the `withdrawBeforeRestaking` function is called, which can only occur before `hasRestaked` is set to true for this pod.
                         * Proofs for this pod are only valid against Beacon Chain state roots corresponding to timestamps after the stored `mostRecentWithdrawalTimestamp`.
                         */
                        function mostRecentWithdrawalTimestamp() external view returns (uint64);
                        /// @notice Returns the validatorInfo struct for the provided pubkeyHash
                        function validatorPubkeyHashToInfo(
                            bytes32 validatorPubkeyHash
                        ) external view returns (ValidatorInfo memory);
                        /// @notice Returns the validatorInfo struct for the provided pubkey
                        function validatorPubkeyToInfo(
                            bytes calldata validatorPubkey
                        ) external view returns (ValidatorInfo memory);
                        ///@notice mapping that tracks proven withdrawals
                        function provenWithdrawal(
                            bytes32 validatorPubkeyHash,
                            uint64 slot
                        ) external view returns (bool);
                        /// @notice This returns the status of a given validator
                        function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS);
                        /// @notice This returns the status of a given validator pubkey
                        function validatorStatus(
                            bytes calldata validatorPubkey
                        ) external view returns (VALIDATOR_STATUS);
                        /**
                         * @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to
                         * this contract. It also verifies the effective balance  of the validator.  It verifies the provided proof of the ETH validator against the beacon chain state
                         * root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer.
                         * @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against.
                         * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
                         * @param withdrawalCredentialProofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials
                         * against a beacon chain state root
                         * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                         * for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                         */
                        function verifyWithdrawalCredentials(
                            uint64 oracleTimestamp,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            uint40[] calldata validatorIndices,
                            bytes[] calldata withdrawalCredentialProofs,
                            bytes32[][] calldata validatorFields
                        ) external;
                        /**
                         * @notice This function records an update (either increase or decrease) in the pod's balance in the StrategyManager.  
                                   It also verifies a merkle proof of the validator's current beacon chain balance.  
                         * @param oracleTimestamp The oracleTimestamp whose state root the `proof` will be proven against.
                         *        Must be within `VERIFY_BALANCE_UPDATE_WINDOW_SECONDS` of the current block.
                         * @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs 
                         * @param validatorFieldsProofs proofs against the `beaconStateRoot` for each validator in `validatorFields`
                         * @param validatorFields are the fields of the "Validator Container", refer to consensus specs
                         * @dev For more details on the Beacon Chain spec, see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                         */
                        function verifyBalanceUpdates(
                            uint64 oracleTimestamp,
                            uint40[] calldata validatorIndices,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            bytes[] calldata validatorFieldsProofs,
                            bytes32[][] calldata validatorFields
                        ) external;
                        /**
                         * @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod
                         * @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against
                         * @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven
                         * @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree
                         * @param withdrawalFields are the fields of the withdrawals being proven
                         * @param validatorFields are the fields of the validators being proven
                         */
                        function verifyAndProcessWithdrawals(
                            uint64 oracleTimestamp,
                            BeaconChainProofs.StateRootProof calldata stateRootProof,
                            BeaconChainProofs.WithdrawalProof[] calldata withdrawalProofs,
                            bytes[] calldata validatorFieldsProofs,
                            bytes32[][] calldata validatorFields,
                            bytes32[][] calldata withdrawalFields
                        ) external;
                        /**
                         * @notice Called by the pod owner to activate restaking by withdrawing
                         * all existing ETH from the pod and preventing further withdrawals via
                         * "withdrawBeforeRestaking()"
                         */
                        function activateRestaking() external;
                        /// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
                        function withdrawBeforeRestaking() external;
                        /// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei
                        function withdrawNonBeaconChainETHBalanceWei(
                            address recipient,
                            uint256 amountToWithdraw
                        ) external;
                        /// @notice called by owner of a pod to remove any ERC20s deposited in the pod
                        function recoverTokens(
                            IERC20[] memory tokenList,
                            uint256[] memory amountsToWithdraw,
                            address recipient
                        ) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
                    import "./IETHPOSDeposit.sol";
                    import "./IStrategyManager.sol";
                    import "./IEigenPod.sol";
                    import "./IBeaconChainOracle.sol";
                    import "./IPausable.sol";
                    import "./ISlasher.sol";
                    import "./IStrategy.sol";
                    /**
                     * @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IEigenPodManager is IPausable {
                        /// @notice Emitted to notify the update of the beaconChainOracle address
                        event BeaconOracleUpdated(address indexed newOracleAddress);
                        /// @notice Emitted to notify the deployment of an EigenPod
                        event PodDeployed(address indexed eigenPod, address indexed podOwner);
                        /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
                        event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);
                        /// @notice Emitted when the balance of an EigenPod is updated
                        event PodSharesUpdated(address indexed podOwner, int256 sharesDelta);
                        /// @notice Emitted when a withdrawal of beacon chain ETH is completed
                        event BeaconChainETHWithdrawalCompleted(
                            address indexed podOwner,
                            uint256 shares,
                            uint96 nonce,
                            address delegatedAddress,
                            address withdrawer,
                            bytes32 withdrawalRoot
                        );
                        event DenebForkTimestampUpdated(uint64 newValue);
                        /**
                         * @notice Creates an EigenPod for the sender.
                         * @dev Function will revert if the `msg.sender` already has an EigenPod.
                         * @dev Returns EigenPod address
                         */
                        function createPod() external returns (address);
                        /**
                         * @notice Stakes for a new beacon chain validator on the sender's EigenPod.
                         * Also creates an EigenPod for the sender if they don't have one already.
                         * @param pubkey The 48 bytes public key of the beacon chain validator.
                         * @param signature The validator's signature of the deposit data.
                         * @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
                         */
                        function stake(
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        /**
                         * @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager
                         * to ensure that delegated shares are also tracked correctly
                         * @param podOwner is the pod owner whose balance is being updated.
                         * @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
                         * @dev Callable only by the podOwner's EigenPod contract.
                         * @dev Reverts if `sharesDelta` is not a whole Gwei amount
                         */
                        function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
                        /**
                         * @notice Updates the oracle contract that provides the beacon chain state root
                         * @param newBeaconChainOracle is the new oracle contract being pointed to
                         * @dev Callable only by the owner of this contract (i.e. governance)
                         */
                        function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external;
                        /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
                        function ownerToPod(address podOwner) external view returns (IEigenPod);
                        /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
                        function getPod(address podOwner) external view returns (IEigenPod);
                        /// @notice The ETH2 Deposit Contract
                        function ethPOS() external view returns (IETHPOSDeposit);
                        /// @notice Beacon proxy to which the EigenPods point
                        function eigenPodBeacon() external view returns (IBeacon);
                        /// @notice Oracle contract that provides updates to the beacon chain's state
                        function beaconChainOracle() external view returns (IBeaconChainOracle);
                        /// @notice Returns the beacon block root at `timestamp`. Reverts if the Beacon block root at `timestamp` has not yet been finalized.
                        function getBlockRootAtTimestamp(uint64 timestamp) external view returns (bytes32);
                        /// @notice EigenLayer's StrategyManager contract
                        function strategyManager() external view returns (IStrategyManager);
                        /// @notice EigenLayer's Slasher contract
                        function slasher() external view returns (ISlasher);
                        /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
                        function hasPod(address podOwner) external view returns (bool);
                        /// @notice Returns the number of EigenPods that have been created
                        function numPods() external view returns (uint256);
                        /**
                         * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
                         * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
                         * decrease between the pod owner queuing and completing a withdrawal.
                         * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
                         * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
                         * as the withdrawal "paying off the deficit".
                         */
                        function podOwnerShares(address podOwner) external view returns (int256);
                        /// @notice returns canonical, virtual beaconChainETH strategy
                        function beaconChainETHStrategy() external view returns (IStrategy);
                        /**
                         * @notice Used by the DelegationManager to remove a pod owner's shares while they're in the withdrawal queue.
                         * Simply decreases the `podOwner`'s shares by `shares`, down to a minimum of zero.
                         * @dev This function reverts if it would result in `podOwnerShares[podOwner]` being less than zero, i.e. it is forbidden for this function to
                         * result in the `podOwner` incurring a "share deficit". This behavior prevents a Staker from queuing a withdrawal which improperly removes excessive
                         * shares from the operator to whom the staker is delegated.
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function removeShares(address podOwner, uint256 shares) external;
                        /**
                         * @notice Increases the `podOwner`'s shares by `shares`, paying off deficit if possible.
                         * Used by the DelegationManager to award a pod owner shares on exiting the withdrawal queue
                         * @dev Returns the number of shares added to `podOwnerShares[podOwner]` above zero, which will be less than the `shares` input
                         * in the event that the podOwner has an existing shares deficit (i.e. `podOwnerShares[podOwner]` starts below zero)
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function addShares(address podOwner, uint256 shares) external returns (uint256);
                        /**
                         * @notice Used by the DelegationManager to complete a withdrawal, sending tokens to some destination address
                         * @dev Prioritizes decreasing the podOwner's share deficit, if they have one
                         * @dev Reverts if `shares` is not a whole Gwei amount
                         */
                        function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;
                        /**
                         * @notice the deneb hard fork timestamp used to determine which proof path to use for proving a withdrawal
                         */
                        function denebForkTimestamp() external view returns (uint64);
                        /**
                         * setting the deneb hard fork timestamp by the eigenPodManager owner
                         * @dev this function is designed to be called twice.  Once, it is set to type(uint64).max
                         * prior to the actual deneb fork timestamp being set, and then the second time it is set
                         * to the actual deneb fork timestamp.
                         */
                        function setDenebForkTimestamp(uint64 newDenebForkTimestamp) external;
                    }
                    // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
                    // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
                    // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
                    // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
                    // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
                    // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
                    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                    // SPDX-License-Identifier: CC0-1.0
                    pragma solidity >=0.5.0;
                    // This interface is designed to be compatible with the Vyper version.
                    /// @notice This is the Ethereum 2.0 deposit contract interface.
                    /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
                    interface IETHPOSDeposit {
                        /// @notice A processed deposit event.
                        event DepositEvent(
                            bytes pubkey,
                            bytes withdrawal_credentials,
                            bytes amount,
                            bytes signature,
                            bytes index
                        );
                        /// @notice Submit a Phase 0 DepositData object.
                        /// @param pubkey A BLS12-381 public key.
                        /// @param withdrawal_credentials Commitment to a public key for withdrawals.
                        /// @param signature A BLS12-381 signature.
                        /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
                        /// Used as a protection against malformed input.
                        function deposit(
                            bytes calldata pubkey,
                            bytes calldata withdrawal_credentials,
                            bytes calldata signature,
                            bytes32 deposit_data_root
                        ) external payable;
                        /// @notice Query the current deposit root hash.
                        /// @return The deposit root hash.
                        function get_deposit_root() external view returns (bytes32);
                        /// @notice Query the current deposit count.
                        /// @return The deposit count encoded as a little endian 64-bit number.
                        function get_deposit_count() external view returns (bytes memory);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "../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 Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
                        event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
                        /// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
                        event Paused(address indexed account, uint256 newPausedStatus);
                        /// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
                        event Unpaused(address indexed account, uint256 newPausedStatus);
                        /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
                        function pauserRegistry() external view returns (IPauserRegistry);
                        /**
                         * @notice This function is used to pause an EigenLayer contract's functionality.
                         * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
                         * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                         * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
                         */
                        function pause(uint256 newPausedStatus) external;
                        /**
                         * @notice Alias for `pause(type(uint256).max)`.
                         */
                        function pauseAll() external;
                        /**
                         * @notice This function is used to unpause an EigenLayer contract's functionality.
                         * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
                         * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
                         * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
                         */
                        function unpause(uint256 newPausedStatus) external;
                        /// @notice Returns the current paused status as a uint256.
                        function paused() external view returns (uint256);
                        /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
                        function paused(uint8 index) external view returns (bool);
                        /// @notice Allows the unpauser to set a new pauser registry
                        function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title Interface for the `PauserRegistry` contract.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface IPauserRegistry {
                        event PauserStatusChanged(address pauser, bool canPause);
                        event UnpauserChanged(address previousUnpauser, address newUnpauser);
                        /// @notice Mapping of addresses to whether they hold the pauser role.
                        function isPauser(address pauser) external view returns (bool);
                        /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
                        function unpauser() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    /**
                     * @title The interface for common signature utilities.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     */
                    interface ISignatureUtils {
                        // @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management.
                        struct SignatureWithExpiry {
                            // the signature itself, formatted as a single bytes object
                            bytes signature;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                        // @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management.
                        struct SignatureWithSaltAndExpiry {
                            // the signature itself, formatted as a single bytes object
                            bytes signature;
                            // the salt used to generate the signature
                            bytes32 salt;
                            // the expiration timestamp (UTC) of the signature
                            uint256 expiry;
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity >=0.5.0;
                    import "./IStrategyManager.sol";
                    import "./IDelegationManager.sol";
                    /**
                     * @title Interface for the primary 'slashing' contract for EigenLayer.
                     * @author Layr Labs, Inc.
                     * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
                     * @notice See the `Slasher` contract itself for implementation details.
                     */
                    interface ISlasher {
                        // struct used to store information about the current state of an operator's obligations to middlewares they are serving
                        struct MiddlewareTimes {
                            // The update block for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
                            uint32 stalestUpdateBlock;
                            // The latest 'serveUntilBlock' from all of the middleware that the operator is serving
                            uint32 latestServeUntilBlock;
                        }
                        // struct used to store details relevant to a single middleware that an operator has opted-in to serving
                        struct MiddlewareDetails {
                            // the block at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
                            uint32 registrationMayBeginAtBlock;
                            // the block before which the contract is allowed to slash the user
                            uint32 contractCanSlashOperatorUntilBlock;
                            // the block at which the middleware's view of the operator's stake was most recently updated
                            uint32 latestUpdateBlock;
                        }
                        /// @notice Emitted when a middleware times is added to `operator`'s array.
                        event MiddlewareTimesAdded(
                            address operator,
                            uint256 index,
                            uint32 stalestUpdateBlock,
                            uint32 latestServeUntilBlock
                        );
                        /// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
                        event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
                        /// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilBlock`.
                        event SlashingAbilityRevoked(
                            address indexed operator,
                            address indexed contractAddress,
                            uint32 contractCanSlashOperatorUntilBlock
                        );
                        /**
                         * @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
                         * @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
                         */
                        event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
                        /// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within EigenLayer.
                        event FrozenStatusReset(address indexed previouslySlashedAddress);
                        /**
                         * @notice Gives the `contractAddress` permission to slash the funds of the caller.
                         * @dev Typically, this function must be called prior to registering for a middleware.
                         */
                        function optIntoSlashing(address contractAddress) external;
                        /**
                         * @notice Used for 'slashing' a certain operator.
                         * @param toBeFrozen The operator to be frozen.
                         * @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
                         * @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
                         */
                        function freezeOperator(address toBeFrozen) external;
                        /**
                         * @notice Removes the 'frozen' status from each of the `frozenAddresses`
                         * @dev Callable only by the contract owner (i.e. governance).
                         */
                        function resetFrozenStatus(address[] calldata frozenAddresses) external;
                        /**
                         * @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
                         *         is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                         * @dev adds the middleware's slashing contract to the operator's linked list
                         */
                        function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) external;
                        /**
                         * @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
                         *         to make sure the operator's stake at updateBlock is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param updateBlock the block for which the stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable
                         * @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
                         * @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
                         *      but it is anticipated to be rare and not detrimental.
                         */
                        function recordStakeUpdate(
                            address operator,
                            uint32 updateBlock,
                            uint32 serveUntilBlock,
                            uint256 insertAfter
                        ) external;
                        /**
                         * @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
                         *         is slashable until serveUntil
                         * @param operator the operator whose stake update is being recorded
                         * @param serveUntilBlock the block until which the operator's stake at the current block is slashable
                         * @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
                         * slash `operator` once `serveUntil` is reached
                         */
                        function recordLastStakeUpdateAndRevokeSlashingAbility(
                            address operator,
                            uint32 serveUntilBlock
                        ) external;
                        /// @notice The StrategyManager contract of EigenLayer
                        function strategyManager() external view returns (IStrategyManager);
                        /// @notice The DelegationManager contract of EigenLayer
                        function delegation() external view returns (IDelegationManager);
                        /**
                         * @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
                         * slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
                         * and the staker's status is reset (to 'unfrozen').
                         * @param staker The staker of interest.
                         * @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
                         * to an operator who has their status set to frozen. Otherwise returns 'false'.
                         */
                        function isFrozen(address staker) external view returns (bool);
                        /// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
                        function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
                        /// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`.
                        function contractCanSlashOperatorUntilBlock(
                            address operator,
                            address serviceContract
                        ) external view returns (uint32);
                        /// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake
                        function latestUpdateBlock(
                            address operator,
                            address serviceContract
                        ) external view returns (uint32);
                        /// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
                        function getCorrectValueForInsertAfter(
                            address operator,
                            uint32 updateBlock
                        ) external view returns (uint256);
                        /**
                         * @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used
                         * to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
                         * struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
                         * This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event
                         * that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
                         * @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
                         * this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`.
                         * @param withdrawalStartBlock The block number at which the withdrawal was initiated.
                         * @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
                         * @dev The correct `middlewareTimesIndex` input should be computable off-chain.
                         */
                        function canWithdraw(
                            address operator,
                            uint32 withdrawalStartBlock,
                            uint256 middlewareTimesIndex
                        ) external returns (bool);
                        /**
                         * operator =>
                         *  [
                         *      (
                         *          the least recent update block of all of the middlewares it's serving/served,
                         *          latest time that the stake bonded at that update needed to serve until
                         *      )
                         *  ]
                         */
                        function operatorToMiddlewareTimes(
                            address operator,
                            uint256 arrayIndex
                        ) external view returns (MiddlewareTimes memory);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
                        function middlewareTimesLength(address operator) external view returns (uint256);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`.
                        function getMiddlewareTimesIndexStalestUpdateBlock(
                            address operator,
                            uint32 index
                        ) external view returns (uint32);
                        /// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
                        function getMiddlewareTimesIndexServeUntilBlock(
                            address operator,
                            uint32 index
                        ) external view returns (uint32);
                        /// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
                        function operatorWhitelistedContractsLinkedListSize(
                            address operator
                        ) external view returns (uint256);
                        /// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
                        function operatorWhitelistedContractsLinkedListEntry(
                            address operator,
                            address node
                        ) external view returns (bool, uint256, uint256);
                    }
                    // SPDX-License-Identifier: 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 `recipient`'s address
                         * @param recipient 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 recipient, 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 convenience function for fetching the current total shares of `user` in this strategy, by
                         * querying the `strategyManager` contract
                         */
                        function shares(address user) external view 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.5.0;
                    import "./IStrategy.sol";
                    import "./ISlasher.sol";
                    import "./IDelegationManager.sol";
                    import "./IEigenPodManager.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 {
                        // Access to public vars - hack locally
                        function stakerStrategyList(address staker, uint256 index) external view returns (IStrategy);
                        function strategyIsWhitelistedForDeposit(IStrategy _strategy) external view returns (bool);
                        /**
                         * @notice Emitted when a new deposit occurs on behalf of `staker`.
                         * @param staker Is the staker who is depositing funds into EigenLayer.
                         * @param strategy Is the strategy that `staker` has deposited into.
                         * @param token Is the token that `staker` deposited.
                         * @param shares Is the number of new shares `staker` has been granted in `strategy`.
                         */
                        event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares);
                        /// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner
                        event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value);
                        /// @notice Emitted when the `strategyWhitelister` is changed
                        event StrategyWhitelisterChanged(address previousAddress, address newAddress);
                        /// @notice Emitted when a strategy is added to the approved list of strategies for deposit
                        event StrategyAddedToDepositWhitelist(IStrategy strategy);
                        /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
                        event StrategyRemovedFromDepositWhitelist(IStrategy strategy);
                        /**
                         * @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 staker
                         * @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 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 staker
                         * @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 if thirdPartyTransfersForbidden is set to true for this strategy
                         *
                         *  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 Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
                        function removeShares(address staker, IStrategy strategy, uint256 shares) external;
                        /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
                        function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external;
                        /// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient
                        function withdrawSharesAsTokens(
                            address recipient,
                            IStrategy strategy,
                            uint256 shares,
                            IERC20 token
                        ) external;
                        /// @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 staker's deposits and corresponding shares
                         * @return (staker's strategies, shares in these strategies)
                         */
                        function getDeposits(
                            address staker
                        ) 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 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)
                         * @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy
                         */
                        function addStrategiesToDepositWhitelist(
                            IStrategy[] calldata strategiesToWhitelist,
                            bool[] calldata thirdPartyTransfersForbiddenValues
                        ) 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 EigenPodManager contract of EigenLayer
                        function eigenPodManager() external view returns (IEigenPodManager);
                        /// @notice Returns the address of the `strategyWhitelister`
                        function strategyWhitelister() external view returns (address);
                        /**
                         * @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling
                         * depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker.
                         */
                        function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool);
                        // LIMITED BACKWARDS-COMPATIBILITY FOR DEPRECATED FUNCTIONALITY
                        // packed struct for queued withdrawals; helps deal with stack-too-deep errors
                        struct DeprecatedStruct_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 DeprecatedStruct_QueuedWithdrawal {
                            IStrategy[] strategies;
                            uint256[] shares;
                            address staker;
                            DeprecatedStruct_WithdrawerAndNonce withdrawerAndNonce;
                            uint32 withdrawalStartBlock;
                            address delegatedAddress;
                        }
                        function migrateQueuedWithdrawal(
                            DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal
                        ) external returns (bool, bytes32);
                        function calculateWithdrawalRoot(
                            DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal
                        ) external pure returns (bytes32);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity =0.8.19;
                    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 BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT = 3;
                        uint256 internal constant BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT = 4;
                        uint256 internal constant BEACON_STATE_FIELD_TREE_HEIGHT = 5;
                        uint256 internal constant VALIDATOR_FIELD_TREE_HEIGHT = 3;
                        //Note: changed in the deneb hard fork from 4->5
                        uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB = 5;
                        uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA = 4;
                        // SLOTS_PER_HISTORICAL_ROOT = 2**13, so tree height is 13
                        uint256 internal constant BLOCK_ROOTS_TREE_HEIGHT = 13;
                        //HISTORICAL_ROOTS_LIMIT = 2**24, so tree height is 24
                        uint256 internal constant HISTORICAL_SUMMARIES_TREE_HEIGHT = 24;
                        //Index of block_summary_root in historical_summary container
                        uint256 internal constant BLOCK_SUMMARY_ROOT_INDEX = 0;
                        // tree height for hash tree of an individual withdrawal container
                        uint256 internal constant WITHDRAWAL_FIELD_TREE_HEIGHT = 2;
                        uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;
                        // MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
                        uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
                        //in beacon block body https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconblockbody
                        uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
                        // in beacon block header https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
                        uint256 internal constant SLOT_INDEX = 0;
                        uint256 internal constant STATE_ROOT_INDEX = 3;
                        uint256 internal constant BODY_ROOT_INDEX = 4;
                        // in beacon state https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
                        uint256 internal constant VALIDATOR_TREE_ROOT_INDEX = 11;
                        uint256 internal constant HISTORICAL_SUMMARIES_INDEX = 27;
                        // in validator https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
                        uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
                        uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
                        uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
                        uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;
                        // in execution payload header
                        uint256 internal constant TIMESTAMP_INDEX = 9;
                        //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;
                        //Misc Constants
                        /// @notice The number of slots each epoch in the beacon chain
                        uint64 internal constant SLOTS_PER_EPOCH = 32;
                        /// @notice The number of seconds in a slot in the beacon chain
                        uint64 internal constant SECONDS_PER_SLOT = 12;
                        /// @notice Number of seconds per epoch: 384 == 32 slots/epoch * 12 seconds/slot
                        uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;
                        bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
                        /// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal
                        struct WithdrawalProof {
                            bytes withdrawalProof;
                            bytes slotProof;
                            bytes executionPayloadProof;
                            bytes timestampProof;
                            bytes historicalSummaryBlockRootProof;
                            uint64 blockRootIndex;
                            uint64 historicalSummaryIndex;
                            uint64 withdrawalIndex;
                            bytes32 blockRoot;
                            bytes32 slotRoot;
                            bytes32 timestampRoot;
                            bytes32 executionPayloadRoot;
                        }
                        /// @notice This struct contains the root and proof for verifying the state root against the oracle block root
                        struct StateRootProof {
                            bytes32 beaconStateRoot;
                            bytes proof;
                        }
                        /**
                         * @notice This function verifies merkle proofs of the fields of a certain validator against a beacon chain state root
                         * @param validatorIndex the index of the proven validator
                         * @param beaconStateRoot is the beacon chain state root to be proven against.
                         * @param validatorFieldsProof is the data used in proving the validator's fields
                         * @param validatorFields the claimed fields of the validator
                         */
                        function verifyValidatorFields(
                            bytes32 beaconStateRoot,
                            bytes32[] calldata validatorFields,
                            bytes calldata validatorFieldsProof,
                            uint40 validatorIndex
                        ) internal view {
                            require(
                                validatorFields.length == 2 ** VALIDATOR_FIELD_TREE_HEIGHT,
                                "BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"
                            );
                            /**
                             * Note: the length of the validator merkle proof is BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1.
                             * There is an additional layer added by hashing the root with the length of the validator list
                             */
                            require(
                                validatorFieldsProof.length ==
                                    32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyValidatorFields: Proof has incorrect length"
                            );
                            uint256 index = (VALIDATOR_TREE_ROOT_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) |
                                uint256(validatorIndex);
                            // merkleize the validatorFields to get the leaf to prove
                            bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields);
                            // verify the proof of the validatorRoot against the beaconStateRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: validatorFieldsProof,
                                    root: beaconStateRoot,
                                    leaf: validatorRoot,
                                    index: index
                                }),
                                "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof"
                            );
                        }
                        /**
                         * @notice This function verifies the latestBlockHeader against the state root. the latestBlockHeader is
                         * a tracked in the beacon state.
                         * @param beaconStateRoot is the beacon chain state root to be proven against.
                         * @param stateRootProof is the provided merkle proof
                         * @param latestBlockRoot is hashtree root of the latest block header in the beacon state
                         */
                        function verifyStateRootAgainstLatestBlockRoot(
                            bytes32 latestBlockRoot,
                            bytes32 beaconStateRoot,
                            bytes calldata stateRootProof
                        ) internal view {
                            require(
                                stateRootProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Proof has incorrect length"
                            );
                            //Next we verify the slot against the blockRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: stateRootProof,
                                    root: latestBlockRoot,
                                    leaf: beaconStateRoot,
                                    index: STATE_ROOT_INDEX
                                }),
                                "BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Invalid latest block header root merkle proof"
                            );
                        }
                        /**
                         * @notice This function verifies the slot and the withdrawal fields for a given withdrawal
                         * @param withdrawalProof is the provided set of merkle proofs
                         * @param withdrawalFields is the serialized withdrawal container to be proven
                         */
                        function verifyWithdrawal(
                            bytes32 beaconStateRoot,
                            bytes32[] calldata withdrawalFields,
                            WithdrawalProof calldata withdrawalProof,
                            uint64 denebForkTimestamp
                        ) internal view {
                            require(
                                withdrawalFields.length == 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length"
                            );
                            require(
                                withdrawalProof.blockRootIndex < 2 ** BLOCK_ROOTS_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: blockRootIndex is too large"
                            );
                            require(
                                withdrawalProof.withdrawalIndex < 2 ** WITHDRAWALS_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
                            );
                            require(
                                withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
                                "BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
                            );
                            //Note: post deneb hard fork, the number of exection payload header fields increased from 15->17, adding an extra level to the tree height
                            uint256 executionPayloadHeaderFieldTreeHeight = (getWithdrawalTimestamp(withdrawalProof) <
                                denebForkTimestamp)
                                ? EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA
                                : EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB;
                            require(
                                withdrawalProof.withdrawalProof.length ==
                                    32 * (executionPayloadHeaderFieldTreeHeight + WITHDRAWALS_TREE_HEIGHT + 1),
                                "BeaconChainProofs.verifyWithdrawal: withdrawalProof has incorrect length"
                            );
                            require(
                                withdrawalProof.executionPayloadProof.length ==
                                    32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyWithdrawal: executionPayloadProof has incorrect length"
                            );
                            require(
                                withdrawalProof.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
                                "BeaconChainProofs.verifyWithdrawal: slotProof has incorrect length"
                            );
                            require(
                                withdrawalProof.timestampProof.length == 32 * (executionPayloadHeaderFieldTreeHeight),
                                "BeaconChainProofs.verifyWithdrawal: timestampProof has incorrect length"
                            );
                            require(
                                withdrawalProof.historicalSummaryBlockRootProof.length ==
                                    32 *
                                        (BEACON_STATE_FIELD_TREE_HEIGHT +
                                            (HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) +
                                            1 +
                                            (BLOCK_ROOTS_TREE_HEIGHT)),
                                "BeaconChainProofs.verifyWithdrawal: historicalSummaryBlockRootProof has incorrect length"
                            );
                            /**
                             * Note: Here, the "1" in "1 + (BLOCK_ROOTS_TREE_HEIGHT)" signifies that extra step of choosing the "block_root_summary" within the individual
                             * "historical_summary". Everywhere else it signifies merkelize_with_mixin, where the length of an array is hashed with the root of the array,
                             * but not here.
                             */
                            uint256 historicalBlockHeaderIndex = (HISTORICAL_SUMMARIES_INDEX <<
                                ((HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) + 1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                (uint256(withdrawalProof.historicalSummaryIndex) << (1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
                                (BLOCK_SUMMARY_ROOT_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)) |
                                uint256(withdrawalProof.blockRootIndex);
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.historicalSummaryBlockRootProof,
                                    root: beaconStateRoot,
                                    leaf: withdrawalProof.blockRoot,
                                    index: historicalBlockHeaderIndex
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid historicalsummary merkle proof"
                            );
                            //Next we verify the slot against the blockRoot
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.slotProof,
                                    root: withdrawalProof.blockRoot,
                                    leaf: withdrawalProof.slotRoot,
                                    index: SLOT_INDEX
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid slot merkle proof"
                            );
                            {
                                // Next we verify the executionPayloadRoot against the blockRoot
                                uint256 executionPayloadIndex = (BODY_ROOT_INDEX <<
                                    (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)) | EXECUTION_PAYLOAD_INDEX;
                                require(
                                    Merkle.verifyInclusionSha256({
                                        proof: withdrawalProof.executionPayloadProof,
                                        root: withdrawalProof.blockRoot,
                                        leaf: withdrawalProof.executionPayloadRoot,
                                        index: executionPayloadIndex
                                    }),
                                    "BeaconChainProofs.verifyWithdrawal: Invalid executionPayload merkle proof"
                                );
                            }
                            // Next we verify the timestampRoot against the executionPayload root
                            require(
                                Merkle.verifyInclusionSha256({
                                    proof: withdrawalProof.timestampProof,
                                    root: withdrawalProof.executionPayloadRoot,
                                    leaf: withdrawalProof.timestampRoot,
                                    index: TIMESTAMP_INDEX
                                }),
                                "BeaconChainProofs.verifyWithdrawal: Invalid timestamp merkle proof"
                            );
                            {
                                /**
                                 * Next we verify the withdrawal fields against the executionPayloadRoot:
                                 * First we compute the withdrawal_index, then we merkleize the
                                 * withdrawalFields container to calculate the withdrawalRoot.
                                 *
                                 * Note: Merkleization of the withdrawals root tree uses MerkleizeWithMixin, i.e., the length of the array is hashed with the root of
                                 * the array.  Thus we shift the WITHDRAWALS_INDEX over by WITHDRAWALS_TREE_HEIGHT + 1 and not just WITHDRAWALS_TREE_HEIGHT.
                                 */
                                uint256 withdrawalIndex = (WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1)) |
                                    uint256(withdrawalProof.withdrawalIndex);
                                bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
                                require(
                                    Merkle.verifyInclusionSha256({
                                        proof: withdrawalProof.withdrawalProof,
                                        root: withdrawalProof.executionPayloadRoot,
                                        leaf: withdrawalRoot,
                                        index: withdrawalIndex
                                    }),
                                    "BeaconChainProofs.verifyWithdrawal: Invalid withdrawal merkle proof"
                                );
                            }
                        }
                        /**
                         * @notice This function replicates the ssz hashing of a validator's pubkey, outlined below:
                         *  hh := ssz.NewHasher()
                         *  hh.PutBytes(validatorPubkey[:])
                         *  validatorPubkeyHash := hh.Hash()
                         *  hh.Reset()
                         */
                        function hashValidatorBLSPubkey(
                            bytes memory validatorPubkey
                        ) internal pure returns (bytes32 pubkeyHash) {
                            require(validatorPubkey.length == 48, "Input should be 48 bytes in length");
                            return sha256(abi.encodePacked(validatorPubkey, bytes16(0)));
                        }
                        /**
                         * @dev Retrieve the withdrawal timestamp
                         */
                        function getWithdrawalTimestamp(
                            WithdrawalProof memory withdrawalProof
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalProof.timestampRoot);
                        }
                        /**
                         * @dev Converts the withdrawal's slot to an epoch
                         */
                        function getWithdrawalEpoch(
                            WithdrawalProof memory withdrawalProof
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalProof.slotRoot) / SLOTS_PER_EPOCH;
                        }
                        /**
                         * Indices for validator fields (refer to consensus specs):
                         * 0: pubkey
                         * 1: withdrawal credentials
                         * 2: effective balance
                         * 3: slashed?
                         * 4: activation elligibility epoch
                         * 5: activation epoch
                         * 6: exit epoch
                         * 7: withdrawable epoch
                         */
                        /**
                         * @dev Retrieves a validator's pubkey hash
                         */
                        function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) {
                            return validatorFields[VALIDATOR_PUBKEY_INDEX];
                        }
                        function getWithdrawalCredentials(
                            bytes32[] memory validatorFields
                        ) internal pure returns (bytes32) {
                            return validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
                        }
                        /**
                         * @dev Retrieves a validator's effective balance (in gwei)
                         */
                        function getEffectiveBalanceGwei(
                            bytes32[] memory validatorFields
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
                        }
                        /**
                         * @dev Retrieves a validator's withdrawable epoch
                         */
                        function getWithdrawableEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
                        }
                        /**
                         * Indices for withdrawal fields (refer to consensus specs):
                         * 0: withdrawal index
                         * 1: validator index
                         * 2: execution address
                         * 3: withdrawal amount
                         */
                        /**
                         * @dev Retrieves a withdrawal's validator index
                         */
                        function getValidatorIndex(bytes32[] memory withdrawalFields) internal pure returns (uint40) {
                            return
                                uint40(
                                    Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_INDEX_INDEX])
                                );
                        }
                        /**
                         * @dev Retrieves a withdrawal's withdrawal amount (in gwei)
                         */
                        function getWithdrawalAmountGwei(
                            bytes32[] memory withdrawalFields
                        ) internal pure returns (uint64) {
                            return Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    library Endian {
                        /**
                         * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
                         * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
                         * @return n The big endian-formatted uint64
                         * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
                         * through a right-shift/shr operation.
                         */
                        function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) {
                            // the number needs to be stored in little-endian encoding (ie in bytes 0-8)
                            n = uint64(uint256(lenum >> 192));
                            return
                                (n >> 56) |
                                ((0x00FF000000000000 & n) >> 40) |
                                ((0x0000FF0000000000 & n) >> 24) |
                                ((0x000000FF00000000 & n) >> 8) |
                                ((0x00000000FF000000 & n) << 8) |
                                ((0x0000000000FF0000 & n) << 24) |
                                ((0x000000000000FF00 & n) << 40) |
                                ((0x00000000000000FF & n) << 56);
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @dev These functions deal with verification of Merkle Tree proofs.
                     *
                     * The tree and the proofs can be generated using our
                     * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
                     * You will find a quickstart guide in the readme.
                     *
                     * WARNING: You should avoid using leaf values that are 64 bytes long prior to
                     * hashing, or use a hash function other than keccak256 for hashing leaves.
                     * This is because the concatenation of a sorted pair of internal nodes in
                     * the merkle tree could be reinterpreted as a leaf value.
                     * OpenZeppelin's JavaScript library generates merkle trees that are safe
                     * against this attack out of the box.
                     */
                    library 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 (uint256 i = 0; i < numNodesInLayer; i++) {
                                layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
                            }
                            //the next layer above has half as many nodes
                            numNodesInLayer /= 2;
                            //while we haven't computed the root
                            while (numNodesInLayer != 0) {
                                //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
                                for (uint256 i = 0; i < numNodesInLayer; i++) {
                                    layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
                                }
                                //the next layer above has half as many nodes
                                numNodesInLayer /= 2;
                            }
                            //the first node in the layer is the root
                            return layer[0];
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    /// @dev Error for 0x0 address inputs
                    error InvalidZeroInput();
                    /// @dev Error for already added items to a list
                    error AlreadyAdded();
                    /// @dev Error for not found items in a list
                    error NotFound();
                    /// @dev Error for hitting max TVL
                    error MaxTVLReached();
                    /// @dev Error for caller not having permissions
                    error NotRestakeManagerAdmin();
                    /// @dev Error for call not coming from deposit queue contract
                    error NotDepositQueue();
                    /// @dev Error for contract being paused
                    error ContractPaused();
                    /// @dev Error for exceeding max basis points (100%)
                    error OverMaxBasisPoints();
                    /// @dev Error for invalid token decimals for collateral tokens (must be 18)
                    error InvalidTokenDecimals(uint8 expected, uint8 actual);
                    /// @dev Error when withdraw is already completed
                    error WithdrawAlreadyCompleted();
                    /// @dev Error when a different address tries to complete withdraw
                    error NotOriginalWithdrawCaller(address expectedCaller);
                    /// @dev Error when caller does not have OD admin role
                    error NotOperatorDelegatorAdmin();
                    /// @dev Error when caller does not have Oracle Admin role
                    error NotOracleAdmin();
                    /// @dev Error when caller is not RestakeManager contract
                    error NotRestakeManager();
                    /// @dev Errror when caller does not have ETH Restake Admin role
                    error NotNativeEthRestakeAdmin();
                    /// @dev Error when delegation address was already set - cannot be set again
                    error DelegateAddressAlreadySet();
                    /// @dev Error when caller does not have ERC20 Rewards Admin role
                    error NotERC20RewardsAdmin();
                    /// @dev Error when sending ETH fails
                    error TransferFailed();
                    /// @dev Error when caller does not have ETH Minter Burner Admin role
                    error NotEzETHMinterBurner();
                    /// @dev Error when caller does not have Token Admin role
                    error NotTokenAdmin();
                    /// @dev Error when price oracle is not configured
                    error OracleNotFound();
                    /// @dev Error when price oracle data is stale
                    error OraclePriceExpired();
                    /// @dev Error when array lengths do not match
                    error MismatchedArrayLengths();
                    /// @dev Error when caller does not have Deposit Withdraw Pauser role
                    error NotDepositWithdrawPauser();
                    /// @dev Error when an individual token TVL is over the max
                    error MaxTokenTVLReached();
                    /// @dev Error when Oracle price is invalid
                    error InvalidOraclePrice();
                    /// @dev Error when calling an invalid function
                    error NotImplemented();
                    /// @dev Error when calculating token amounts is invalid
                    error InvalidTokenAmount();
                    /// @dev Error when timestamp is invalid - likely in the past
                    error InvalidTimestamp(uint256 timestamp);
                    /// @dev Error when trade does not meet minimum output amount
                    error InsufficientOutputAmount();
                    /// @dev Error when the token received over the bridge is not the one expected
                    error InvalidTokenReceived();
                    /// @dev Error when the origin address is not whitelisted
                    error InvalidOrigin();
                    /// @dev Error when the sender is not expected
                    error InvalidSender(address expectedSender, address actualSender);
                    /// @dev error when function returns 0 amount
                    error InvalidZeroOutput();
                    /// @dev error when xRenzoBridge does not have enough balance to pay for fee
                    error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees);
                    /// @dev error when source chain is not expected
                    error InvalidSourceChain(uint64 expectedCCIPChainSelector, uint64 actualCCIPChainSelector);
                    /// @dev Error when an unauthorized address tries to call the bridge function on the L2
                    error UnauthorizedBridgeSweeper();
                    /// @dev Error when caller does not have BRIDGE_ADMIN role
                    error NotBridgeAdmin();
                    /// @dev Error when caller does not have PRICE_FEED_SENDER role
                    error NotPriceFeedSender();
                    /// @dev Error for connext price Feed unauthorised call
                    error UnAuthorisedCall();
                    /// @dev Error for no price feed configured on L2
                    error PriceFeedNotAvailable();
                    /// @dev Error for invalid bridge fee share configuration
                    error InvalidBridgeFeeShare(uint256 bridgeFee);
                    /// @dev Error for invalid sweep batch size
                    error InvalidSweepBatchSize(uint256 batchSize);
                    /// @dev Error when caller does not have Withdraw Queue admin role
                    error NotWithdrawQueueAdmin();
                    /// @dev Error when caller try to withdraw more than Buffer
                    error NotEnoughWithdrawBuffer();
                    /// @dev Error when caller try to claim withdraw before cooldown period
                    error EarlyClaim();
                    /// @dev Error when caller try to withdraw for unsupported asset
                    error UnsupportedWithdrawAsset();
                    /// @dev Error when caller try to claim invalidWithdrawIndex
                    error InvalidWithdrawIndex();
                    /// @dev Error when TVL was expected to be 0
                    error InvalidTVL();
                    /// @dev Error when incorrect BeaconChainStrategy is set for LST in completeQueuedWithdrawal
                    error IncorrectStrategy();
                    /// @dev Error when adding new OperatorDelegator which is not delegated
                    error OperatoDelegatorNotDelegated();
                    /// @dev Error when emergency tracking already tracked withdrawal
                    error WithdrawalAlreadyTracked();
                    /// @dev Error when emergency tracking already completed withdrawal
                    error WithdrawalAlreadyCompleted();
                    /// @dev Error when caller does not have Emergency Withdraw Tracking Admin role
                    error NotEmergencyWithdrawTrackingAdmin();
                    /// @dev Error when strategy does not have specified underlying
                    error InvalidStrategy();
                    /// @dev Error when strategy already set and hold non zero token balance
                    error NonZeroUnderlyingStrategyExist();
                    /// @dev Error when caller tried to claim queued withdrawal when not filled
                    error QueuedWithdrawalNotFilled();
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "./Delegation/IOperatorDelegator.sol";
                    import "./Deposits/IDepositQueue.sol";
                    interface IRestakeManager {
                        function stakeEthInOperatorDelegator(
                            IOperatorDelegator operatorDelegator,
                            bytes calldata pubkey,
                            bytes calldata signature,
                            bytes32 depositDataRoot
                        ) external payable;
                        function depositTokenRewardsFromProtocol(IERC20 _token, uint256 _amount) external;
                        function depositQueue() external view returns (IDepositQueue);
                        function calculateTVLs() external view returns (uint256[][] memory, uint256[] memory, uint256);
                        function depositETH() external payable;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    interface IRenzoOracle {
                        function lookupTokenValue(IERC20 _token, uint256 _balance) external view returns (uint256);
                        function lookupTokenAmountFromValue(
                            IERC20 _token,
                            uint256 _value
                        ) external view returns (uint256);
                        function lookupTokenValues(
                            IERC20[] memory _tokens,
                            uint256[] memory _balances
                        ) external view returns (uint256);
                        function calculateMintAmount(
                            uint256 _currentValueInProtocol,
                            uint256 _newValueAdded,
                            uint256 _existingEzETHSupply
                        ) external pure returns (uint256);
                        function calculateRedeemAmount(
                            uint256 _ezETHBeingBurned,
                            uint256 _existingEzETHSupply,
                            uint256 _currentValueInProtocol
                        ) external pure returns (uint256);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    interface IRoleManager {
                        /// @dev Determines if the specified address has permissions to manage RoleManager
                        /// @param potentialAddress Address to check
                        function isRoleManagerAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to mint or burn ezETH tokens
                        /// @param potentialAddress Address to check
                        function isEzETHMinterBurner(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the OperatorDelgator Contracts
                        /// @param potentialAddress Address to check
                        function isOperatorDelegatorAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Oracle Contract config
                        /// @param potentialAddress Address to check
                        function isOracleAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Restake Manager
                        /// @param potentialAddress Address to check
                        function isRestakeManagerAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to update config on the Token Contract
                        /// @param potentialAddress Address to check
                        function isTokenAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to trigger restaking of native ETH
                        /// @param potentialAddress Address to check
                        function isNativeEthRestakeAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to sweep and deposit ERC20 Rewards
                        /// @param potentialAddress Address to check
                        function isERC20RewardsAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to pause deposits and withdraws
                        /// @param potentialAddress Address to check
                        function isDepositWithdrawPauser(address potentialAddress) external view returns (bool);
                        /// @dev Determines if the specified address has permission to set whitelisted origin in xRenzoBridge
                        /// @param potentialAddress Address to check
                        function isBridgeAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determined if the specified address has permission to send price feed of ezETH to L2
                        /// @param potentialAddress Address to check
                        function isPriceFeedSender(address potentialAddress) external view returns (bool);
                        /// @dev Determine if the specified address haas permission to update Withdraw Queue params
                        /// @param potentialAddress Address to check
                        function isWithdrawQueueAdmin(address potentialAddress) external view returns (bool);
                        /// @dev Determine if the specified address has permission to track emergency pending queued withdrawals
                        /// @param potentialAddress Address to check
                        function isEmergencyWithdrawTrackingAdmin(
                            address potentialAddress
                        ) external view returns (bool);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.9;
                    import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
                    interface IEzEthToken is IERC20Upgradeable {
                        function mint(address to, uint256 amount) external;
                        function burn(address from, uint256 amount) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    interface IWithdrawQueue {
                        /// @dev To get available value to withdraw from buffer
                        /// @param _asset address of token
                        function getAvailableToWithdraw(address _asset) external view returns (uint256);
                        /// @dev To get the withdraw buffer target of given asset
                        /// @param _asset address of token
                        function withdrawalBufferTarget(address _asset) external view returns (uint256);
                        /// @dev To get the current Target Buffer Deficit
                        /// @param _asset address of token
                        function getWithdrawDeficit(address _asset) external view returns (uint256);
                        /// @dev Fill ERC20 Withdraw Buffer
                        /// @param _asset the token address to fill the respective buffer
                        /// @param _amount  amount of token to fill with
                        function fillERC20WithdrawBuffer(address _asset, uint256 _amount) external;
                        /// @dev to get the withdrawRequests for particular user
                        /// @param _user address of the user
                        function withdrawRequests(address _user) external view returns (uint256[] memory);
                        /// @dev Fill ETH Withdraw buffer
                        function fillEthWithdrawBuffer() external payable;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "./WithdrawQueueStorage.sol";
                    import "../Errors/Errors.sol";
                    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                    import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
                    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    contract WithdrawQueue is
                        Initializable,
                        PausableUpgradeable,
                        ReentrancyGuardUpgradeable,
                        WithdrawQueueStorageV2
                    {
                        using SafeERC20 for IERC20;
                        event WithdrawBufferTargetUpdated(uint256 oldBufferTarget, uint256 newBufferTarget);
                        event CoolDownPeriodUpdated(uint256 oldCoolDownPeriod, uint256 newCoolDownPeriod);
                        event EthBufferFilled(uint256 amount);
                        event ERC20BufferFilled(address asset, uint256 amount);
                        event WithdrawRequestCreated(
                            uint256 indexed withdrawRequestID,
                            address user,
                            address claimToken,
                            uint256 amountToRedeem,
                            uint256 ezETHAmountLocked,
                            uint256 withdrawRequestIndex,
                            bool queued,
                            uint256 queueFilled
                        );
                        event WithdrawRequestClaimed(WithdrawRequest withdrawRequest);
                        event QueueFilled(uint256 amount, address asset);
                        /// @dev Allows only Withdraw Queue Admin to configure the contract
                        modifier onlyWithdrawQueueAdmin() {
                            if (!roleManager.isWithdrawQueueAdmin(msg.sender)) revert NotWithdrawQueueAdmin();
                            _;
                        }
                        /// @dev Allows only a whitelisted address to set pause state
                        modifier onlyDepositWithdrawPauserAdmin() {
                            if (!roleManager.isDepositWithdrawPauser(msg.sender)) revert NotDepositWithdrawPauser();
                            _;
                        }
                        /// @dev Allows only RestakeManager to call the functions
                        modifier onlyRestakeManager() {
                            if (msg.sender != address(restakeManager)) revert NotRestakeManager();
                            _;
                        }
                        modifier onlyDepositQueue() {
                            if (msg.sender != address(restakeManager.depositQueue())) revert NotDepositQueue();
                            _;
                        }
                        /// @dev Prevents implementation contract from being initialized.
                        /// @custom:oz-upgrades-unsafe-allow constructor
                        constructor() {
                            _disableInitializers();
                        }
                        /**
                         * @notice  Initializes the contract with initial vars
                         */
                        function initialize(
                            IRoleManager _roleManager,
                            IRestakeManager _restakeManager,
                            IEzEthToken _ezETH,
                            IRenzoOracle _renzoOracle,
                            uint256 _coolDownPeriod,
                            TokenWithdrawBuffer[] calldata _withdrawalBufferTarget
                        ) external initializer {
                            if (
                                address(_roleManager) == address(0) ||
                                address(_ezETH) == address(0) ||
                                address(_renzoOracle) == address(0) ||
                                address(_restakeManager) == address(0) ||
                                _withdrawalBufferTarget.length == 0 ||
                                _coolDownPeriod == 0
                            ) revert InvalidZeroInput();
                            __Pausable_init();
                            roleManager = _roleManager;
                            restakeManager = _restakeManager;
                            ezETH = _ezETH;
                            renzoOracle = _renzoOracle;
                            coolDownPeriod = _coolDownPeriod;
                            for (uint256 i = 0; i < _withdrawalBufferTarget.length; ) {
                                if (
                                    _withdrawalBufferTarget[i].asset == address(0) ||
                                    _withdrawalBufferTarget[i].bufferAmount == 0
                                ) revert InvalidZeroInput();
                                withdrawalBufferTarget[_withdrawalBufferTarget[i].asset] = _withdrawalBufferTarget[i]
                                    .bufferAmount;
                                unchecked {
                                    ++i;
                                }
                            }
                            // Deploy WithdrawQueue with paused state
                            _pause();
                        }
                        /**
                         * @notice  Updates the WithdrawBufferTarget for max withdraw available
                         * @dev     Permissioned call (onlyWithdrawQueueAdmin)
                         * @param   _newBufferTarget  new max buffer target available to withdraw
                         */
                        function updateWithdrawBufferTarget(
                            TokenWithdrawBuffer[] calldata _newBufferTarget
                        ) external onlyWithdrawQueueAdmin {
                            if (_newBufferTarget.length == 0) revert InvalidZeroInput();
                            for (uint256 i = 0; i < _newBufferTarget.length; ) {
                                if (_newBufferTarget[i].asset == address(0) || _newBufferTarget[i].bufferAmount == 0)
                                    revert InvalidZeroInput();
                                emit WithdrawBufferTargetUpdated(
                                    withdrawalBufferTarget[_newBufferTarget[i].asset],
                                    _newBufferTarget[i].bufferAmount
                                );
                                withdrawalBufferTarget[_newBufferTarget[i].asset] = _newBufferTarget[i].bufferAmount;
                                unchecked {
                                    ++i;
                                }
                            }
                        }
                        /**
                         * @notice Updates the coolDownPeriod for withdrawal requests
                         * @dev    It is a permissioned call (onlyWithdrawQueueAdmin)
                         * @param   _newCoolDownPeriod  new coolDownPeriod in seconds
                         */
                        function updateCoolDownPeriod(uint256 _newCoolDownPeriod) external onlyWithdrawQueueAdmin {
                            if (_newCoolDownPeriod == 0) revert InvalidZeroInput();
                            emit CoolDownPeriodUpdated(coolDownPeriod, _newCoolDownPeriod);
                            coolDownPeriod = _newCoolDownPeriod;
                        }
                        /**
                         * @notice  Pause the contract
                         * @dev     Permissioned call (onlyDepositWithdrawPauserAdmin)
                         */
                        function pause() external onlyDepositWithdrawPauserAdmin {
                            _pause();
                        }
                        /**
                         * @notice  Unpause the contract
                         * @dev     Permissioned call (onlyDepositWithdrawPauserAdmin)
                         */
                        function unpause() external onlyDepositWithdrawPauserAdmin {
                            _unpause();
                        }
                        /**
                         * @notice  returns available to withdraw for particular asset
                         * @param   _asset  address of asset. for ETH _asset = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
                         * @return  uint256  amount available to withdraw from buffer
                         */
                        function getAvailableToWithdraw(address _asset) public view returns (uint256) {
                            if (_asset != IS_NATIVE) {
                                return IERC20(_asset).balanceOf(address(this)) - claimReserve[_asset];
                            } else {
                                return address(this).balance - claimReserve[_asset];
                            }
                        }
                        /**
                         * @notice  returns empty withdraw buffer for particular asset
                         * @dev     returns 0 is buffer is full
                         * @param   _asset  address of asset. for ETH _asset = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
                         * @return  uint256  amount of buffer to be filled
                         */
                        function getWithdrawDeficit(address _asset) public view returns (uint256) {
                            uint256 availableToWithdraw = getAvailableToWithdraw(_asset);
                            uint256 bufferDeficit = withdrawalBufferTarget[_asset] > availableToWithdraw
                                ? withdrawalBufferTarget[_asset] - availableToWithdraw
                                : 0;
                            // Only allow queueDeficit for ETH
                            if (_asset != IS_NATIVE) {
                                return bufferDeficit;
                            } else {
                                uint256 queueDeficit = (ethWithdrawQueue.queuedWithdrawToFill >
                                    ethWithdrawQueue.queuedWithdrawFilled)
                                    ? (ethWithdrawQueue.queuedWithdrawToFill - ethWithdrawQueue.queuedWithdrawFilled)
                                    : 0;
                                return bufferDeficit + queueDeficit;
                            }
                        }
                        /**
                         * @notice  fill Eth WithdrawBuffer from RestakeManager deposits
                         * @dev     permissioned call (onlyRestakeManager)
                         */
                        function fillEthWithdrawBuffer() external payable nonReentrant onlyDepositQueue {
                            uint256 queueFilled = _checkAndFillEthWithdrawQueue(msg.value);
                            emit EthBufferFilled(msg.value - queueFilled);
                        }
                        /**
                         * @notice  Fill ERC20 token withdraw buffer from RestakeManager deposits
                         * @dev     permissioned call (onlyDepositQueue)
                         * @param   _asset  address of ERC20 asset to fill up the buffer
                         * @param   _amount  amount of ERC20 asset to fill up the buffer
                         */
                        function fillERC20WithdrawBuffer(
                            address _asset,
                            uint256 _amount
                        ) external nonReentrant onlyDepositQueue {
                            if (_asset == address(0) || _amount == 0) revert InvalidZeroInput();
                            // check if provided assetOut is supported
                            if (withdrawalBufferTarget[_asset] == 0) revert UnsupportedWithdrawAsset();
                            IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
                            emit ERC20BufferFilled(_asset, _amount);
                        }
                        /**
                         * @notice  Creates a withdraw request for user
                         * @param   _amount  amount of ezETH to withdraw
                         * @param   _assetOut  output token to receive on claim
                         */
                        function withdraw(uint256 _amount, address _assetOut) external nonReentrant whenNotPaused {
                            // check for 0 values
                            if (_amount == 0 || _assetOut == address(0)) revert InvalidZeroInput();
                            // check if provided assetOut is supported
                            if (withdrawalBufferTarget[_assetOut] == 0) revert UnsupportedWithdrawAsset();
                            // transfer ezETH tokens to this address
                            IERC20(address(ezETH)).safeTransferFrom(msg.sender, address(this), _amount);
                            uint256 amountToRedeem = _calculateAmountToRedeem(_amount, _assetOut);
                            // increment the withdrawRequestNonce
                            withdrawRequestNonce++;
                            WithdrawRequest memory withdrawRequest = WithdrawRequest(
                                _assetOut,
                                withdrawRequestNonce,
                                amountToRedeem,
                                _amount,
                                block.timestamp
                            );
                            uint256 availableToWithdraw = getAvailableToWithdraw(_assetOut);
                            bool queued = false;
                            // If amountToRedeem is greater than available to withdraw
                            if (amountToRedeem > availableToWithdraw) {
                                // Revert if assetOut is not ETH
                                if (_assetOut != IS_NATIVE) revert NotEnoughWithdrawBuffer();
                                // increase the claim reserve to partially fill withdrawRequest with max available in buffer
                                claimReserve[_assetOut] += availableToWithdraw;
                                // fill the queue with availableToWithdraw
                                ethWithdrawQueue.queuedWithdrawFilled += availableToWithdraw;
                                // update the queue to fill
                                ethWithdrawQueue.queuedWithdrawToFill += amountToRedeem;
                                // calculate withdrawRequest hash
                                bytes32 withdrawHash = keccak256(abi.encode(withdrawRequest, msg.sender));
                                // mark withdraw as queued and track fillAt with current queue top
                                withdrawQueued[withdrawHash].queued = true;
                                withdrawQueued[withdrawHash].fillAt = ethWithdrawQueue.queuedWithdrawToFill;
                                // mark queued to true
                                queued = true;
                            } else {
                                // add redeem amount to claimReserve of claim asset
                                claimReserve[_assetOut] += amountToRedeem;
                            }
                            // add withdraw request for msg.sender
                            withdrawRequests[msg.sender].push(withdrawRequest);
                            emit WithdrawRequestCreated(
                                withdrawRequestNonce,
                                msg.sender,
                                _assetOut,
                                amountToRedeem,
                                _amount,
                                withdrawRequests[msg.sender].length - 1,
                                queued,
                                availableToWithdraw
                            );
                        }
                        /**
                         * @notice  Returns the number of outstanding withdrawal requests of the specified user
                         * @param   user  address of the user
                         * @return  uint256  number of outstanding withdrawal requests
                         */
                        function getOutstandingWithdrawRequests(address user) public view returns (uint256) {
                            return withdrawRequests[user].length;
                        }
                        /**
                         * @notice  Claim user withdraw request
                         * @dev     revert on claim before cooldown period
                         * @param   withdrawRequestIndex  Index of the Withdraw Request user wants to claim
                         * @param   user address of the user to claim withdrawRequest for
                         */
                        function claim(uint256 withdrawRequestIndex, address user) external nonReentrant whenNotPaused {
                            // check if provided withdrawRequest Index is valid
                            if (withdrawRequestIndex >= withdrawRequests[user].length) revert InvalidWithdrawIndex();
                            WithdrawRequest memory _withdrawRequest = withdrawRequests[user][withdrawRequestIndex];
                            if (block.timestamp - _withdrawRequest.createdAt < coolDownPeriod) revert EarlyClaim();
                            // calculate the amount to redeem
                            uint256 claimAmountToRedeem = _calculateAmountToRedeem(
                                _withdrawRequest.ezETHLocked,
                                _withdrawRequest.collateralToken
                            );
                            // check if collateral asset is ETH and queued
                            if (_withdrawRequest.collateralToken == IS_NATIVE) {
                                bytes32 _withdrawHash = keccak256(abi.encode(_withdrawRequest, user));
                                // Revert if withdrawal is queued and not filled completely
                                if (
                                    withdrawQueued[_withdrawHash].queued &&
                                    withdrawQueued[_withdrawHash].fillAt > ethWithdrawQueue.queuedWithdrawFilled
                                ) revert QueuedWithdrawalNotFilled();
                            }
                            // reduce initial amountToRedeem from claim reserve
                            claimReserve[_withdrawRequest.collateralToken] -= _withdrawRequest.amountToRedeem;
                            // update withdraw request amount to redeem if lower at claim time.
                            if (claimAmountToRedeem < _withdrawRequest.amountToRedeem) {
                                _withdrawRequest.amountToRedeem = claimAmountToRedeem;
                            }
                            // delete the withdraw request
                            withdrawRequests[user][withdrawRequestIndex] = withdrawRequests[user][
                                withdrawRequests[user].length - 1
                            ];
                            withdrawRequests[user].pop();
                            // burn ezETH locked for withdraw request
                            ezETH.burn(address(this), _withdrawRequest.ezETHLocked);
                            // send selected redeem asset to user
                            if (_withdrawRequest.collateralToken == IS_NATIVE) {
                                (bool success, ) = payable(user).call{ value: _withdrawRequest.amountToRedeem }("");
                                if (!success) revert TransferFailed();
                            } else {
                                IERC20(_withdrawRequest.collateralToken).transfer(
                                    user,
                                    _withdrawRequest.amountToRedeem
                                );
                            }
                            // emit the event
                            emit WithdrawRequestClaimed(_withdrawRequest);
                        }
                        function _calculateAmountToRedeem(
                            uint256 _amount,
                            address _assetOut
                        ) internal view returns (uint256 _amountToRedeem) {
                            // calculate totalTVL
                            (, , uint256 totalTVL) = restakeManager.calculateTVLs();
                            // Calculate amount to Redeem in ETH
                            _amountToRedeem = renzoOracle.calculateRedeemAmount(_amount, ezETH.totalSupply(), totalTVL);
                            // update amount in claim asset, if claim asset is not ETH
                            if (_assetOut != IS_NATIVE) {
                                // Get ERC20 asset equivalent amount
                                _amountToRedeem = renzoOracle.lookupTokenAmountFromValue(
                                    IERC20(_assetOut),
                                    _amountToRedeem
                                );
                            }
                        }
                        function _checkAndFillEthWithdrawQueue(uint256 amount) internal returns (uint256) {
                            uint256 queueDeficit = (ethWithdrawQueue.queuedWithdrawToFill >
                                ethWithdrawQueue.queuedWithdrawFilled)
                                ? (ethWithdrawQueue.queuedWithdrawToFill - ethWithdrawQueue.queuedWithdrawFilled)
                                : 0;
                            uint256 queueFilled = 0;
                            if (queueDeficit > 0) {
                                queueFilled = queueDeficit > amount ? amount : queueDeficit;
                                // Increase claimReserve
                                claimReserve[IS_NATIVE] += queueFilled;
                                // Increase the queueFilled
                                ethWithdrawQueue.queuedWithdrawFilled += queueFilled;
                                emit QueueFilled(queueFilled, IS_NATIVE);
                            }
                            return queueFilled;
                        }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.19;
                    import "../Permissions/IRoleManager.sol";
                    import "../Oracle/IRenzoOracle.sol";
                    import "../IRestakeManager.sol";
                    import "../token/IEzEthToken.sol";
                    abstract contract WithdrawQueueStorageV1 {
                        address public constant IS_NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                        struct TokenWithdrawBuffer {
                            address asset;
                            uint256 bufferAmount;
                        }
                        struct WithdrawRequest {
                            address collateralToken;
                            uint256 withdrawRequestID;
                            uint256 amountToRedeem;
                            uint256 ezETHLocked;
                            uint256 createdAt;
                        }
                        /// @dev reference to the RenzoOracle contract
                        IRenzoOracle public renzoOracle;
                        /// @dev reference to the ezETH token contract
                        IEzEthToken public ezETH;
                        /// @dev reference to the RoleManager contract
                        IRoleManager public roleManager;
                        /// @dev reference to the RestakeManager contract
                        IRestakeManager public restakeManager;
                        /// @dev cooldown period for user to claim their withdrawal
                        uint256 public coolDownPeriod;
                        /// @dev nonce for tracking withdraw requests, This only increments (doesn't decrement)
                        uint256 public withdrawRequestNonce;
                        /// @dev mapping of withdrawalBufferTarget, indexed by token address
                        mapping(address => uint256) public withdrawalBufferTarget;
                        /// @dev mapping of claimReserve (already withdraw requested), indexed by token address
                        mapping(address => uint256) public claimReserve;
                        /// @dev mapiing of withdraw requests array, indexed by user address
                        mapping(address => WithdrawRequest[]) public withdrawRequests;
                    }
                    abstract contract WithdrawQueueStorageV2 is WithdrawQueueStorageV1 {
                        /// @dev Struct for the withdrawQueue
                        struct EthWithdrawQueue {
                            uint256 queuedWithdrawToFill;
                            uint256 queuedWithdrawFilled;
                        }
                        /// @dev Struct for WithdrawRequest queue status with expected to be filled
                        struct WithdrawQueueStatus {
                            bool queued;
                            uint256 fillAt;
                        }
                        /// @dev mapping of queued withdrawRequest, indexed by withdrawRequest hash
                        mapping(bytes32 => WithdrawQueueStatus) public withdrawQueued;
                        /// @dev mapping for asset withdrawQueue
                        /// @dev WithdrawQueue is a sliding window
                        EthWithdrawQueue public ethWithdrawQueue;
                    }
                    

                    File 10 of 10: TransparentUpgradeableProxy
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v4.5.0-rc.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 v4.4.1 (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 initializating the storage of the proxy like a Solidity constructor.
                         */
                        constructor(address _logic, bytes memory _data) payable {
                            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                            _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.5.0-rc.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 virtual 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.5.0-rc.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 overriden 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 internall 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 overriden should call `super._beforeFallback()`.
                         */
                        function _beforeFallback() internal virtual {}
                    }
                    // 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 v4.4.1 (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) {
                            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                            _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.5.0-rc.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
                                    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/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) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                         */
                        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                         */
                        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                        /**
                         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                         */
                        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                            assembly {
                                r.slot := slot
                            }
                        }
                    }